8259070: Add jcmd option to dump CDS

Reviewed-by: ccheung, iklam, mli
This commit is contained in:
Yumin Qi 2021-04-15 05:21:24 +00:00
parent 593194864a
commit e7cbeba866
23 changed files with 800 additions and 75 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2021, 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
@ -25,7 +25,15 @@
package jdk.internal.misc;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
@ -63,6 +71,7 @@ public class CDS {
public static boolean isSharingEnabled() {
return isSharingEnabled;
}
private static native boolean isDumpingClassList0();
private static native boolean isDumpingArchive0();
private static native boolean isSharingEnabled0();
@ -195,4 +204,123 @@ public class CDS {
};
return retArray;
}
private static native void dumpClassList(String listFileName);
private static native void dumpDynamicArchive(String archiveFileName);
private static String drainOutput(InputStream stream, long pid, String tail, List<String> cmds) {
String fileName = "java_pid" + pid + "_" + tail;
new Thread( ()-> {
try (InputStreamReader isr = new InputStreamReader(stream);
BufferedReader rdr = new BufferedReader(isr);
PrintStream prt = new PrintStream(fileName)) {
prt.println("Command:");
for (String s : cmds) {
prt.print(s + " ");
}
prt.println("");
String line;
while((line = rdr.readLine()) != null) {
prt.println(line);
}
} catch (IOException e) {
throw new RuntimeException("IOExeption happens during drain stream to file " +
fileName + ": " + e.getMessage());
}}).start();
return fileName;
}
private static String[] excludeFlags = {
"-XX:DumpLoadedClassList=",
"-XX:+DumpSharedSpaces",
"-XX:+DynamicDumpSharedSpaces",
"-XX:+RecordDynamicDumpInfo",
"-Xshare:",
"-XX:SharedClassListFile=",
"-XX:SharedArchiveFile=",
"-XX:ArchiveClassesAtExit=",
"-XX:+UseSharedSpaces",
"-XX:+RequireSharedSpaces"};
private static boolean containsExcludedFlags(String testStr) {
for (String e : excludeFlags) {
if (testStr.contains(e)) {
return true;
}
}
return false;
}
/**
* called from jcmd VM.cds to dump static or dynamic shared archive
* @param isStatic true for dump static archive or false for dynnamic archive.
* @param fileName user input archive name, can be null.
*/
private static void dumpSharedArchive(boolean isStatic, String fileName) throws Exception {
String currentPid = String.valueOf(ProcessHandle.current().pid());
String archiveFile = fileName != null ? fileName :
"java_pid" + currentPid + (isStatic ? "_static.jsa" : "_dynamic.jsa");
// delete if archive file aready exists
File fileArchive = new File(archiveFile);
if (fileArchive.exists()) {
fileArchive.delete();
}
if (isStatic) {
String listFile = archiveFile + ".classlist";
File fileList = new File(listFile);
if (fileList.exists()) {
fileList.delete();
}
dumpClassList(listFile);
String jdkHome = System.getProperty("java.home");
String classPath = System.getProperty("java.class.path");
List<String> cmds = new ArrayList<String>();
cmds.add(jdkHome + File.separator + "bin" + File.separator + "java"); // java
cmds.add("-cp");
cmds.add(classPath);
cmds.add("-Xlog:cds");
cmds.add("-Xshare:dump");
cmds.add("-XX:SharedClassListFile=" + listFile);
cmds.add("-XX:SharedArchiveFile=" + archiveFile);
// All runtime args.
String[] vmArgs = VM.getRuntimeArguments();
if (vmArgs != null) {
for (String arg : vmArgs) {
if (arg != null && !containsExcludedFlags(arg)) {
cmds.add(arg);
}
}
}
Process proc = Runtime.getRuntime().exec(cmds.toArray(new String[0]));
// Drain stdout/stderr to files in new threads.
String stdOutFile = drainOutput(proc.getInputStream(), proc.pid(), "stdout", cmds);
String stdErrFile = drainOutput(proc.getErrorStream(), proc.pid(), "stderr", cmds);
proc.waitFor();
// done, delete classlist file.
if (fileList.exists()) {
fileList.delete();
}
// Check if archive has been successfully dumped. We won't reach here if exception happens.
// Throw exception if file is not created.
if (!fileArchive.exists()) {
throw new RuntimeException("Archive file " + archiveFile +
" is not created, please check stdout file " +
stdOutFile + " or stderr file " +
stdErrFile + " for more detail");
}
} else {
dumpDynamicArchive(archiveFile);
if (!fileArchive.exists()) {
throw new RuntimeException("Archive file " + archiveFile +
" is not created, please check process " +
currentPid + " output for more detail");
}
}
// Everyting goes well, print out the file name.
System.out.println((isStatic ? "Static" : " Dynamic") + " dump to file " + archiveFile);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2021, 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
@ -63,3 +63,13 @@ JNIEXPORT void JNICALL
Java_jdk_internal_misc_CDS_logLambdaFormInvoker(JNIEnv *env, jclass jcls, jstring line) {
JVM_LogLambdaFormInvoker(env, line);
}
JNIEXPORT void JNICALL
Java_jdk_internal_misc_CDS_dumpClassList(JNIEnv *env, jclass jcls, jstring fileName) {
JVM_DumpClassListToFile(env, fileName);
}
JNIEXPORT void JNICALL
Java_jdk_internal_misc_CDS_dumpDynamicArchive(JNIEnv *env, jclass jcls, jstring archiveName) {
JVM_DumpDynamicArchive(env, archiveName);
}