/* * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package compiler.ciReplay; import jdk.test.lib.Asserts; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.function.BiConsumer; import java.util.function.BiPredicate; public class ReplayFile { private final Path replayFilePath; private final List replayFile; public ReplayFile(String replayFileName) { try { this.replayFilePath = Paths.get(replayFileName); this.replayFile = Files.readAllLines(replayFilePath); } catch (IOException ioe) { throw new Error("Failed to read/write replay data: " + ioe, ioe); } } public void removeLineStartingWith(String oldLine) { replaceLineStartingWith(oldLine, ""); } public String findLineStartingWith(String toFind) { return replayFile.stream() .filter(line -> line.startsWith(toFind)) .findFirst() .orElse(""); } public void replaceLineStartingWith(String oldLine, String newLine) { boolean foundOldLine = false; List newReplayFile = new ArrayList<>(); for (String line : replayFile) { if (line.startsWith(oldLine)) { foundOldLine = true; if (!newLine.isEmpty()) { // Only add if non-empty. Otherwise, line removal. newReplayFile.add(newLine); } } else { newReplayFile.add(line); } } Asserts.assertTrue(foundOldLine, "Did not find oldLine \"" + oldLine + "\" in " + replayFilePath); try { Files.write(replayFilePath, newReplayFile, StandardOpenOption.TRUNCATE_EXISTING); } catch (IOException ioe) { throw new Error("Failed to read/write replay data: " + ioe, ioe); } } static public class ParsedReplayFile { sealed interface Command permits VersionCommand, JvmtiExportCommand, InstanceKlassCommand, CiInstanceKlassCommand, StaticFieldCommand, CiMethodDataCommand, CiMethodCommand, CompileCommand {} // version public record VersionCommand(int version) implements Command {} // JvmtiExport public record JvmtiExportCommand(String field, int value) implements Command {} // instanceKlass // | @bci * ; // | @cpi * ; sealed interface InstanceKlassCommand extends Command permits InstanceKlassCommandName, InstanceKlassCommandBci, InstanceKlassCommandCpi {} public record InstanceKlassCommandName(String name) implements InstanceKlassCommand {} public record InstanceKlassCommandBci(String klass, String name, String signature, int bci, List location) implements InstanceKlassCommand {} public record InstanceKlassCommandCpi(String klass, int cpi, List location) implements InstanceKlassCommand {} // ciInstanceKlass tag* public record CiInstanceKlassCommand(String name, boolean isLinked, boolean isInitialized, int length, List tag) implements Command {} // staticfield [IBCSZJFD] // | "[" [IBCSZJFD] // | "ref" ("nullable" | "null-free") // | "flat" ("nullable" | "null-free") ("atomic" | "non-atomic") // | "Ljava/lang/String;" // | ? sealed interface StaticFieldCommand extends Command permits StaticFieldCommandPrimitive, StaticFieldCommandPrimitiveArray, StaticFieldCommandRefArray, StaticFieldCommandFlatArray, StaticFieldCommandNullArray, StaticFieldCommandString, StaticFieldCommandInstance {} public record StaticFieldCommandPrimitive(String klass, String fieldName, String signature, String value) implements StaticFieldCommand {} public record StaticFieldCommandPrimitiveArray(String klass, String fieldName, String signature, int length) implements StaticFieldCommand {} public record StaticFieldCommandRefArray(String klass, String fieldName, String signature, int length, boolean nullFree, String actualKlass) implements StaticFieldCommand {} public record StaticFieldCommandFlatArray(String klass, String fieldName, String signature, int length, boolean nullFree, boolean nonAtomic, String actualKlass) implements StaticFieldCommand {} public record StaticFieldCommandNullArray(String klass, String fieldName, String signature) implements StaticFieldCommand {} public record StaticFieldCommandString(String klass, String fieldName, String value) implements StaticFieldCommand {} public record StaticFieldCommandInstance(String klass, String fieldName, String signature, List actualKlassOrValues) implements StaticFieldCommand {} // ciMethodData orig * data * oops ( ?)* methods ( )* sealed interface CiMethodDataCommandOop permits CiMethodDataCommandOopInstance, CiMethodDataCommandOopArray {} public record CiMethodDataCommandOopInstance(int offset, String klass) implements CiMethodDataCommandOop {} public record CiMethodDataCommandOopArray(int offset, String klass, int arrayProperties) implements CiMethodDataCommandOop {} public record CiMethodDataCommandMethod(int offset, String klass, String name, String signature) {} public record CiMethodDataCommand(String klass, String name, String signature, int state, int invocationCounter, List orig, List data, List oops, List methods) implements Command {} // ciMethod public record CiMethodCommand(String klass, String name, String signature, int invocationCounter, int backedgeCounter, int interpreterInvocationCount, int interpreterThrowoutCount, int instructionsSize) implements Command {} // compile inline ( )* public record CompileCommandInline(int depth, int bci, boolean inlineLate, String klass, String name, String signature) {} public record CompileCommand(String klass, String name, String signature, int entryBci, int compLevel, List inlines) implements Command {} ParsedReplayFile(List commands) { this.commands = commands; } List commands; static public ParsedReplayFile parse(File file) throws IOException { return parse(Files.readAllLines(file.toPath())); } static public ParsedReplayFile parse(List lines) { return new ParsedReplayFile(lines.stream().map(ParsedReplayFile::parseLine).filter(Objects::nonNull).toList()); } static Command parseLine(String line) { List pieces = Arrays.stream(line.split(" ")).filter(piece -> !piece.isEmpty()).toList(); int commentIdx = pieces.indexOf("#"); if (commentIdx >= 0) { pieces = pieces.subList(0, commentIdx); } if (pieces.isEmpty()) { return null; } String command = pieces.getFirst(); var linePieces = LinePieces.make(pieces, command); var cmd = switch (command) { case "version" -> parseVersion(linePieces); case "JvmtiExport" -> parseJvmtiExport(linePieces); case "instanceKlass" -> parseInstanceKlass(linePieces); case "ciInstanceKlass" -> parseCiInstanceKlass(linePieces); case "staticfield" -> parseStaticField(linePieces); case "ciMethodData" -> parseCiMethodData(linePieces); case "ciMethod" -> parseCiMethod(linePieces); case "compile" -> parseCompile(linePieces); default -> throw new RuntimeException("unknown command: " + command); }; linePieces.checkAtEnd(); return cmd; } static class LinePieces { int pos = 0; List pieces; private LinePieces(List pieces) { this.pieces = List.copyOf(pieces); } @Override public String toString() { var before = pieces.subList(0, pos); var after = pieces.subList(pos, pieces.size()); return before + ">>" + after; } static public LinePieces make(List pieces, String commandName) { var line = new LinePieces(pieces); line.getKeywork(commandName); return line; } void checkBounds(int nb) { if (pos < 0) throw new IndexOutOfBoundsException("negative position: " + pos); if (pos + nb - 1 >= pieces.size()) throw new IndexOutOfBoundsException("size: " + pieces.size() + "; pos: " + pos + "; nb: " + nb); } void getKeywork(String keyword) { checkBounds(1); String s = getString(); if (!keyword.equals(s)) { throw new RuntimeException("expected keyword: " + keyword + "; got: " + s); } } public String getString() { checkBounds(1); String s = pieces.get(pos); pos++; return s; } public List getStrings(int n) { checkBounds(n); List sub = pieces.subList(pos, pos + n); pos += n; return sub; } public List getLeftoverStrings() { return getStrings(pieces.size() - pos); } public int getInt() { String s = getString(); return Integer.parseInt(s); } public List getInts(int n) { List s = getStrings(n); return s.stream().map(Integer::parseInt).toList(); } public Optional getIntIfTwoIntsAvailable() { if (pos + 1 >= pieces.size()) { return Optional.empty(); } String s0 = pieces.get(pos); String s1 = pieces.get(pos + 1); try { Integer.parseInt(s0); Integer.parseInt(s1); } catch (NumberFormatException _) { return Optional.empty(); } return Optional.of(getInt()); } public boolean getBool() { int s = getInt(); return switch (s) { case 0 -> false; case 1 -> true; default -> throw new RuntimeException("unexpected bool: " + s); }; } public boolean getBoolKeyword(String falseKw, String trueKw) { String s = getString(); if (s.equals(falseKw)) return false; if (s.equals(trueKw)) return true; throw new RuntimeException("unexepcted boolean keyword; got " + s + "; expected " + falseKw + " (for false) or " + trueKw + " (for true)"); } public boolean atEnd() { return pos == pieces.size(); } public void checkAtEnd() { if (!atEnd()) { throw new RuntimeException("not at end; size: " + pieces.size() + "; pos: " + pos + "; pieces: " + this); } } } static VersionCommand parseVersion(LinePieces pieces) { int version = pieces.getInt(); return new VersionCommand(version); } static JvmtiExportCommand parseJvmtiExport(LinePieces pieces) { String field = pieces.getString(); int value = pieces.getInt(); return new JvmtiExportCommand(field, value); } static InstanceKlassCommand parseInstanceKlass(LinePieces pieces) { String name = pieces.getString(); return switch (name) { case "@bci" -> parseInstanceKlassBci(pieces); case "@cpi" -> parseInstanceKlassCpi(pieces); default -> new InstanceKlassCommandName(name); }; } static InstanceKlassCommandBci parseInstanceKlassBci(LinePieces pieces) { String klass = pieces.getString(); String name = pieces.getString(); String signature = pieces.getString(); int bci = pieces.getInt(); List location = new ArrayList<>(); var nextS = pieces.getString(); while (!nextS.equals(";")) { location.add(nextS); nextS = pieces.getString(); } return new InstanceKlassCommandBci(klass, name, signature, bci, location); } static InstanceKlassCommandCpi parseInstanceKlassCpi(LinePieces pieces) { String klass = pieces.getString(); int cpi = pieces.getInt(); List location = pieces.getLeftoverStrings(); return new InstanceKlassCommandCpi(klass, cpi, location); } static CiInstanceKlassCommand parseCiInstanceKlass(LinePieces pieces) { String name = pieces.getString(); boolean isLinked = pieces.getBool(); boolean isInitialized = pieces.getBool(); int length = pieces.getInt(); List tag = pieces.getInts(length - 1); return new CiInstanceKlassCommand(name, isLinked, isInitialized, length, tag); } static boolean isPrimitiveType(char c) { return "IBCSZJFD".contains(String.valueOf(c)); } static StaticFieldCommand parseStaticField(LinePieces pieces) { String klass = pieces.getString(); String fieldName = pieces.getString(); String signature = pieces.getString(); if (isPrimitiveType(signature.charAt(0))) { String val = pieces.getString(); return new StaticFieldCommandPrimitive(klass, fieldName, signature, val); } if (signature.charAt(0) == '[') { if (isPrimitiveType(signature.charAt(1))) { int length = pieces.getInt(); return new StaticFieldCommandPrimitiveArray(klass, fieldName, signature, length); } else { int length = pieces.getInt(); if (length == -1) { return new StaticFieldCommandNullArray(klass, fieldName, signature); } boolean isFlat = pieces.getBoolKeyword("ref", "flat"); boolean nullFree = pieces.getBoolKeyword("nullable", "null-free"); if (isFlat) { boolean nonAtomic = pieces.getBoolKeyword("atomic", "non-atomic"); String actualKlass = pieces.getString(); return new StaticFieldCommandFlatArray(klass, fieldName, signature, length, nullFree, nonAtomic, actualKlass); } else { String actualKlass = pieces.getString(); return new StaticFieldCommandRefArray(klass, fieldName, signature, length, nullFree, actualKlass); } } } if (signature.equals("Ljava/lang/String;")) { String value = pieces.getString(); return new StaticFieldCommandString(klass, fieldName, value); } List actualKlassOrValues = pieces.getLeftoverStrings(); return new StaticFieldCommandInstance(klass, fieldName, signature, actualKlassOrValues); } // oops ( ?)* methods ( )* static CiMethodDataCommand parseCiMethodData(LinePieces pieces) { String klass = pieces.getString(); String name = pieces.getString(); String signature = pieces.getString(); int state = pieces.getInt(); int invocationCounter = pieces.getInt(); pieces.getKeywork("orig"); int origLength = pieces.getInt(); List orig = pieces.getInts(origLength); pieces.getKeywork("data"); int datalength = pieces.getInt(); List data = pieces.getStrings(datalength); pieces.getKeywork("oops"); int oopsLength = pieces.getInt(); List oops = new ArrayList<>(oopsLength); for (int i = 0; i < oopsLength; i++) { int offset = pieces.getInt(); String klass_ = pieces.getString(); Optional properties = pieces.getIntIfTwoIntsAvailable(); oops.add( properties .map(prop -> (CiMethodDataCommandOop)new CiMethodDataCommandOopArray(offset, klass_, prop)) .orElse(new CiMethodDataCommandOopInstance(offset, klass_)) ); } pieces.getKeywork("methods"); int methodsLength = pieces.getInt(); List methods = new ArrayList<>(methodsLength); for (int i = 0; i < methodsLength; i++) { int offset = pieces.getInt(); String klass_ = pieces.getString(); String name_ = pieces.getString(); String signature_ = pieces.getString(); methods.add(new CiMethodDataCommandMethod(offset, klass_, name_, signature_)); } return new CiMethodDataCommand(klass, name, signature, state, invocationCounter, orig, data, oops, methods); } static CiMethodCommand parseCiMethod(LinePieces pieces) { String klass = pieces.getString(); String name = pieces.getString(); String signature = pieces.getString(); int invocationCounter = pieces.getInt(); int backedgeCounter = pieces.getInt(); int interpreterInvocationCount = pieces.getInt(); int interpreterThrowoutCount = pieces.getInt(); int instructionsSize = pieces.getInt(); return new CiMethodCommand(klass, name, signature, invocationCounter, backedgeCounter, interpreterInvocationCount, interpreterThrowoutCount, instructionsSize); } static CompileCommand parseCompile(LinePieces pieces) { String klass = pieces.getString(); String name = pieces.getString(); String signature = pieces.getString(); int entryBci = pieces.getInt(); int compLevel = pieces.getInt(); pieces.getKeywork("inline"); int count = pieces.getInt(); List inlines = new ArrayList<>(); for (int i = 0; i < count; i++) { int depth = pieces.getInt(); int bci = pieces.getInt(); boolean inlineLate = pieces.getBool(); String klass_ = pieces.getString(); String name_ = pieces.getString(); String signature_ = pieces.getString(); inlines.add(new CompileCommandInline(depth, bci, inlineLate, klass_, name_, signature_)); } return new CompileCommand(klass, name, signature, entryBci, compLevel, inlines); } static String klassOfStaticField(StaticFieldCommand cmd) { return switch (cmd) { case StaticFieldCommandFlatArray(String klass, String fieldName, String signature, int length, boolean nullFree, boolean nonAtomic, String actualKlass) -> klass; case StaticFieldCommandInstance(String klass, String fieldName, String signature, List actualKlassOrValues) -> klass; case StaticFieldCommandNullArray(String klass, String fieldName, String signature) -> klass; case StaticFieldCommandPrimitive(String klass, String fieldName, String signature, String value) -> klass; case StaticFieldCommandPrimitiveArray(String klass, String fieldName, String signature, int length) -> klass; case StaticFieldCommandRefArray(String klass, String fieldName, String signature, int length,boolean nullFree, String actualKlass) -> klass; case StaticFieldCommandString(String klass, String fieldName, String value) -> klass; }; } static List checkSanity(ParsedReplayFile parsed) { record Method(String klass, String name, String signature) {} List insanities = new ArrayList<>(); int seenVersionCommands = 0; Set seenCiMethod = new HashSet<>(); Set seenCiMethodData = new HashSet<>(); Set seenCompile = new HashSet<>(); Set seenKlasses = new HashSet<>(); for (Command c : parsed.commands) { switch (c) { case CiInstanceKlassCommand(String name, boolean isLinked, boolean isInitialized, int length, List tag) -> seenKlasses.add(name); case StaticFieldCommand cmd -> { if (!seenKlasses.contains(klassOfStaticField(cmd))) { insanities.add("Static field command " + cmd + " seen before the corresponding ciInstanceKlass command."); } } case CompileCommand(String klass, String name, String signature, int entryBci, int compLevel, List inlines) -> { var method = new Method(klass, name, signature); seenCompile.add(method); if (!seenCiMethod.contains(method)) { insanities.add("Found \"compile\" command without a \"ciMethod\" command for the same method."); } if (!seenCiMethodData.contains(method)) { insanities.add("Found \"compile\" command without a \"ciMethodData\" command for the same method."); } } case CiMethodCommand(String klass, String name, String signature, int invocationCounter, int backedgeCounter, int interpreterInvocationCount, int interpreterThrowoutCount, int instructionsSize) -> seenCiMethod.add(new Method(klass, name, signature)); case CiMethodDataCommand(String klass, String name, String signature, int state, int invocationCounter, List orig, List data, List oops, List methods) -> seenCiMethodData.add(new Method(klass, name, signature)); case VersionCommand _ -> seenVersionCommands++; case InstanceKlassCommand _, JvmtiExportCommand _ -> { } } } if (seenCompile.isEmpty()) { insanities.add("No \"compile\" command found."); } if (seenVersionCommands == 0) { insanities.add("No \"version\" command found."); } else if (seenVersionCommands > 1) { insanities.add("Found too many \"version\" commands: " + seenVersionCommands); } return insanities; } static Optional getVersion(ParsedReplayFile parsed) { return parsed.commands.stream().map(cmd -> switch (cmd) { case VersionCommand(int version) -> version; default -> null; }).filter(Objects::nonNull).findAny(); } static void compareVersion(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { Optional lhsVersion = getVersion(lhs); Optional rhsVersion = getVersion(rhs); if (lhsVersion.isPresent() && rhsVersion.isPresent() && !lhsVersion.get().equals(rhsVersion.get())) { differences.add("Versions mismatch: lhs=" + lhsVersion.get() + "; rhs=" + rhsVersion.get()); } } static HashSet extractSet(ParsedReplayFile parsed, BiConsumer, Command> f) { return parsed.commands.stream().collect( HashSet::new, f, HashSet::addAll ); } static void diffSets(String name, HashSet lhs, HashSet rhs, List differences) { lhs.forEach((v) -> { if (!rhs.contains(v)) { differences.add(name + " mismatch: element=" + v + " exists only in lhs"); } } ); rhs.forEach((v) -> { if (!lhs.contains(v)) { differences.add(name + " mismatch: element=" + v + " exists only in rhs"); } } ); } static HashMap extractMap(ParsedReplayFile parsed, BiConsumer, Command> f) { return parsed.commands.stream().collect( HashMap::new, f, HashMap::putAll ); } static void diffMaps(String name, HashMap lhs, HashMap rhs, BiPredicate eqValue, List differences) { lhs.forEach((key, lValue) -> { if (!rhs.containsKey(key)) { differences.add(name + " mismatch: key=" + key + " exists only in lhs"); } else { U rValue = rhs.get(key); if (!eqValue.test(lValue, rValue)) { differences.add(name + " mismatch: for key=" + key + "; value in lhs=" + lValue + "; value in rhs=" + rValue); } } } ); rhs.forEach((key, _) -> { if (!lhs.containsKey(key)) { differences.add(name + " mismatch: key=" + key + " exists only in rhs"); } } ); } static void compareJvmtiExport(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { BiConsumer, Command> folder = (acc, command) -> { if (command instanceof JvmtiExportCommand(String field, int value)) { acc.put(field, value); } }; HashMap lhsJvmti = extractMap(lhs, folder); HashMap rhsJvmti = extractMap(rhs, folder); diffMaps("JvmtiExport", lhsJvmti, rhsJvmti, Integer::equals, differences); } static void compareInstanceKlassNames(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { BiConsumer, Command> folder = (acc, command) -> { if (command instanceof InstanceKlassCommandName(String name)) { acc.add(name); } }; HashSet lhsKlasses = extractSet(lhs, folder); HashSet rhsKlasses = extractSet(rhs, folder); diffSets("InstanceKlass", lhsKlasses, rhsKlasses, differences); } static void compareInstanceKlassCpi(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Key(String klass, int cpi) {} BiConsumer>, Command> folder = (acc, command) -> { if (command instanceof InstanceKlassCommandCpi(String klass, int cpi, List location)) { acc.put(new Key(klass, cpi), location); } }; var lhsKlasses = extractMap(lhs, folder); var rhsKlasses = extractMap(rhs, folder); diffMaps("InstanceKlass", lhsKlasses, rhsKlasses, List::equals, differences); } static void compareInstanceKlassBci(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Key(String klass, String name, String signature, int bci) {} BiConsumer>, Command> folder = (acc, command) -> { if (command instanceof InstanceKlassCommandBci(String klass, String name, String signature, int bci, List location)) { acc.put(new Key(klass, name, signature, bci), location); } }; var lhsKlasses = extractMap(lhs, folder); var rhsKlasses = extractMap(rhs, folder); diffMaps("InstanceKlass", lhsKlasses, rhsKlasses, List::equals, differences); } static void compareInstanceKlasses(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { compareInstanceKlassNames(lhs, rhs, differences); compareInstanceKlassCpi(lhs, rhs, differences); compareInstanceKlassBci(lhs, rhs, differences); } static void compareCiInstanceKlasses(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Element(String name, boolean isLinked, boolean isInitialized, int length) {} BiConsumer, Command> folder = (acc, command) -> { if (command instanceof CiInstanceKlassCommand(String name, boolean isLinked, boolean isInitialized, int length, List _)) { acc.add(new Element(name, isLinked, isInitialized, length)); } }; var lhsCiLlasses = extractSet(lhs, folder); var rhsCiLlasses = extractSet(rhs, folder); diffSets("CiInstanceKlass", lhsCiLlasses, rhsCiLlasses, differences); } static void compareStaticFieldCommandPrimitive(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Key(String klass, String fieldName, String signature) {} BiConsumer, Command> folder = (acc, command) -> { if (command instanceof StaticFieldCommandPrimitive(String klass, String fieldName, String signature, String value)) { acc.put(new Key(klass, fieldName, signature), value); } }; var lhsStaticFields = extractMap(lhs, folder); var rhsStaticFields = extractMap(rhs, folder); diffMaps("CiInstanceKlass", lhsStaticFields, rhsStaticFields, String::equals, differences); } static void compareStaticFieldCommandPrimitiveArray(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Key(String klass, String fieldName, String signature) {} BiConsumer, Command> folder = (acc, command) -> { if (command instanceof StaticFieldCommandPrimitiveArray(String klass, String fieldName, String signature, int length)) { acc.put(new Key(klass, fieldName, signature), length); } }; var lhsStaticFields = extractMap(lhs, folder); var rhsStaticFields = extractMap(rhs, folder); diffMaps("CiInstanceKlass", lhsStaticFields, rhsStaticFields, Integer::equals, differences); } static void compareStaticFieldCommandRefArray(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Key(String klass, String fieldName, String signature) {} record Value(int length, boolean nullFree, String actualKlass) {} BiConsumer, Command> folder = (acc, command) -> { if (command instanceof StaticFieldCommandRefArray(String klass, String fieldName, String signature, int length, boolean nullFree, String actualKlass)) { acc.put(new Key(klass, fieldName, signature), new Value(length, nullFree, actualKlass)); } }; var lhsStaticFields = extractMap(lhs, folder); var rhsStaticFields = extractMap(rhs, folder); diffMaps("CiInstanceKlass", lhsStaticFields, rhsStaticFields, Value::equals, differences); } static void compareStaticFieldCommandFlatArray(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Key(String klass, String fieldName, String signature) {} record Value(int length, boolean nullFree, boolean nonAtomic, String actualKlass) {} BiConsumer, Command> folder = (acc, command) -> { if (command instanceof StaticFieldCommandFlatArray(String klass, String fieldName, String signature, int length, boolean nullFree, boolean nonAtomic, String actualKlass)) { acc.put(new Key(klass, fieldName, signature), new Value(length, nullFree, nonAtomic, actualKlass)); } }; var lhsStaticFields = extractMap(lhs, folder); var rhsStaticFields = extractMap(rhs, folder); diffMaps("CiInstanceKlass", lhsStaticFields, rhsStaticFields, Value::equals, differences); } static void compareStaticFieldCommandNullArray(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Element(String klass, String fieldName, String signature) {} BiConsumer, Command> folder = (acc, command) -> { if (command instanceof StaticFieldCommandNullArray(String klass, String fieldName, String signature)) { acc.add(new Element(klass, fieldName, signature)); } }; var lhsStaticFields = extractSet(lhs, folder); var rhsStaticFields = extractSet(rhs, folder); diffSets("CiInstanceKlass", lhsStaticFields, rhsStaticFields, differences); } static void compareStaticFieldCommandString(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Key(String klass, String fieldName) {} BiConsumer, Command> folder = (acc, command) -> { if (command instanceof StaticFieldCommandString(String klass, String fieldName, String value)) { acc.put(new Key(klass, fieldName), value); } }; var lhsStaticFields = extractMap(lhs, folder); var rhsStaticFields = extractMap(rhs, folder); diffMaps("CiInstanceKlass", lhsStaticFields, rhsStaticFields, String::equals, differences); } static void compareStaticFieldCommandInstance(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Key(String klass, String fieldName, String signature) {} BiConsumer>, Command> folder = (acc, command) -> { if (command instanceof StaticFieldCommandInstance(String klass, String fieldName, String signature, List actualKlassOrValues)) { acc.put(new Key(klass, fieldName, signature), actualKlassOrValues); } }; var lhsStaticFields = extractMap(lhs, folder); var rhsStaticFields = extractMap(rhs, folder); diffMaps("CiInstanceKlass", lhsStaticFields, rhsStaticFields, List::equals, differences); } static void compareStaticFieldCommand(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { compareStaticFieldCommandPrimitive(lhs, rhs, differences); compareStaticFieldCommandPrimitiveArray(lhs, rhs, differences); compareStaticFieldCommandRefArray(lhs, rhs, differences); compareStaticFieldCommandFlatArray(lhs, rhs, differences); compareStaticFieldCommandNullArray(lhs, rhs, differences); compareStaticFieldCommandString(lhs, rhs, differences); compareStaticFieldCommandInstance(lhs, rhs, differences); } static void compareCiMethodDataCommand(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Key(String klass, String name, String signature) {} record Value(int state, int invocationCounter) {} BiConsumer, Command> folder = (acc, command) -> { if (command instanceof CiMethodDataCommand(String klass, String name, String signature, int state, int invocationCounter, List _, List _, List _, List _)) { acc.put(new Key(klass, name, signature), new Value(state, invocationCounter)); } }; var lhsStaticFields = extractMap(lhs, folder); var rhsStaticFields = extractMap(rhs, folder); diffMaps("CiInstanceKlass", lhsStaticFields, rhsStaticFields, Value::equals, differences); } static void compareCiMethodCommand(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Key(String klass, String name, String signature) {} record Value(int invocationCounter, int backedgeCounter, int interpreterInvocationCount, int interpreterThrowoutCount, int instructionsSize) {} BiConsumer, Command> folder = (acc, command) -> { if (command instanceof CiMethodCommand(String klass, String name, String signature, int invocationCounter, int backedgeCounter, int interpreterInvocationCount, int interpreterThrowoutCount, int instructionsSize)) { acc.put(new Key(klass, name, signature), new Value(invocationCounter, backedgeCounter, interpreterInvocationCount, interpreterThrowoutCount, instructionsSize)); } }; var lhsStaticFields = extractMap(lhs, folder); var rhsStaticFields = extractMap(rhs, folder); diffMaps("CiInstanceKlass", lhsStaticFields, rhsStaticFields, Value::equals, differences); } static void compareCompileCommand(ParsedReplayFile lhs, ParsedReplayFile rhs, List differences) { record Key(String klass, String name, String signature) {} record Value(int entryBci, int compLevel, List inlines) {} BiConsumer, Command> folder = (acc, command) -> { if (command instanceof CompileCommand(String klass, String name, String signature, int entryBci, int compLevel, List inlines)) { acc.put(new Key(klass, name, signature), new Value(entryBci, compLevel, inlines)); } }; var lhsStaticFields = extractMap(lhs, folder); var rhsStaticFields = extractMap(rhs, folder); diffMaps("CiInstanceKlass", lhsStaticFields, rhsStaticFields, Value::equals, differences); } static List findDifferences(ParsedReplayFile lhs, ParsedReplayFile rhs) { List differences = new ArrayList<>(); compareVersion(lhs, rhs, differences); compareJvmtiExport(lhs, rhs, differences); compareInstanceKlasses(lhs, rhs, differences); compareCiInstanceKlasses(lhs, rhs, differences); compareStaticFieldCommand(lhs, rhs, differences); compareCiMethodDataCommand(lhs, rhs, differences); compareCiMethodCommand(lhs, rhs, differences); compareCompileCommand(lhs, rhs, differences); return differences; } } }