8388709: [lworld] replay parts of JDK-8350865

Reviewed-by: dlong, thartmann
This commit is contained in:
Marc Chevalier 2026-08-03 07:09:31 +00:00
parent 17e1f978d8
commit 81c9ea0682
23 changed files with 1081 additions and 53 deletions

View File

@ -187,9 +187,9 @@ ciEnv::ciEnv(CompileTask* task)
// {
// RecordLocation fp(this, "field1");
// // location: "field1"
// { RecordLocation fp(this, " field2"); // location: "field1 field2" }
// { RecordLocation fp(this, "field2"); // location: "field1 field2" }
// // location: "field1"
// { RecordLocation fp(this, " field3"); // location: "field1 field3" }
// { RecordLocation fp(this, "field3"); // location: "field1 field3" }
// // location: "field1"
// }
// // location: ""
@ -225,10 +225,13 @@ public:
// append a new component
ATTRIBUTE_PRINTF(3, 4)
RecordLocation(ciEnv* ci, const char* fmt, ...) {
end = ci->_dyno_name + strlen(ci->_dyno_name);
size_t len = strlen(ci->_dyno_name);
end = ci->_dyno_name + len;
va_list args;
va_start(args, fmt);
push(ci, " ");
if (len > 0) {
push(ci, " ");
}
push_va(ci, fmt, args);
va_end(args);
}

View File

