8256450: Add gz option to jmap to write a gzipped heap dump

Reviewed-by: cjplummer, sspitsyn, phh
This commit is contained in:
Lin Zang 2020-11-25 16:51:42 +00:00 committed by Paul Hohensee
parent dee79d6053
commit 461c5fc637
3 changed files with 56 additions and 9 deletions

View File

@ -218,6 +218,7 @@ static jint jcmd(AttachOperation* op, outputStream* out) {
// Input arguments :- // Input arguments :-
// arg0: Name of the dump file // arg0: Name of the dump file
// arg1: "-live" or "-all" // arg1: "-live" or "-all"
// arg2: Compress level
jint dump_heap(AttachOperation* op, outputStream* out) { jint dump_heap(AttachOperation* op, outputStream* out) {
const char* path = op->arg(0); const char* path = op->arg(0);
if (path == NULL || path[0] == '\0') { if (path == NULL || path[0] == '\0') {
@ -233,11 +234,22 @@ jint dump_heap(AttachOperation* op, outputStream* out) {
live_objects_only = strcmp(arg1, "-live") == 0; live_objects_only = strcmp(arg1, "-live") == 0;
} }
const char* num_str = op->arg(2);
uintx level = 0;
if (num_str != NULL && num_str[0] != '\0') {
if (!Arguments::parse_uintx(num_str, &level, 0)) {
out->print_cr("Invalid compress level: [%s]", num_str);
return JNI_ERR;
} else if (level < 1 || level > 9) {
out->print_cr("Compression level out of range (1-9): " UINTX_FORMAT, level);
return JNI_ERR;
}
}
// Request a full GC before heap dump if live_objects_only = true // Request a full GC before heap dump if live_objects_only = true
// This helps reduces the amount of unreachable objects in the dump // This helps reduces the amount of unreachable objects in the dump
// and makes it easier to browse. // and makes it easier to browse.
HeapDumper dumper(live_objects_only /* request GC */); HeapDumper dumper(live_objects_only /* request GC */);
dumper.dump(op->arg(0), out); dumper.dump(op->arg(0), out, (int)level);
} }
return JNI_OK; return JNI_OK;
} }

View File

