Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8259070: Add jcmd option to dump CDS #2737

Closed
wants to merge 17 commits into from
Closed
Changes from 1 commit
Commits
File filter
Filter file types
Jump to
Jump to file
Failed to load files.

Always

Just for now

@@ -52,6 +52,8 @@ JVM_DefineClass
JVM_DefineClassWithSource
JVM_DesiredAssertionStatus
JVM_DumpAllStacks
JVM_DumpClassListToFile
JVM_DumpDynamicArchive
JVM_DumpThreads
JVM_FillInStackTrace
JVM_FindClassFromCaller
@@ -84,6 +86,7 @@ JVM_GetClassNameUTF
JVM_GetClassSignature
JVM_GetClassSigners
JVM_GetClassTypeAnnotations
JVM_GetVMArguments
JVM_GetCPClassNameUTF
JVM_GetCPFieldClassNameUTF
JVM_GetCPFieldModifiers
@@ -300,6 +300,8 @@
template(jdk_internal_misc_CDS, "jdk/internal/misc/CDS") \
template(generateLambdaFormHolderClasses, "generateLambdaFormHolderClasses") \
template(generateLambdaFormHolderClasses_signature, "([Ljava/lang/String;)[Ljava/lang/Object;") \
template(dumpSharedArchive, "dumpSharedArchive") \
template(dumpSharedArchive_signature, "(ZLjava/lang/String;)V") \

This comment has been minimized.

@iklam

iklam Mar 31, 2021
Member

Need to align the "dumpSharedArchive" part with the previous line.

\
/* Intrinsic Annotation (JDK 9 and above) */ \
template(jdk_internal_vm_annotation_DontInline_signature, "Ljdk/internal/vm/annotation/DontInline;") \
@@ -185,6 +185,9 @@ JVM_LookupLambdaProxyClassFromArchive(JNIEnv* env, jclass caller,
jobject implMethodMember,
jobject instantiatedMethodType);

JNIEXPORT jobjectArray JNICALL
JVM_GetVMArguments(JNIEnv* env);

JNIEXPORT jboolean JNICALL
JVM_IsCDSDumpingEnabled(JNIEnv* env);

@@ -200,6 +203,12 @@ JVM_GetRandomSeedForDumping();
JNIEXPORT void JNICALL
JVM_LogLambdaFormInvoker(JNIEnv* env, jstring line);

JNIEXPORT void JNICALL
JVM_DumpClassListToFile(JNIEnv* env, jstring fileName);

JNIEXPORT void JNICALL
JVM_DumpDynamicArchive(JNIEnv* env, jstring archiveName);

/*
* java.lang.Throwable
*/
@@ -343,12 +343,7 @@ bool DynamicArchive::_has_been_dumped_once = false;

void DynamicArchive::dump() {
if (Arguments::GetSharedDynamicArchivePath() == NULL) {
if (!RecordDynamicDumpInfo) {
// If run with -XX:+RecordDynamicDumpInfo, DynamicDumpSharedSpaces will be turned on,
// but ArchiveClassesAtExit, ie, the shared archive file is not specified. To differ the
// two cases, silence when RecordDynamicDumpInfo is on.
log_warning(cds, dynamic)("SharedDynamicArchivePath is not specified");
}
log_warning(cds, dynamic)("SharedDynamicArchivePath is not specified");
return;
}

@@ -777,132 +777,6 @@ bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) {
}
}

void append_strings(char* buffer, size_t buff_len, const char* arg) {
char* start = buffer + strlen(buffer);
snprintf(start, buff_len, "%s ", arg);
}

