Skip to content

Commit

Permalink
8293980: Resolve CONSTANT_FieldRef at CDS dump time
Browse files Browse the repository at this point in the history
Reviewed-by: erikj, matsaave, heidinga
  • Loading branch information
iklam committed Jun 14, 2024
1 parent eb2488f commit b818679
Show file tree
Hide file tree
Showing 35 changed files with 1,119 additions and 173 deletions.
13 changes: 11 additions & 2 deletions make/GenerateLinkOptData.gmk
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,23 @@ ifeq ($(EXTERNAL_BUILDJDK), true)
INTERIM_IMAGE_DIR := $(BUILD_JDK)
endif

# These are needed for deterministic classlist:
# - The classlist can be influenced by locale. Always set it to en/US.
# - Run with -Xint, as the compiler can speculatively resolve constant pool entries.
# - ForkJoinPool parallelism can cause constant pool resolution to be non-deterministic.
CLASSLIST_FILE_VM_OPTS = \
-Duser.language=en -Duser.country=US \
-Xint \
-Djava.util.concurrent.ForkJoinPool.common.parallelism=0

# Save the stderr output of the command and print it along with stdout in case
# something goes wrong.
$(CLASSLIST_FILE): $(INTERIM_IMAGE_DIR)/bin/java$(EXECUTABLE_SUFFIX) $(CLASSLIST_JAR)
$(call MakeDir, $(LINK_OPT_DIR))
$(call LogInfo, Generating $(patsubst $(OUTPUTDIR)/%, %, $@))
$(call LogInfo, Generating $(patsubst $(OUTPUTDIR)/%, %, $(JLI_TRACE_FILE)))
$(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@.raw \
-Duser.language=en -Duser.country=US \
$(CLASSLIST_FILE_VM_OPTS) \
-cp $(SUPPORT_OUTPUTDIR)/classlist.jar \
build.tools.classlist.HelloClasslist $(LOG_DEBUG)
$(GREP) -v HelloClasslist $@.raw > $@.interim
Expand All @@ -79,7 +88,7 @@ $(CLASSLIST_FILE): $(INTERIM_IMAGE_DIR)/bin/java$(EXECUTABLE_SUFFIX) $(CLASSLIST
$(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@.raw.2 \
-XX:SharedClassListFile=$@.interim -XX:SharedArchiveFile=$@.jsa \
-Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true \
-Duser.language=en -Duser.country=US \
$(CLASSLIST_FILE_VM_OPTS) \
--module-path $(SUPPORT_OUTPUTDIR)/classlist.jar \
-cp $(SUPPORT_OUTPUTDIR)/classlist.jar \
build.tools.classlist.HelloClasslist \
Expand Down
18 changes: 18 additions & 0 deletions src/hotspot/share/cds/archiveBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,14 @@ void ArchiveBuilder::write_pointer_in_buffer(address* ptr_location, address src_
}
}

void ArchiveBuilder::mark_and_relocate_to_buffered_addr(address* ptr_location) {
assert(*ptr_location != nullptr, "sanity");
if (!is_in_mapped_static_archive(*ptr_location)) {
*ptr_location = get_buffered_addr(*ptr_location);
}
ArchivePtrMarker::mark_pointer(ptr_location);
}

address ArchiveBuilder::get_buffered_addr(address src_addr) const {
SourceObjInfo* p = _src_obj_table.get(src_addr);
assert(p != nullptr, "src_addr " INTPTR_FORMAT " is used but has not been archived",
Expand Down Expand Up @@ -755,6 +763,16 @@ void ArchiveBuilder::make_klasses_shareable() {
int num_obj_array_klasses = 0;
int num_type_array_klasses = 0;

for (int i = 0; i < klasses()->length(); i++) {
// Some of the code in ConstantPool::remove_unshareable_info() requires the classes
// to be in linked state, so it must be call here before the next loop, which returns
// all classes to unlinked state.
Klass* k = get_buffered_addr(klasses()->at(i));
if (k->is_instance_klass()) {
InstanceKlass::cast(k)->constants()->remove_unshareable_info();
}
}

for (int i = 0; i < klasses()->length(); i++) {
const char* type;
const char* unlinked = "";
Expand Down
5 changes: 5 additions & 0 deletions src/hotspot/share/cds/archiveBuilder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,11 @@ class ArchiveBuilder : public StackObj {
write_pointer_in_buffer((address*)ptr_location, (address)src_addr);
}

void mark_and_relocate_to_buffered_addr(address* ptr_location);
template <typename T> void mark_and_relocate_to_buffered_addr(T ptr_location) {
mark_and_relocate_to_buffered_addr((address*)ptr_location);
}

address get_buffered_addr(address src_addr) const;
template <typename T> T get_buffered_addr(T src_addr) const {
return (T)get_buffered_addr((address)src_addr);
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/cds/archiveUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ void ArchiveUtils::log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) {
ResourceMark rm(THREAD);
int pool_index = bootstrap_specifier->bss_index();
ClassListWriter w;
w.stream()->print("%s %s", LAMBDA_PROXY_TAG, pool->pool_holder()->name()->as_C_string());
w.stream()->print("%s %s", ClassListParser::lambda_proxy_tag(), pool->pool_holder()->name()->as_C_string());
CDSIndyInfo cii;
ClassListParser::populate_cds_indy_info(pool, pool_index, &cii, CHECK);
GrowableArray<const char*>* indy_items = cii.items();
Expand Down
139 changes: 130 additions & 9 deletions src/hotspot/share/cds/classListParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "cds/archiveUtils.hpp"
#include "cds/classListParser.hpp"
#include "cds/classPrelinker.hpp"
#include "cds/lambdaFormInvokers.hpp"
#include "cds/metaspaceShared.hpp"
#include "cds/unregisteredClasses.hpp"
Expand Down Expand Up @@ -52,6 +53,10 @@
#include "utilities/macros.hpp"
#include "utilities/utf8.hpp"

const char* ClassListParser::CONSTANT_POOL_TAG = "@cp";
const char* ClassListParser::LAMBDA_FORM_TAG = "@lambda-form-invoker";
const char* ClassListParser::LAMBDA_PROXY_TAG = "@lambda-proxy";

volatile Thread* ClassListParser::_parsing_thread = nullptr;
ClassListParser* ClassListParser::_instance = nullptr;

Expand Down Expand Up @@ -299,6 +304,9 @@ void ClassListParser::parse_at_tags(TRAPS) {
}
} else if (strcmp(_token, LAMBDA_FORM_TAG) == 0) {
LambdaFormInvokers::append(os::strdup((const char*)(_line + offset), mtInternal));
} else if (strcmp(_token, CONSTANT_POOL_TAG) == 0) {
_token = _line + offset;
parse_constant_pool_tag();
} else {
error("Invalid @ tag at the beginning of line \"%s\" line #%zu", _token, lineno());
}
Expand Down Expand Up @@ -395,9 +403,14 @@ void ClassListParser::print_actual_interfaces(InstanceKlass* ik) {
jio_fprintf(defaultStream::error_stream(), "}\n");
}

void ClassListParser::error(const char* msg, ...) {
void ClassListParser::print_diagnostic_info(outputStream* st, const char* msg, ...) {
va_list ap;
va_start(ap, msg);
print_diagnostic_info(st, msg, ap);
va_end(ap);
}

void ClassListParser::print_diagnostic_info(outputStream* st, const char* msg, va_list ap) {
int error_index = pointer_delta_as_int(_token, _line);
if (error_index >= _line_len) {
error_index = _line_len - 1;
Expand All @@ -412,25 +425,34 @@ void ClassListParser::error(const char* msg, ...) {
jio_vfprintf(defaultStream::error_stream(), msg, ap);

if (_line_len <= 0) {
jio_fprintf(defaultStream::error_stream(), "\n");
st->print("\n");
} else {
jio_fprintf(defaultStream::error_stream(), ":\n");
st->print(":\n");
for (int i=0; i<_line_len; i++) {
char c = _line[i];
if (c == '\0') {
jio_fprintf(defaultStream::error_stream(), "%s", " ");
st->print("%s", " ");
} else {
jio_fprintf(defaultStream::error_stream(), "%c", c);
st->print("%c", c);
}
}
jio_fprintf(defaultStream::error_stream(), "\n");
st->print("\n");
for (int i=0; i<error_index; i++) {
jio_fprintf(defaultStream::error_stream(), "%s", " ");
st->print("%s", " ");
}
jio_fprintf(defaultStream::error_stream(), "^\n");
st->print("^\n");
}
va_end(ap);
}

void ClassListParser::error(const char* msg, ...) {
va_list ap;
va_start(ap, msg);
fileStream fs(defaultStream::error_stream());
//TODO: we should write to UL/error instead, but that requires fixing some tests cases.
//LogTarget(Error, cds) lt;
//LogStream ls(lt);
print_diagnostic_info(&fs, msg, ap);
va_end(ap);
vm_exit_during_initialization("class list format error.", nullptr);
}

Expand All @@ -453,6 +475,16 @@ void ClassListParser::check_class_name(const char* class_name) {
}
}

void ClassListParser::constant_pool_resolution_warning(const char* msg, ...) {
va_list ap;
va_start(ap, msg);
LogTarget(Warning, cds, resolve) lt;
LogStream ls(lt);
print_diagnostic_info(&ls, msg, ap);
ls.print("Your classlist may be out of sync with the JDK or the application.");
va_end(ap);
}

// This function is used for loading classes for customized class loaders
// during archive dumping.
InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS) {
Expand Down Expand Up @@ -727,3 +759,92 @@ InstanceKlass* ClassListParser::lookup_interface_for_current_class(Symbol* inter
ShouldNotReachHere();
return nullptr;
}

InstanceKlass* ClassListParser::find_builtin_class_helper(JavaThread* current, Symbol* class_name_symbol, oop class_loader_oop) {
Handle class_loader(current, class_loader_oop);
Handle protection_domain;
return SystemDictionary::find_instance_klass(current, class_name_symbol, class_loader, protection_domain);
}

InstanceKlass* ClassListParser::find_builtin_class(JavaThread* current, const char* class_name) {
TempNewSymbol class_name_symbol = SymbolTable::new_symbol(class_name);
InstanceKlass* ik;

if ( (ik = find_builtin_class_helper(current, class_name_symbol, nullptr)) != nullptr
|| (ik = find_builtin_class_helper(current, class_name_symbol, SystemDictionary::java_platform_loader())) != nullptr
|| (ik = find_builtin_class_helper(current, class_name_symbol, SystemDictionary::java_system_loader())) != nullptr) {
return ik;
} else {
return nullptr;
}
}

void ClassListParser::parse_constant_pool_tag() {
if (parse_lambda_forms_invokers_only()) {
return;
}

JavaThread* THREAD = JavaThread::current();
skip_whitespaces();
char* class_name = _token;
skip_non_whitespaces();
*_token = '\0';
_token ++;

InstanceKlass* ik = find_builtin_class(THREAD, class_name);
if (ik == nullptr) {
_token = class_name;
if (strstr(class_name, "/$Proxy") != nullptr ||
strstr(class_name, "MethodHandle$Species_") != nullptr) {
// ignore -- TODO: we should filter these out in classListWriter.cpp
} else {
constant_pool_resolution_warning("class %s is not (yet) loaded by one of the built-in loaders", class_name);
}
return;
}

ResourceMark rm(THREAD);
constantPoolHandle cp(THREAD, ik->constants());
GrowableArray<bool> preresolve_list(cp->length(), cp->length(), false);
bool preresolve_class = false;
bool preresolve_fmi = false;
bool preresolve_indy = false;

while (*_token) {
int cp_index;
skip_whitespaces();
parse_uint(&cp_index);
if (cp_index < 1 || cp_index >= cp->length()) {
constant_pool_resolution_warning("Invalid constant pool index %d", cp_index);
return;
} else {
preresolve_list.at_put(cp_index, true);
}
constantTag cp_tag = cp->tag_at(cp_index);
switch (cp_tag.value()) {
case JVM_CONSTANT_UnresolvedClass:
preresolve_class = true;
break;
case JVM_CONSTANT_UnresolvedClassInError:
case JVM_CONSTANT_Class:
// ignore
break;
case JVM_CONSTANT_Fieldref:
preresolve_fmi = true;
break;
break;
default:
constant_pool_resolution_warning("Unsupported constant pool index %d: %s (type=%d)",
cp_index, cp_tag.internal_name(), cp_tag.value());
return;
}
}

if (preresolve_class) {
ClassPrelinker::preresolve_class_cp_entries(THREAD, ik, &preresolve_list);
}
if (preresolve_fmi) {
ClassPrelinker::preresolve_field_and_method_cp_entries(THREAD, ik, &preresolve_list);
}
}

22 changes: 18 additions & 4 deletions src/hotspot/share/cds/classListParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@
#include "utilities/istream.hpp"
#include "utilities/resizeableResourceHash.hpp"

#define LAMBDA_PROXY_TAG "@lambda-proxy"
#define LAMBDA_FORM_TAG "@lambda-form-invoker"

class constantPoolHandle;
class Thread;

Expand Down Expand Up @@ -68,6 +65,10 @@ class CDSIndyInfo {
};

class ClassListParser : public StackObj {
static const char* CONSTANT_POOL_TAG;
static const char* LAMBDA_FORM_TAG;
static const char* LAMBDA_PROXY_TAG;

public:
enum ParseMode {
_parse_all,
Expand Down Expand Up @@ -117,17 +118,25 @@ class ClassListParser : public StackObj {
void print_actual_interfaces(InstanceKlass *ik);
bool is_matching_cp_entry(const constantPoolHandle &pool, int cp_index, TRAPS);

InstanceKlass* find_builtin_class_helper(JavaThread* current, Symbol* class_name_symbol, oop class_loader_oop);
InstanceKlass* find_builtin_class(JavaThread* current, const char* class_name);

void resolve_indy(JavaThread* current, Symbol* class_name_symbol);
void resolve_indy_impl(Symbol* class_name_symbol, TRAPS);
void clean_up_input_line();
void read_class_name_and_attributes();
void parse_class_name_and_attributes(TRAPS);
Klass* load_current_class(Symbol* class_name_symbol, TRAPS);
void parse_constant_pool_tag();

size_t lineno() { return _input_stream.lineno(); }
FILE* do_open(const char* file);
ClassListParser(const char* file, ParseMode _parse_mode);
~ClassListParser();
void print_diagnostic_info(outputStream* st, const char* msg, va_list ap) ATTRIBUTE_PRINTF(3, 0);
void print_diagnostic_info(outputStream* st, const char* msg, ...) ATTRIBUTE_PRINTF(3, 0);
void constant_pool_resolution_warning(const char* msg, ...) ATTRIBUTE_PRINTF(2, 0);
void error(const char* msg, ...) ATTRIBUTE_PRINTF(2, 0);

public:
static void parse_classlist(const char* classlist_path, ParseMode parse_mode, TRAPS) {
Expand All @@ -141,13 +150,18 @@ class ClassListParser : public StackObj {
assert(_instance != nullptr, "must be");
return _instance;
}
static const char* lambda_proxy_tag() {
return LAMBDA_PROXY_TAG;
}
static const char* lambda_form_tag() {
return LAMBDA_FORM_TAG;
}

void parse(TRAPS);
void split_tokens_by_whitespace(int offset, GrowableArray<const char*>* items);
int split_at_tag_from_line();
void parse_at_tags(TRAPS);
char* _token;
void error(const char* msg, ...);
void parse_int(int* value);
void parse_uint(int* value);
bool try_parse_uint(int* value);
Expand Down
Loading

1 comment on commit b818679

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.