@ -838,8 +838,8 @@ public:
StaticFieldPrinter(out), _obj(obj) {
}
void do_field(fieldDescriptor* fd) {
do_field_helper(fd, _obj, true);
_out->print(" ");
do_field_helper(fd, _obj, true);
}
};
@ -865,27 +865,48 @@ void StaticFieldPrinter::do_field_helper(fieldDescriptor* fd, oop mirror, bool i
case T_ARRAY: // fall-through
case T_OBJECT:
if (!fd->is_null_free_inline_type()) {
_out->print("%s ", fd->signature()->as_quoted_ascii());
_out->print("%s", fd->signature()->as_quoted_ascii());
oop value = mirror->obj_field_acquire(fd->offset());
if (value == nullptr) {
if (field_type == T_ARRAY) {
_out->print("%d", -1);
_out->print(" %d", -1);
}
_out->cr();
} else if (value->is_instance()) {
assert(field_type == T_OBJECT, "");
if (value->is_a(vmClasses::String_klass())) {
const char* ascii_value = java_lang_String::as_quoted_ascii(value);
_out->print("\"%s\"", (ascii_value != nullptr) ? ascii_value : "");
_out->print(" \"%s\"", (ascii_value != nullptr) ? ascii_value : "");
} else {
const char* klass_name = value->klass()->name()->as_quoted_ascii();
_out->print("%s", klass_name);
_out->print(" %s", klass_name);
}
} else if (value->is_array()) {
arrayOop a = (arrayOop)value;
_out->print("%d", a->length());
_out->print(" %d", a->length());
if (value->is_objArray()) {
objArrayOop oa = (objArrayOop)value;
if (value->is_flatArray()) {
FlatArrayKlass* klass = ((flatArrayOop)oa)->klass();
LayoutKind lk = klass->layout_kind();
_out->print(" flat");
if (LayoutKindHelper::is_nullable_flat(lk)) {
_out->print(" nullable");
} else {
_out->print(" null-free");
}
if (LayoutKindHelper::is_atomic_flat(lk)) {
_out->print(" atomic");
} else {
_out->print(" non-atomic");
}
} else {
_out->print(" ref");
if (oa->klass()->is_null_free_array_klass()) {
_out->print(" null-free");
} else {
_out->print(" nullable");
}
}
const char* klass_name = value->klass()->name()->as_quoted_ascii();
_out->print(" %s", klass_name);
}
@ -895,6 +916,7 @@ void StaticFieldPrinter::do_field_helper(fieldDescriptor* fd, oop mirror, bool i
break;
} else {
// handling of null free inline type
_out->print("%s", fd->signature()->as_quoted_ascii());
ResetNoHandleMark rnhm;
Thread* THREAD = Thread::current();
SignatureStream ss(fd->signature(), false);

View File

@ -514,15 +514,14 @@ class CompileReplay : public StackObj {
return k;
}
obj = ciReplay::obj_field(obj, field);
// TODO 8350865 I think we need to handle null-free/flat arrays here
if (obj != nullptr && obj->is_refArray()) {
refArrayOop arr = oop_cast<refArrayOop>(obj);
if (obj != nullptr && obj->is_objArray()) {
objArrayOop arr = oop_cast<objArrayOop>(obj);
int index = parse_int("index");
if (index >= arr->length()) {
report_error("bad array index");
return nullptr;
}
obj = arr->obj_at(index);
obj = arr->obj_at(index, THREAD);
}
} while (obj != nullptr);
if (obj == nullptr) {
@ -825,7 +824,7 @@ class CompileReplay : public StackObj {
rec->_instructions_size = parse_int("instructions_size");
}
// ciMethodData <klass> <name> <signature> <state> <invocation_counter> orig <length> <byte>* data <length> <ptr>* oops <length> (<offset> <klass>)* methods <length> (<offset> <klass> <name> <signature>)*
// ciMethodData <klass> <name> <signature> <state> <invocation_counter> orig <length> <byte>* data <length> <ptr>* oops <length> (<offset> <klass> <array properties>?)* methods <length> (<offset> <klass> <name> <signature>)*
void process_ciMethodData(TRAPS) {
Method* method = parse_method(CHECK);
if (had_error()) return;
@ -1139,12 +1138,26 @@ class CompileReplay : public StackObj {
value = oopFactory::new_longArray(length, CHECK_(true));
} else if (field_signature[0] == JVM_SIGNATURE_ARRAY &&
field_signature[1] == JVM_SIGNATURE_CLASS) {
Klass* actual_array_klass = parse_klass(CHECK_(true));
// TODO 8350865 I think we need to handle null-free/flat arrays here
// This handling will change the array property argument passed to the
// factory below
Klass* kelem = ObjArrayKlass::cast(actual_array_klass)->element_klass();
value = oopFactory::new_objArray(kelem, length, CHECK_(true));
const char* flatness = parse_string();
if (strcmp(flatness, "ref") == 0) {
const char* nullability = parse_string();
bool null_restricted = (strcmp(nullability, "null-free") == 0);
Klass* actual_array_klass = parse_klass(CHECK_(true));
Klass* kelem = ObjArrayKlass::cast(actual_array_klass)->element_klass();
ArrayProperties props = ArrayProperties::Default().with_non_atomic(false).with_null_restricted(null_restricted);
value = oopFactory::new_refArray(kelem, length, props, CHECK_(true));
} else if (strcmp(flatness, "flat") == 0) {
const char* nullability = parse_string();
const char* atomicity = parse_string();
bool null_restricted = (strcmp(nullability, "null-free") == 0);
bool non_atomic = (strcmp(atomicity, "non-atomic") == 0);
Klass* actual_array_klass = parse_klass(CHECK_(true));
Klass* kelem = ObjArrayKlass::cast(actual_array_klass)->element_klass();
ArrayProperties props = ArrayProperties::Default().with_non_atomic(non_atomic).with_null_restricted(null_restricted);
value = oopFactory::new_flatArray(InlineKlass::cast(kelem), length, props, CHECK_(true));
} else {
report_error("unrecognized array kind");
}
} else {
report_error("unhandled array staticfield");
}
@ -1190,7 +1203,7 @@ class CompileReplay : public StackObj {
fieldDescriptor fd;
Symbol* name = SymbolTable::new_symbol(field_name);
Symbol* sig = SymbolTable::new_symbol(field_signature);
if (!k->find_local_field(name, sig, &fd) ||
if (!k->find_local_field(name, sig, &fd, _version >= 3) ||
!fd.is_static() ||
fd.has_initial_value()) {
report_error(field_name);

View File

@ -134,7 +134,7 @@ class ciReplay {
// 1: first instanceKlass sets protection domain (8275868)
// replace current_mileage with invocation_count (8276095)
// 2: incremental inlining support (8254108)
// 3: value class array support (8375548)
// 3: value class array support (8375548 & 8388709)
#define REPLAY_VERSION 3 // current version, bump up for incompatible changes
#endif // SHARE_CI_CIREPLAY_HPP

View File

@ -117,7 +117,7 @@ objArrayOop oopFactory::new_objArray(Klass* klass, int length, ArrayProperties p
}
objArrayOop oopFactory::new_objArray(Klass* klass, int length, TRAPS) {
return new_objArray(klass, length, ArrayProperties::Default(), THREAD);
return new_objArray(klass, length, ArrayProperties::Default(), THREAD);
}
refArrayOop oopFactory::new_refArray(Klass* klass, int length, ArrayProperties properties, TRAPS) {

View File

@ -1583,7 +1583,7 @@ void InstanceKlass::initialize_impl(TRAPS) {
call_class_initializer(THREAD);
}
if (has_strict_static_fields() && !HAS_PENDING_EXCEPTION) {
if (has_strict_static_fields() && !HAS_PENDING_EXCEPTION && !ReplayCompiles) {
// Step 9 also verifies that strict static fields have been initialized.
// Status bits were set in ClassFileParser::post_process_parsed_stream.
// After <clinit>, bits must all be clear, or else we must throw an error.
@ -2134,12 +2134,25 @@ bool InstanceKlass::find_local_field(Symbol* name, Symbol* sig, fieldDescriptor*
if (fs.lookup(name, sig)) {
assert(fs.name() == name, "name must match");
assert(fs.signature() == sig, "signature must match");
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.to_FieldInfo());
fd->reinitialize(this, fs.to_FieldInfo());
return true;
}
return false;
}
bool InstanceKlass::find_local_field(Symbol* name, Symbol* sig, fieldDescriptor* fd, bool also_internal) const {
if (!also_internal) {
return find_local_field( name, sig, fd);
}
for (AllFieldStream fs(this); !fs.done(); fs.next()) {
if (fs.name() == name && fs.signature() == sig) {
fd->reinitialize(this, fs.to_FieldInfo());
return true;
}
}
return false;
}
Klass* InstanceKlass::find_interface_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const {
const int n = local_interfaces()->length();

View File

@ -669,6 +669,8 @@ public:
// find local field, returns true if found
bool find_local_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const;
// find local field, returns true if found
bool find_local_field(Symbol* name, Symbol* sig, fieldDescriptor* fd, bool also_internal) const;
// find field in direct superinterfaces, returns the interface in which the field is defined
Klass* find_interface_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const;
// find field according to JVM spec 5.4.3.2, returns the klass in which the field is defined

View File

@ -249,7 +249,7 @@ class Symbol : public MetaspaceObj {
int index_of_at(int i, const char* substr, int substr_len) const;
// Three-way compare for sorting; returns -1/0/1 if receiver is </==/> than arg
// note that the ordering is not alfabetical
// note that the ordering is not alphabetical
inline int fast_compare(const Symbol* other) const;
// Returns receiver converted to null-terminated UTF-8 string; string is

View File

@ -58,9 +58,7 @@ public abstract class CiReplayBase {
public static final String CLIENT_VM_OPTION = "-client";
public static final String SERVER_VM_OPTION = "-server";
public static final String TEST_CORE_FILE_NAME = "test_core";
public static final String RUN_SHELL_NO_LIMIT = "ulimit -c unlimited && ";
private static final String REPLAY_FILE_OPTION = "-XX:ReplayDataFile=" + REPLAY_FILE_NAME;
private static final String LOCATIONS_STRING = "location: ";
private static final String HS_ERR_NAME = "hs_err_pid";
private static final String RUN_SHELL_ZERO_LIMIT = "ulimit -S -c 0 && ";
private static final String VERSION_OPTION = "-version";

View File

@ -40,7 +40,7 @@ import java.util.stream.Collectors;
public abstract class DumpReplayBase extends CiReplayBase {
private static final String DUMP_REPLAY_PATTERN = "replay_pid";
protected static final String DUMP_REPLAY_PATTERN = "replay_pid";
private List<File> replayFiles;
private String replayFileName;

View File

@ -25,13 +25,15 @@ 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.ArrayList;
import java.util.List;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
public class ReplayFile {
private final Path replayFilePath;
@ -78,4 +80,747 @@ public class ReplayFile {
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 <version>
public record VersionCommand(int version) implements Command {}
// JvmtiExport <field> <value>
public record JvmtiExportCommand(String field, int value) implements Command {}
// instanceKlass <name>
// | @bci <klass> <name> <signature> <bci> <location>* ;
// | @cpi <klass> <cpi> <location>* ;
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<String> location) implements InstanceKlassCommand {}
public record InstanceKlassCommandCpi(String klass, int cpi, List<String> location) implements InstanceKlassCommand {}
// ciInstanceKlass <name> <is_linked> <is_initialized> <length> tag*
public record CiInstanceKlassCommand(String name, boolean isLinked, boolean isInitialized, int length, List<Integer> tag) implements Command {}
// staticfield <klass> <field_name> [IBCSZJFD] <value>
// | "[" [IBCSZJFD] <length>
// | <array-of-klass> <length> "ref" ("nullable" | "null-free") <klass>
// | "flat" ("nullable" | "null-free") ("atomic" | "non-atomic") <klass>
// | "Ljava/lang/String;" <value>
// | <klass> <klass>?
sealed interface StaticFieldCommand extends Command permits
StaticFieldCommandPrimitive,
StaticFieldCommandPrimitiveArray,
StaticFieldCommandRefArray,
StaticFieldCommandFlatArray,
StaticFieldCommandNullArray,
StaticFieldCommandString,
StaticFieldCommandInstance {
String klass();
String fieldName();
String signature();
}
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 String signature() { return "Ljava/lang/String;"; }
}
public record StaticFieldCommandInstance(String klass, String fieldName, String signature, List<String> actualKlassOrValues) implements StaticFieldCommand {}
// ciMethodData <klass> <name> <signature> <state> <invocationCounter> orig <length> <byte>* data <length> <ptr>* oops <length> (<offset> <klass> <array properties>?)* methods <length> (<offset> <klass> <name> <signature>)*
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<Integer> orig, List<String> data, List<CiMethodDataCommandOop> oops, List<CiMethodDataCommandMethod> methods) implements Command {}
// ciMethod <klass> <name> <signature> <invocation_counter> <backedge_counter> <interpreter_invocation_count> <interpreter_throwout_count> <instructions_size>
public record CiMethodCommand(String klass, String name, String signature, int invocationCounter, int backedgeCounter, int interpreterInvocationCount, int interpreterThrowoutCount, int instructionsSize) implements Command {}
// compile <klass> <name> <signature> <entry_bci> <comp_level> inline <count> (<depth> <bci> <inline_late> <klass> <name> <signature>)*
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<CompileCommandInline> inlines) implements Command {}
ParsedReplayFile(List<Command> commands) { this.commands = commands; }
List<Command> commands;
// Set by sanity checking
boolean checked = false;
// Set by indexing, only after sanity checking
public record StaticField(String klass, String name) {}
HashMap<StaticField, StaticFieldCommand> staticFieldCommands = null;
static public ParsedReplayFile parse(File file) throws IOException {
return parse(Files.readAllLines(file.toPath()));
}
static public ParsedReplayFile parse(List<String> lines) {
return new ParsedReplayFile(lines.stream().map(ParsedReplayFile::parseLine).filter(Objects::nonNull).toList());
}
static Command parseLine(String line) {
List<String> 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<String> pieces;
private LinePieces(List<String> 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<String> 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<String> getStrings(int n) {
checkBounds(n);
List<String> sub = pieces.subList(pos, pos + n);
pos += n;
return sub;
}
public List<String> getLeftoverStrings() {
return getStrings(pieces.size() - pos);
}
public int getInt() {
String s = getString();
return Integer.parseInt(s);
}
public List<Integer> getInts(int n) {
List<String> s = getStrings(n);
return s.stream().map(Integer::parseInt).toList();
}
public Optional<Integer> 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<String> 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<String> 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<Integer> 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<String> actualKlassOrValues = pieces.getLeftoverStrings();
return new StaticFieldCommandInstance(klass, fieldName, signature, actualKlassOrValues);
}
// oops <length> (<offset> <klass> <array properties>?)* methods <length> (<offset> <klass> <name> <signature>)*
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<Integer> orig = pieces.getInts(origLength);
pieces.getKeywork("data");
int datalength = pieces.getInt();
List<String> data = pieces.getStrings(datalength);
pieces.getKeywork("oops");
int oopsLength = pieces.getInt();
List<CiMethodDataCommandOop> oops = new ArrayList<>(oopsLength);
for (int i = 0; i < oopsLength; i++) {
int offset = pieces.getInt();
String klass_ = pieces.getString();
Optional<Integer> 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<CiMethodDataCommandMethod> 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<CompileCommandInline> 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);
}
List<String> checkSanity() {
record Method(String klass, String name, String signature) {}
record Field(String klass, String name) {}
List<String> insanities = new ArrayList<>();
int seenVersionCommands = 0;
Set<Method> seenCiMethod = new HashSet<>();
Set<Method> seenCiMethodData = new HashSet<>();
Set<Method> seenCompile = new HashSet<>();
Set<String> seenKlasses = new HashSet<>();
Map<Field, String> seenFields = new HashMap<>();
for (Command c : commands) {
switch (c) {
case CiInstanceKlassCommand(String name, boolean isLinked, boolean isInitialized, int length, List<Integer> tag) -> seenKlasses.add(name);
case StaticFieldCommand cmd -> {
String klass = cmd.klass();
String fieldName = cmd.fieldName();
if (!seenKlasses.contains(klass)) {
insanities.add("Static field command " + cmd + " seen before the corresponding ciInstanceKlass command.");
}
var field = new Field(klass, fieldName);
if (seenFields.containsKey(field)) {
insanities.add("Already seen the static field " + klass + "::" + fieldName + " with signature " + seenFields.get(field) + ". This time, it had signature " + cmd.signature() + ".");
} else {
seenFields.put(field, cmd.signature());
}
}
case CompileCommand(String klass, String name, String signature, int entryBci, int compLevel, List<CompileCommandInline> 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<Integer> orig, List<String> data, List<CiMethodDataCommandOop> oops, List<CiMethodDataCommandMethod> 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);
}
checked = true;
return insanities;
}
// Use it only after checkSanity.
void index() {
Asserts.assertTrue(checked);
staticFieldCommands = new HashMap<>();
for (Command c : commands) {
switch (c) {
case StaticFieldCommand cmd -> {
String klass = cmd.klass();
String fieldName = cmd.fieldName();
staticFieldCommands.put(new StaticField(klass, fieldName), cmd);
}
case CiInstanceKlassCommand _,
CiMethodCommand _,
CiMethodDataCommand _,
CompileCommand _,
InstanceKlassCommand _,
JvmtiExportCommand _,
VersionCommand _ -> {}
}
}
}
static Optional<Integer> 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<String> differences) {
Optional<Integer> lhsVersion = getVersion(lhs);
Optional<Integer> rhsVersion = getVersion(rhs);
if (lhsVersion.isPresent() && rhsVersion.isPresent() && !lhsVersion.get().equals(rhsVersion.get())) {
differences.add("Versions mismatch: lhs=" + lhsVersion.get() + "; rhs=" + rhsVersion.get());
}
}
static <T> HashSet<T> extractSet(ParsedReplayFile parsed, BiConsumer<HashSet<T>, Command> f) {
return parsed.commands.stream().collect(
HashSet::new,
f,
HashSet::addAll
);
}
static <T> void diffSets(String name, HashSet<T> lhs, HashSet<T> rhs, List<String> 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 <T, U> HashMap<T, U> extractMap(ParsedReplayFile parsed, BiConsumer<HashMap<T, U>, Command> f) {
return parsed.commands.stream().collect(
HashMap::new,
f,
HashMap::putAll
);
}
static <T, U> void diffMaps(String name, HashMap<T, U> lhs, HashMap<T, U> rhs, BiPredicate<U, U> eqValue, List<String> 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<String> differences) {
BiConsumer<HashMap<String, Integer>, Command> folder = (acc, command) -> {
if (command instanceof JvmtiExportCommand(String field, int value)) {
acc.put(field, value);
}
};
HashMap<String, Integer> lhsJvmti = extractMap(lhs, folder);
HashMap<String, Integer> rhsJvmti = extractMap(rhs, folder);
diffMaps("JvmtiExport", lhsJvmti, rhsJvmti, Integer::equals, differences);
}
static void compareInstanceKlassNames(ParsedReplayFile lhs, ParsedReplayFile rhs, List<String> differences) {
BiConsumer<HashSet<String>, Command> folder = (acc, command) -> {
if (command instanceof InstanceKlassCommandName(String name)) {
acc.add(name);
}
};
HashSet<String> lhsKlasses = extractSet(lhs, folder);
HashSet<String> rhsKlasses = extractSet(rhs, folder);
diffSets("InstanceKlass", lhsKlasses, rhsKlasses, differences);
}
static void compareInstanceKlassCpi(ParsedReplayFile lhs, ParsedReplayFile rhs, List<String> differences) {
record Key(String klass, int cpi) {}
BiConsumer<HashMap<Key, List<String>>, Command> folder = (acc, command) -> {
if (command instanceof InstanceKlassCommandCpi(String klass, int cpi, List<String> 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<String> differences) {
record Key(String klass, String name, String signature, int bci) {}
BiConsumer<HashMap<Key, List<String>>, Command> folder = (acc, command) -> {
if (command instanceof InstanceKlassCommandBci(String klass, String name, String signature, int bci, List<String> 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<String> differences) {
compareInstanceKlassNames(lhs, rhs, differences);
compareInstanceKlassCpi(lhs, rhs, differences);
compareInstanceKlassBci(lhs, rhs, differences);
}
static void compareCiInstanceKlasses(ParsedReplayFile lhs, ParsedReplayFile rhs, List<String> differences) {
record Element(String name, boolean isLinked, boolean isInitialized, int length) {}
BiConsumer<HashSet<Element>, Command> folder = (acc, command) -> {
if (command instanceof CiInstanceKlassCommand(String name, boolean isLinked, boolean isInitialized, int length, List<Integer> _)) {
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<String> differences) {
record Key(String klass, String fieldName, String signature) {}
BiConsumer<HashMap<Key, String>, 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<String> differences) {
record Key(String klass, String fieldName, String signature) {}
BiConsumer<HashMap<Key, Integer>, 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<String> differences) {
record Key(String klass, String fieldName, String signature) {}
record Value(int length, boolean nullFree, String actualKlass) {}
BiConsumer<HashMap<Key, Value>, 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<String> differences) {
record Key(String klass, String fieldName, String signature) {}
record Value(int length, boolean nullFree, boolean nonAtomic, String actualKlass) {}
BiConsumer<HashMap<Key, Value>, 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<String> differences) {
record Element(String klass, String fieldName, String signature) {}
BiConsumer<HashSet<Element>, 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<String> differences) {
record Key(String klass, String fieldName) {}
BiConsumer<HashMap<Key, String>, 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<String> differences) {
record Key(String klass, String fieldName, String signature) {}
BiConsumer<HashMap<Key, List<String>>, Command> folder = (acc, command) -> {
if (command instanceof StaticFieldCommandInstance(String klass, String fieldName, String signature, List<String> 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<String> 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<String> differences) {
record Key(String klass, String name, String signature) {}
record Value(int state, int invocationCounter) {}
BiConsumer<HashMap<Key, Value>, Command> folder = (acc, command) -> {
if (command instanceof CiMethodDataCommand(String klass, String name, String signature, int state, int invocationCounter, List<Integer> _, List<String> _, List<CiMethodDataCommandOop> _, List<CiMethodDataCommandMethod> _)) {
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<String> differences) {
record Key(String klass, String name, String signature) {}
record Value(int invocationCounter, int backedgeCounter, int interpreterInvocationCount, int interpreterThrowoutCount, int instructionsSize) {}
BiConsumer<HashMap<Key, Value>, 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<String> differences) {
record Key(String klass, String name, String signature) {}
record Value(int entryBci, int compLevel, List<CompileCommandInline> inlines) {}
BiConsumer<HashMap<Key, Value>, Command> folder = (acc, command) -> {
if (command instanceof CompileCommand(String klass, String name, String signature, int entryBci, int compLevel, List<CompileCommandInline> 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<String> findDifferences(ParsedReplayFile lhs, ParsedReplayFile rhs) {
List<String> 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;
}
Optional<StaticFieldCommand> findStaticFieldCommand(String klass, String fieldName) {
Asserts.assertNotNull(staticFieldCommands); // Must be already indexed
var f = new StaticField(klass, fieldName);
if (!staticFieldCommands.containsKey(f)) {
return Optional.empty();
}
return Optional.ofNullable(staticFieldCommands.get(f));
}
}
}

View File

@ -30,8 +30,8 @@
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions
* -Xbootclasspath/a:. -XX:+WhiteBoxAPI
* -Xbatch -XX:-TieredCompilation -XX:+AlwaysIncrementalInline
* -XX:CompileCommand=compileonly,compiler.ciReplay.TestDumpReplay::*
* compiler.ciReplay.TestDumpReplay
* -XX:CompileCommand=compileonly,${test.main.class}::*
* ${test.main.class}
*/
package compiler.ciReplay;

View File

@ -32,7 +32,7 @@
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+TieredCompilation
* compiler.ciReplay.TestDumpReplayCommandLine
* ${test.main.class}
*/
package compiler.ciReplay;
@ -40,15 +40,7 @@ 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.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestDumpReplayCommandLine extends DumpReplayBase {

View File

@ -31,7 +31,7 @@
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* compiler.ciReplay.TestIncrementalInlining
* ${test.main.class}
*/
package compiler.ciReplay;

View File

@ -31,7 +31,7 @@
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* compiler.ciReplay.TestInlining
* ${test.main.class}
*/
package compiler.ciReplay;

View File

@ -31,7 +31,7 @@
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* compiler.ciReplay.TestInliningProtectionDomain
* ${test.main.class}
*/
package compiler.ciReplay;

View File

@ -29,9 +29,11 @@
* @requires vm.compMode != "Xint"
* @modules java.base/jdk.internal.misc
* java.management
* @run driver TestInvalidReplayFile
* @run driver ${test.main.class}
*/
package compiler.ciReplay;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;

View File

@ -31,7 +31,7 @@
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* compiler.ciReplay.TestLambdas
* ${test.main.class}
*/
package compiler.ciReplay;

View File

@ -31,7 +31,7 @@
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* compiler.ciReplay.TestNoClassFile
* ${test.main.class}
*/
package compiler.ciReplay;

View File

@ -31,7 +31,7 @@
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* compiler.ciReplay.TestNullStaticField
* ${test.main.class}
*/
package compiler.ciReplay;

View File

@ -0,0 +1,238 @@
/*
* 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.
*/
/*
* @test
* @bug 8375548
* @enablePreview
* @library / /test/lib
* @summary Testing the additions and fixes of Replay file v4
* @requires vm.flagless & vm.flightRecorder != true & vm.compMode != "Xint" & vm.compMode != "Xcomp" &
* vm.debug == true & vm.compiler2.enabled
* @modules java.base/jdk.internal.misc
* java.base/jdk.internal.value
* java.base/jdk.internal.vm.annotation
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+TieredCompilation
* ${test.main.class}
*/
package compiler.ciReplay;
import jdk.internal.value.ValueClass;
import jdk.internal.vm.annotation.NullRestricted;
import jdk.test.lib.Asserts;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import compiler.ciReplay.ReplayFile.ParsedReplayFile;
import compiler.ciReplay.ReplayFile.ParsedReplayFile.*;
public class TestReplayV4 extends DumpReplayBase {
private final String[] defaultReplayRunFlags;
public static void main(String[] args) {
new TestReplayV4().runTest("-XX:CompileCommand=dontinline,*::*",
"--enable-preview",
"--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
TIERED_DISABLED_VM_OPTION);
}
private TestReplayV4() {
defaultReplayRunFlags = defaultReplayRunFlags();
}
private String[] defaultReplayRunFlags() {
List<String> vmFlags = new ArrayList<>();
Collections.addAll(vmFlags,
"-XX:+ReplayIgnoreInitErrors",
"-XX:CompileCommand=dontinline,*::*",
"--enable-preview",
"--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
TIERED_DISABLED_VM_OPTION
);
return vmFlags.toArray(new String[0]);
}
@Override
public void testAction() {
reDumpAndCompare();
}
private String makeMessageFromList(String header, List<String> lines) {
var message = new StringBuilder(header);
message.append(":\n");
for (String diff : lines) {
message.append(" - ").append(diff).append("\n");
}
return message.toString();
}
private void reDumpAndCompare() {
ParsedReplayFile firstParsedReplay;
ParsedReplayFile secondParsedReplay;
try {
String[] reDumpingFlags = Arrays.copyOf(defaultReplayRunFlags, defaultReplayRunFlags.length + 2);
reDumpingFlags[defaultReplayRunFlags.length] = "-XX:CompileCommand=option," + "*::*" + ",bool,DumpReplay,true";
reDumpingFlags[defaultReplayRunFlags.length+1] = "-XX:CompileCommand=PrintCompilation,*::*";
Asserts.assertEQ(getReplayFiles().size(), 1);
File firstReplay = getReplayFiles().getFirst();
positiveTest(reDumpingFlags);
List<File> replayFilesPostRun;
try (Stream<Path> files = Files.list(Paths.get("."))) {
replayFilesPostRun = files.map(Path::toFile).filter(f -> f.getName().startsWith(DUMP_REPLAY_PATTERN)).toList();
}
Asserts.assertEQ(replayFilesPostRun.size(), 2);
Asserts.assertTrue(replayFilesPostRun.contains(firstReplay));
var secondReplayOpt = replayFilesPostRun.stream().filter(file -> !file.equals(firstReplay)).findAny();
Asserts.assertTrue(secondReplayOpt.isPresent());
var secondReplay = secondReplayOpt.get();
System.out.println("Replay read by the second run: "+firstReplay+"; replay produced: "+secondReplay);
firstParsedReplay = ParsedReplayFile.parse(firstReplay);
secondParsedReplay = ParsedReplayFile.parse(secondReplay);
} catch (Throwable t) {
System.out.println(t);
System.out.println(t.getMessage());
throw new Error("Can't find replay: " + t, t);
}
// First, let's make sure replay files are not crazy.
var firstInsanities = firstParsedReplay.checkSanity();
Asserts.assertTrue(firstInsanities.isEmpty(), makeMessageFromList("Insane first replay file", firstInsanities));
var secondInsanities = secondParsedReplay.checkSanity();
Asserts.assertTrue(secondInsanities.isEmpty(), makeMessageFromList("Insane second replay file", secondInsanities));
// For lookup later. This is allowed only after sanity checking.
firstParsedReplay.index();
secondParsedReplay.index();
// Now, we make sure they have equivalent enough content.
var differences = ParsedReplayFile.findDifferences(firstParsedReplay, secondParsedReplay);
Asserts.assertTrue(differences.isEmpty(), makeMessageFromList("Differences", differences));
// Finally, we check a few facts about the second replay file.
var oArrNullCmdOpt = secondParsedReplay.findStaticFieldCommand("compiler/ciReplay/TestReplayV4$Test", "oArrNull");
Asserts.assertTrue(oArrNullCmdOpt.isPresent());
var oArrNullCmd = oArrNullCmdOpt.get();
Asserts.assertTrue(oArrNullCmd instanceof StaticFieldCommandNullArray);
var oArrRefArrayCmdOpt = secondParsedReplay.findStaticFieldCommand("compiler/ciReplay/TestReplayV4$Test", "oArrRefArray");
Asserts.assertTrue(oArrRefArrayCmdOpt.isPresent());
var oArrRefArrayCmdUntyped = oArrRefArrayCmdOpt.get();
Asserts.assertTrue(oArrRefArrayCmdUntyped instanceof StaticFieldCommandRefArray);
var oArrRefArrayCmd = (StaticFieldCommandRefArray)oArrRefArrayCmdUntyped;
Asserts.assertFalse(oArrRefArrayCmd.nullFree());
Asserts.assertEquals(oArrRefArrayCmd.length(), 2);
var oArrNullableAtomicArrayCmdOpt = secondParsedReplay.findStaticFieldCommand("compiler/ciReplay/TestReplayV4$Test", "oArrNullableAtomicArray");
Asserts.assertTrue(oArrNullableAtomicArrayCmdOpt.isPresent());
var oArrNullableAtomicArrayCmdUntyped = oArrNullableAtomicArrayCmdOpt.get();
Asserts.assertTrue(oArrNullableAtomicArrayCmdUntyped instanceof StaticFieldCommandFlatArray);
var oArrNullableAtomicArrayCmd = (StaticFieldCommandFlatArray)oArrNullableAtomicArrayCmdUntyped;
Asserts.assertFalse(oArrNullableAtomicArrayCmd.nullFree());
Asserts.assertFalse(oArrNullableAtomicArrayCmd.nonAtomic());
Asserts.assertEquals(oArrNullableAtomicArrayCmd.length(), 2);
var oArrNullRestrictedAtomicArrayCmdOpt = secondParsedReplay.findStaticFieldCommand("compiler/ciReplay/TestReplayV4$Test", "oArrNullRestrictedAtomicArray");
Asserts.assertTrue(oArrNullRestrictedAtomicArrayCmdOpt.isPresent());
var oArrNullRestrictedAtomicArrayCmdUntyped = oArrNullRestrictedAtomicArrayCmdOpt.get();
Asserts.assertTrue(oArrNullRestrictedAtomicArrayCmdUntyped instanceof StaticFieldCommandFlatArray);
var oArrNullRestrictedAtomicArrayCmd = (StaticFieldCommandFlatArray)oArrNullRestrictedAtomicArrayCmdUntyped;
Asserts.assertTrue(oArrNullRestrictedAtomicArrayCmd.nullFree());
Asserts.assertFalse(oArrNullRestrictedAtomicArrayCmd.nonAtomic());
Asserts.assertEquals(oArrNullRestrictedAtomicArrayCmd.length(), 2);
}
@Override
public String getTestClass() {
return Test.class.getName();
}
private static class Test {
static final Base[] oArrDefault = new Base[2];
static final Base[] oArrNullableAtomicArray = (Base[]) ValueClass.newNullableAtomicArray(Derived.class, 2);
static final Base[] oArrNullRestrictedAtomicArray = (Base[]) ValueClass.newNullRestrictedAtomicArray(Derived.class, 2, new Derived(1, 0));
static final Base[] oArrNullRestrictedNonAtomicArray = (Base[]) ValueClass.newNullRestrictedNonAtomicArray(Derived.class, 2, new Derived(2, 0));
static final Base[] oArrRefArray = (Base[]) ValueClass.newReferenceArray(Derived.class, 2);
static final Base[] oArrNull = null;
static Base o1, o2, o3, o4;
static final Base a = new Derived(10, 15);
static final Base a_base_null = null;
static final Derived a_derived_null = null;
@NullRestricted
static final Base a_base_null_free = new Derived(10, 15);
@NullRestricted
static final Derived a_derived_null_free = new Derived(10, 15);
public static void main(String[] args) {
oArrDefault[0] = new Derived(3, 5);
oArrNullableAtomicArray[0] = new Derived(4, 6);
oArrNullRestrictedAtomicArray[0] = new Derived(5, 7);
oArrNullRestrictedNonAtomicArray[0] = new Derived(6, 8);
oArrRefArray[0] = new Derived(7, 9);
for (int i = 0; i < 10000; i++) {
test();
}
}
static void test() {
o1 = oArrDefault[0];
oArrDefault[1] = a;
o2 = oArrNullableAtomicArray[0];
oArrNullableAtomicArray[1] = a;
o3 = oArrNullRestrictedAtomicArray[0];
oArrNullRestrictedAtomicArray[1] = a;
o4 = oArrNullRestrictedNonAtomicArray[0];
oArrNullRestrictedNonAtomicArray[1] = a;
}
static abstract value class Base {
short x;
byte y;
public Base(int x, int y) {
this.x = (short)x;
this.y = (byte)y;
}
}
static value class Derived extends Base {
public Derived(int x, int y) {
super(x, y);
}
}
}
}

View File

@ -31,7 +31,7 @@
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* compiler.ciReplay.TestUnresolvedClasses
* ${test.main.class}
*/
package compiler.ciReplay;

View File

@ -35,7 +35,7 @@
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+TieredCompilation
* compiler.ciReplay.TestValueClassArrays
* ${test.main.class}
*/
package compiler.ciReplay;