void MetaspaceShared::cmd_dump_shared_archive(outputStream* output, const char* cmd, const char* file_name, TRAPS) {
// The existing file will be overwritten.
char filename[JVM_MAXPATHLEN];
const char* file = file_name;
assert(strcmp(cmd, "static_dump") == 0 || strcmp(cmd, "dynamic_dump") == 0, "Sanity check");
bool is_static = strcmp(cmd, "static_dump") == 0;
if (is_static) {
output->print_cr("Static dump");
if (file_name ==nullptr) {
os::snprintf(filename, sizeof(filename), "java_pid%d_static.jsa", os::current_process_id());
file = filename;
} else {
if (strstr(file_name, ".jsa") == nullptr) {
os::snprintf(filename, sizeof(filename), "%s.jsa", file_name);
file = filename;
}
}
cmd_dump_static(output, file, THREAD);
} else {
output->print_cr("Dynamic dump");
if (!UseSharedSpaces) {
output->print_cr("CDS is not available for this version.");
return;
}
if (!RecordDynamicDumpInfo) {
output->print_cr("Please run with -Xshare:auto -XX:+RecordDynamicDumpInfo dumping dynamic archive!");
return;
}
if (file_name == nullptr) {
os::snprintf(filename, sizeof(filename), "java_pid%d_dynamic.jsa", os::current_process_id());
file = filename;
} else {
if (strstr(file_name, ".jsa") == nullptr) {
os::snprintf(filename, sizeof(filename), "%s.jsa", file_name);
file = filename;
}
}
cmd_dump_dynamic(output, file, THREAD);
}
}

class DumpClassListCLDClosure : public CLDClosure {
fileStream *_stream;
public:
DumpClassListCLDClosure(fileStream* f) : CLDClosure() { _stream = f; }
~DumpClassListCLDClosure() {
delete _stream; // The file need close since in child process it will be used.
}
void do_cld(ClassLoaderData* cld) {
for (Klass* klass = cld->klasses(); klass != NULL; klass = klass->next_link()) {
if (klass->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(klass);
if (ik->is_shareable()) {
_stream->print_cr("%s", ik->name()->as_C_string());
}
}
}
}
};

// To create a static dump, steps:
// 1. output a classlist file
// 2. fork a new process to dump the shared archive
void MetaspaceShared::cmd_dump_static(outputStream* output, const char* file, TRAPS) {
const char* java_home = Arguments::get_java_home();
const char* file_separator = os::file_separator();
const char* app_class_path = Arguments::get_appclasspath(); // -cp ..
const char* java_command = Arguments::java_command(); // arguments to app
char exec_path[JVM_MAXPATHLEN]; // $JAVA_HOME/bin/java or %JAVA_HOME%\\jre\bin\\java ...
char classlist_name[JVM_MAXPATHLEN];

os::snprintf(classlist_name, sizeof(classlist_name), "%s.classlist", file);

ResourceMark rm;
fileStream* stream = new (ResourceObj::C_HEAP, mtInternal) fileStream(classlist_name, "w");
if (stream->is_open()) {
MutexLocker lock(ClassLoaderDataGraph_lock);
DumpClassListCLDClosure collect_classes(stream);
ClassLoaderDataGraph::loaded_cld_do(&collect_classes);
} else {
output->print_cr("Error to open %s for write!", classlist_name);
return;
}
os::snprintf(exec_path, sizeof(exec_path),
"%s%sbin%sjava -Xshare:dump -XX:SharedClassListFile=%s -XX:SharedArchiveFile=%s ",
java_home, file_separator, file_separator, classlist_name, file);
int num_vm_args = Arguments::num_jvm_args();
char** vm_args = Arguments::jvm_args_array();
for (int i = 0; i < num_vm_args; i ++) {
append_strings(exec_path, sizeof(exec_path), vm_args[i]);
}
// Turn off RecordDynamicDumpInfo
if (RecordDynamicDumpInfo) {
append_strings(exec_path, sizeof(exec_path), "-XX:-RecordDynamicDumpInfo");
}
char* buff_start = exec_path + strlen(exec_path);
snprintf(buff_start, sizeof(exec_path), " -cp %s %s", app_class_path, java_command);
output->print_cr("%s", exec_path);
os::fork_and_exec(exec_path);
}