@ -209,6 +209,7 @@ public class JMap {
String subopts[] = options.split(","); String subopts[] = options.split(",");
String filename = null; String filename = null;
String liveopt = "-all"; String liveopt = "-all";
String compress_level = null;
for (int i = 0; i < subopts.length; i++) { for (int i = 0; i < subopts.length; i++) {
String subopt = subopts[i]; String subopt = subopts[i];
@ -224,6 +225,12 @@ public class JMap {
} }
} else if (subopt.equals("format=b")) { } else if (subopt.equals("format=b")) {
// ignore format (not needed at this time) // ignore format (not needed at this time)
} else if (subopt.startsWith("gz=")) {
compress_level = subopt.substring("gz=".length());
if (compress_level == null) {
System.err.println("Fail: no number provided in option: '" + subopt + "'");
usage(1);
}
} else { } else {
System.err.println("Fail: invalid option: '" + subopt + "'"); System.err.println("Fail: invalid option: '" + subopt + "'");
usage(1); usage(1);
@ -238,7 +245,7 @@ public class JMap {
System.out.flush(); System.out.flush();
// dumpHeap is not the same as jcmd GC.heap_dump // dumpHeap is not the same as jcmd GC.heap_dump
executeCommandForPid(pid, "dumpheap", filename, liveopt); executeCommandForPid(pid, "dumpheap", filename, liveopt, compress_level);
} }
private static void checkForUnsupportedOptions(String[] args) { private static void checkForUnsupportedOptions(String[] args) {
@ -303,6 +310,8 @@ public class JMap {
System.err.println(" all dump all objects in the heap (default if one of \"live\" or \"all\" is not specified)"); System.err.println(" all dump all objects in the heap (default if one of \"live\" or \"all\" is not specified)");
System.err.println(" format=b binary format"); System.err.println(" format=b binary format");
System.err.println(" file=<file> dump heap to <file>"); System.err.println(" file=<file> dump heap to <file>");
System.err.println(" gz=<number> If specified, the heap dump is written in gzipped format using the given compression level.");
System.err.println(" 1 (recommended) is the fastest, 9 the strongest compression.");
System.err.println(""); System.err.println("");
System.err.println(" Example: jmap -dump:live,format=b,file=heap.bin <pid>"); System.err.println(" Example: jmap -dump:live,format=b,file=heap.bin <pid>");
System.err.println(""); System.err.println("");

View File

@ -22,10 +22,13 @@
*/ */
import static jdk.test.lib.Asserts.assertTrue; import static jdk.test.lib.Asserts.assertTrue;
import static jdk.test.lib.Asserts.assertFalse;
import static jdk.test.lib.Asserts.fail; import static jdk.test.lib.Asserts.fail;
import java.io.File; import java.io.File;
import java.nio.file.Files;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.JDKToolLauncher;
import jdk.test.lib.Utils; import jdk.test.lib.Utils;
@ -114,6 +117,7 @@ public class BasicJMapTest {
testDump(); testDump();
testDumpLive(); testDumpLive();
testDumpAll(); testDumpAll();
testDumpCompressed();
} }
private static void testHisto() throws Exception { private static void testHisto() throws Exception {
@ -211,20 +215,25 @@ public class BasicJMapTest {
} }
private static void testDump() throws Exception { private static void testDump() throws Exception {
dump(false, false); dump(false, false, false);
} }
private static void testDumpLive() throws Exception { private static void testDumpLive() throws Exception {
dump(true, false); dump(true, false, false);
} }
private static void testDumpAll() throws Exception { private static void testDumpAll() throws Exception {
dump(false, true); dump(false, true, false);
} }
private static void dump(boolean live, boolean explicitAll) throws Exception { private static void testDumpCompressed() throws Exception {
dump(true, false, true);
}
private static void dump(boolean live, boolean explicitAll, boolean compressed) throws Exception {
String liveArg = ""; String liveArg = "";
String fileArg = ""; String fileArg = "";
String compressArg = "";
String allArgs = "-dump:"; String allArgs = "-dump:";
if (live && explicitAll) { if (live && explicitAll) {
@ -237,14 +246,20 @@ public class BasicJMapTest {
liveArg = "all,"; liveArg = "all,";
} }
File file = new File("jmap.dump" + System.currentTimeMillis() + ".hprof"); String filePath = "jmap.dump" + System.currentTimeMillis() + ".hprof";
if (compressed) {
compressArg = "gz=1,";
filePath = filePath + ".gz";
}
File file = new File(filePath);
if (file.exists()) { if (file.exists()) {
file.delete(); file.delete();
} }
fileArg = "file=" + file.getName(); fileArg = "file=" + file.getName();
OutputAnalyzer output; OutputAnalyzer output;
allArgs = allArgs + liveArg + "format=b," + fileArg; allArgs = allArgs + liveArg + compressArg + "format=b," + fileArg;
output = jmap(allArgs); output = jmap(allArgs);
output.shouldHaveExitValue(0); output.shouldHaveExitValue(0);
output.shouldContain("Heap dump file created"); output.shouldContain("Heap dump file created");
@ -255,7 +270,18 @@ public class BasicJMapTest {
private static void verifyDumpFile(File dump) { private static void verifyDumpFile(File dump) {
assertTrue(dump.exists() && dump.isFile(), "Could not create dump file " + dump.getAbsolutePath()); assertTrue(dump.exists() && dump.isFile(), "Could not create dump file " + dump.getAbsolutePath());
try { try {
HprofParser.parse(dump); File out = HprofParser.parse(dump);
assertTrue(out != null && out.exists() && out.isFile(),
"Could not find hprof parser output file");
List<String> lines = Files.readAllLines(out.toPath());
assertTrue(lines.size() > 0, "hprof parser output file is empty");
for (String line : lines) {
assertFalse(line.matches(".*WARNING(?!.*Failed to resolve " +
"object.*constantPoolOop.*).*"));
}
out.delete();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
fail("Could not parse dump file " + dump.getAbsolutePath()); fail("Could not parse dump file " + dump.getAbsolutePath());