void MetaspaceShared::cmd_dump_dynamic(outputStream* output, const char* file, TRAPS) {
if (DynamicArchive::has_been_dumped_once()) {
output->print_cr("Dynamic dump has been done, and should only be done once.");
return;
} else {
// prevent multiple dumps.
DynamicArchive::set_has_been_dumped_once();
}
assert(UseSharedSpaces && RecordDynamicDumpInfo, "Sanity check");
const char* tmp_file = ArchiveClassesAtExit;
ArchiveClassesAtExit = file;
if (Arguments::init_shared_archive_paths()) {
DynamicArchive::dump();
} else {
output->print_cr("Could not setup SharedDynamicArchivePath!");
}
ArchiveClassesAtExit = tmp_file;
Arguments::init_shared_archive_paths();
}

#if INCLUDE_CDS_JAVA_HEAP
void VM_PopulateDumpSharedSpace::dump_java_heap_objects(GrowableArray<Klass*>* klasses) {
if(!HeapShared::is_heap_object_archiving_allowed()) {
@@ -83,11 +83,6 @@ class MetaspaceShared : AllStatic {
static int preload_classes(const char * class_list_path,
TRAPS) NOT_CDS_RETURN_(0);

// those cmd_ functions used for dump cds for jcmd.
static void cmd_dump_shared_archive(outputStream* output, const char* cmd, const char* filename, TRAPS) NOT_CDS_RETURN;
static void cmd_dump_static(outputStream* output, const char* filename, TRAPS) NOT_CDS_RETURN;
static void cmd_dump_dynamic(outputStream* output, const char* filename, TRAPS) NOT_CDS_RETURN;

static Symbol* symbol_rs_base() {
return (Symbol*)_symbol_rs.base();
}
@@ -30,6 +30,7 @@
#include "classfile/classLoader.hpp"
#include "classfile/classLoaderData.hpp"
#include "classfile/classLoaderData.inline.hpp"
#include "classfile/classLoaderDataGraph.hpp"
#include "classfile/classLoadInfo.hpp"
#include "classfile/javaAssertions.hpp"
#include "classfile/javaClasses.inline.hpp"
@@ -3652,12 +3653,27 @@ JVM_ENTRY(jclass, JVM_LookupLambdaProxyClassFromArchive(JNIEnv* env,
#endif // INCLUDE_CDS
JVM_END

JVM_ENTRY(jobjectArray, JVM_GetVMArguments(JNIEnv* env))
int num_vm_args = Arguments::num_jvm_args();
if (num_vm_args == 0) {
return NULL;
}

char** vm_args = Arguments::jvm_args_array();
objArrayHandle h_args = oopFactory::new_objArray_handle(vmClasses::String_klass(), num_vm_args, CHECK_NULL);
for(int i = 0; i < num_vm_args; i++) {
Handle h = java_lang_String::create_from_str(vm_args[i], THREAD);
h_args->obj_at_put(i, h());
}
return (jobjectArray) JNIHandles::make_local(THREAD, h_args());
JVM_END

JVM_ENTRY(jboolean, JVM_IsCDSDumpingEnabled(JNIEnv* env))
return Arguments::is_dumping_archive();
return Arguments::is_dumping_archive();
JVM_END

JVM_ENTRY(jboolean, JVM_IsSharingEnabled(JNIEnv* env))
return UseSharedSpaces;
return UseSharedSpaces;
JVM_END

JVM_ENTRY_NO_ENV(jlong, JVM_GetRandomSeedForDumping())
@@ -3703,6 +3719,69 @@ JVM_ENTRY(void, JVM_LogLambdaFormInvoker(JNIEnv *env, jstring line))
#endif // INCLUDE_CDS
JVM_END

#if INCLUDE_CDS
class DumpClassListCLDClosure : public CLDClosure {
fileStream *_stream;
public:
DumpClassListCLDClosure(fileStream* f) : CLDClosure() { _stream = f; }
void do_cld(ClassLoaderData* cld) {
for (Klass* klass = cld->klasses(); klass != NULL; klass = klass->next_link()) {
if (klass->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(klass);
if (ik->is_shareable()) {
_stream->print_cr("%s", ik->name()->as_C_string());
}
}
}
}
};
#endif

JVM_ENTRY(void, JVM_DumpClassListToFile(JNIEnv *env, jstring listFileName))
#if INCLUDE_CDS
ResourceMark rm(THREAD);
Handle file_handle(THREAD, JNIHandles::resolve_non_null(listFileName));
char* file_name = java_lang_String::as_utf8_string(file_handle());
fileStream stream(file_name, "w");
if (stream.is_open()) {
MutexLocker lock(ClassLoaderDataGraph_lock);
DumpClassListCLDClosure collect_classes(&stream);
ClassLoaderDataGraph::loaded_cld_do(&collect_classes);
} else {
THROW_MSG(vmSymbols::java_io_IOException(), "Failed to open file");
}
#endif // INCLUDE_CDS
JVM_END

JVM_ENTRY(void, JVM_DumpDynamicArchive(JNIEnv *env, jstring archiveName))
#if INCLUDE_CDS
assert(UseSharedSpaces && RecordDynamicDumpInfo, "Sanity check");
This conversation was marked as resolved by yminqi

This comment has been minimized.

@iklam

iklam Mar 19, 2021
Member

I think the message should say why this is true.

How about assert(UseSharedSpaces && RecordDynamicDumpInfo, "already checked in arguments.cpp");?

(same for the next assert statement at line 3774)

if (DynamicArchive::has_been_dumped_once()) {

This comment has been minimized.

@iklam

iklam Mar 31, 2021
Member

Maybe add a comment like this:?

// During dynamic archive dumping, some of the data structures are overwritten so
// we cannot dump the dynamic archive again. TODO: this should be fixed.
THROW_MSG(vmSymbols::java_lang_RuntimeException(),
"Dynamic dump has been done, and should only be done once");
} else {
// prevent multiple dumps.
DynamicArchive::set_has_been_dumped_once();
}
assert(ArchiveClassesAtExit == nullptr, "Sanity check");
Handle file_handle(THREAD, JNIHandles::resolve_non_null(archiveName));
char* archive_name = java_lang_String::as_utf8_string(file_handle());
This conversation was marked as resolved by yminqi

This comment has been minimized.

@iklam

iklam Mar 31, 2021
Member

A ResourceMark is needed before calling java_lang_String::as_utf8_string().

In general, I think the code in jvm.cpp should only marshall the jobject argument (e.g., convert jstring to char*.). The main functionality of JVM_DumpDynamicArchive should be moved to dynamicArchive.cpp. Similarly, most of the work in JVM_DumpClassListToFile should be moved to metaspaceShared.cpp.

ArchiveClassesAtExit = archive_name;
if (Arguments::init_shared_archive_paths()) {
DynamicArchive::dump();
} else {
THROW_MSG(vmSymbols::java_lang_RuntimeException(),

This comment has been minimized.

@iklam

iklam Mar 31, 2021
Member

Need to set ArchiveClassesAtExit to NULL before throwing the exception, since dynamic dump may not work anymore after the failure.

"Could not setup SharedDynamicArchivePath");
}
// prevent do dynamic dump at exit.
ArchiveClassesAtExit = nullptr;
if (!Arguments::init_shared_archive_paths()) {
THROW_MSG(vmSymbols::java_lang_RuntimeException(),
"Could not restore SharedDynamicArchivePath");
}
#endif // INCLUDE_CDS
JVM_END

// Returns an array of all live Thread objects (VM internal JavaThreads,
// jvmti agent threads, and JNI attaching threads are skipped)
// See CR 6404306 regarding JNI attaching threads
@@ -1893,7 +1893,7 @@ const intx ObjectAlignmentInBytes = 8;
"Dynamic archive") \
\
product(bool, RecordDynamicDumpInfo, false, \
"Record class info for jcmd Dynamic dump") \
"Record class info for jcmd VM.cds dynamic_dump") \
\
product(bool, PrintSharedArchiveAndExit, false, \
"Print shared archive file contents") \
ProTip! Use n and p to navigate between commits in a pull request.