8257234: Add gz option to SA jmap to write a gzipped heap dump
Reviewed-by: cjplummer, ysuenaga, sspitsyn
This commit is contained in:
parent
aa35b42354
commit
c54724da14
@ -163,7 +163,6 @@ public class CommandProcessor {
|
||||
|
||||
Tokens(String cmd) {
|
||||
input = cmd;
|
||||
|
||||
// check for quoting
|
||||
int quote = cmd.indexOf('"');
|
||||
ArrayList<String> t = new ArrayList<>();
|
||||
@ -1787,25 +1786,60 @@ public class CommandProcessor {
|
||||
sysProps.run();
|
||||
}
|
||||
},
|
||||
new Command("dumpheap", "dumpheap [filename]", false) {
|
||||
new Command("dumpheap", "dumpheap [gz=<1-9>] [filename]", false) {
|
||||
public void doit(Tokens t) {
|
||||
if (t.countTokens() > 1) {
|
||||
int cntTokens = t.countTokens();
|
||||
if (cntTokens > 2) {
|
||||
err.println("More than 2 options specified: " + cntTokens);
|
||||
usage();
|
||||
} else {
|
||||
JMap jmap = new JMap();
|
||||
String filename;
|
||||
if (t.countTokens() == 1) {
|
||||
filename = t.nextToken();
|
||||
return;
|
||||
}
|
||||
JMap jmap = new JMap();
|
||||
String filename = "heap.bin";
|
||||
int gzlevel = 0;
|
||||
/*
|
||||
* Possible command:
|
||||
* dumpheap gz=1 file;
|
||||
* dumpheap gz=1;
|
||||
* dumpheap file;
|
||||
* dumpheap
|
||||
*
|
||||
* Use default filename if cntTokens == 0.
|
||||
* Handle cases with cntTokens == 1 or 2.
|
||||
*/
|
||||
if (cntTokens == 1) { // first argument could be filename or "gz="
|
||||
String option = t.nextToken();
|
||||
if (!option.startsWith("gz=")) {
|
||||
filename = option;
|
||||
} else {
|
||||
filename = "heap.bin";;
|
||||
}
|
||||
try {
|
||||
jmap.writeHeapHprofBin(filename);
|
||||
} catch (Exception e) {
|
||||
err.println("Error: " + e);
|
||||
if (verboseExceptions) {
|
||||
e.printStackTrace(err);
|
||||
gzlevel = parseHeapDumpCompressionLevel(option);
|
||||
if (gzlevel == 0) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
filename = "heap.bin.gz";
|
||||
}
|
||||
}
|
||||
if (cntTokens == 2) { // first argument is "gz=" followed by filename
|
||||
String option = t.nextToken();
|
||||
gzlevel = parseHeapDumpCompressionLevel(option);
|
||||
if (gzlevel == 0) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
filename = t.nextToken();
|
||||
if (filename.startsWith("gz=")) {
|
||||
err.println("Filename should not start with \"gz=\": " + filename);
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
jmap.writeHeapHprofBin(filename, gzlevel);
|
||||
} catch (Exception e) {
|
||||
err.println("Error: " + e);
|
||||
if (verboseExceptions) {
|
||||
e.printStackTrace(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2080,4 +2114,34 @@ public class CommandProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse compression level
|
||||
* @return 1-9 compression level
|
||||
* 0 compression level is illegal
|
||||
*/
|
||||
private int parseHeapDumpCompressionLevel(String option) {
|
||||
|
||||
String[] keyValue = option.split("=");
|
||||
if (!keyValue[0].equals("gz")) {
|
||||
err.println("Expected option is \"gz=\"");
|
||||
return 0;
|
||||
}
|
||||
if (keyValue.length != 2) {
|
||||
err.println("Exactly one argument is expected for option \"gz\"");
|
||||
return 0;
|
||||
}
|
||||
int gzl = 0;
|
||||
String level = keyValue[1];
|
||||
try {
|
||||
gzl = Integer.parseInt(level);
|
||||
} catch (NumberFormatException e) {
|
||||
err.println("gz option value not an integer ("+level+")");
|
||||
return 0;
|
||||
}
|
||||
if (gzl < 1 || gzl > 9) {
|
||||
err.println("Compression level out of range (1-9): " + level);
|
||||
return 0;
|
||||
}
|
||||
return gzl;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 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
|
||||
@ -126,7 +126,8 @@ public class SALauncher {
|
||||
System.out.println(" <no option> To print same info as Solaris pmap.");
|
||||
System.out.println(" --heap To print java heap summary.");
|
||||
System.out.println(" --binaryheap To dump java heap in hprof binary format.");
|
||||
System.out.println(" --dumpfile <name> The name of the dump file.");
|
||||
System.out.println(" --dumpfile <name> The name of the dump file. Only valid with --binaryheap.");
|
||||
System.out.println(" --gz <1-9> The compression level for gzipped dump file. Only valid with --binaryheap.");
|
||||
System.out.println(" --histo To print histogram of java object heap.");
|
||||
System.out.println(" --clstats To print class loader statistics.");
|
||||
System.out.println(" --finalizerinfo To print information on objects awaiting finalization.");
|
||||
@ -301,33 +302,40 @@ public class SALauncher {
|
||||
}
|
||||
|
||||
private static void runJMAP(String[] oldArgs) {
|
||||
Map<String, String> longOptsMap = Map.of("exe=", "exe",
|
||||
"core=", "core",
|
||||
"pid=", "pid",
|
||||
"connect=", "connect",
|
||||
"heap", "-heap",
|
||||
"binaryheap", "binaryheap",
|
||||
"dumpfile=", "dumpfile",
|
||||
"histo", "-histo",
|
||||
"clstats", "-clstats",
|
||||
"finalizerinfo", "-finalizerinfo");
|
||||
Map<String, String> longOptsMap = Map.ofEntries(
|
||||
Map.entry("exe=", "exe"),
|
||||
Map.entry("core=", "core"),
|
||||
Map.entry("pid=", "pid"),
|
||||
Map.entry("connect=", "connect"),
|
||||
Map.entry("heap", "-heap"),
|
||||
Map.entry("binaryheap", "binaryheap"),
|
||||
Map.entry("dumpfile=", "dumpfile"),
|
||||
Map.entry("gz=", "gz"),
|
||||
Map.entry("histo", "-histo"),
|
||||
Map.entry("clstats", "-clstats"),
|
||||
Map.entry("finalizerinfo", "-finalizerinfo"));
|
||||
Map<String, String> newArgMap = parseOptions(oldArgs, longOptsMap);
|
||||
|
||||
boolean requestHeapdump = newArgMap.containsKey("binaryheap");
|
||||
String dumpfile = newArgMap.get("dumpfile");
|
||||
String gzLevel = newArgMap.get("gz");
|
||||
String command = "-heap:format=b";
|
||||
if (!requestHeapdump && (dumpfile != null)) {
|
||||
throw new IllegalArgumentException("Unexpected argument: dumpfile");
|
||||
}
|
||||
if (requestHeapdump) {
|
||||
if (dumpfile == null) {
|
||||
newArgMap.put("-heap:format=b", null);
|
||||
} else {
|
||||
newArgMap.put("-heap:format=b,file=" + dumpfile, null);
|
||||
if (gzLevel != null) {
|
||||
command += ",gz=" + gzLevel;
|
||||
}
|
||||
if (dumpfile != null) {
|
||||
command += ",file=" + dumpfile;
|
||||
}
|
||||
newArgMap.put(command, null);
|
||||
}
|
||||
|
||||
newArgMap.remove("binaryheap");
|
||||
newArgMap.remove("dumpfile");
|
||||
newArgMap.remove("gz");
|
||||
JMap.main(buildAttachArgs(newArgMap, false));
|
||||
}
|
||||
|
||||
@ -485,6 +493,8 @@ public class SALauncher {
|
||||
} catch (SAGetoptException e) {
|
||||
System.err.println(e.getMessage());
|
||||
toolHelp(args[0]);
|
||||
// Exit with error status
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2004, 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
|
||||
@ -50,16 +50,21 @@ public class JMap extends Tool {
|
||||
}
|
||||
|
||||
protected String getCommandFlags() {
|
||||
return "-heap|-heap:format=b|-histo|-clstats|-finalizerinfo";
|
||||
return "-heap|-heap:format=b[,gz=<1-9>][,file=<dumpfile>]|-heap:format=x[,file=<dumpfile>]|{-histo|-clstats|-finalizerinfo";
|
||||
}
|
||||
|
||||
protected void printFlagsUsage() {
|
||||
System.out.println(" <no option>\tto print same info as Solaris pmap");
|
||||
System.out.println(" -heap\tto print java heap summary");
|
||||
System.out.println(" -heap:format=b\tto dump java heap in hprof binary format");
|
||||
System.out.println(" -histo\tto print histogram of java object heap");
|
||||
System.out.println(" -clstats\tto print class loader statistics");
|
||||
System.out.println(" -finalizerinfo\tto print information on objects awaiting finalization");
|
||||
System.out.println(" <no option>\tTo print same info as Solaris pmap.");
|
||||
System.out.println(" -heap\tTo print java heap summary.");
|
||||
System.out.println(" -heap:format=b[,gz=<1-9>][,file=<dumpfile>] \tTo dump java heap in hprof binary format.");
|
||||
System.out.println(" \tIf gz specified, the heap dump is written in gzipped format");
|
||||
System.out.println(" \tusing the given compression level.");
|
||||
System.err.println(" \t1 (recommended) is the fastest, 9 the strongest compression.");
|
||||
System.out.println(" -heap:format=x[,file=<dumpfile>] \tTo dump java heap in GXL format.");
|
||||
System.out.println(" \tPlease be aware that \"gz\" option is not valid for heap dump in GXL format.");
|
||||
System.out.println(" -histo\tTo print histogram of java object heap.");
|
||||
System.out.println(" -clstats\tTo print class loader statistics.");
|
||||
System.out.println(" -finalizerinfo\tTo print information on objects awaiting finalization.");
|
||||
super.printFlagsUsage();
|
||||
}
|
||||
|
||||
@ -72,6 +77,7 @@ public class JMap extends Tool {
|
||||
public static final int MODE_FINALIZERINFO = 6;
|
||||
|
||||
private static String dumpfile = "heap.bin";
|
||||
private static int gzLevel = 0;
|
||||
|
||||
public void run() {
|
||||
Tool tool = null;
|
||||
@ -94,7 +100,7 @@ public class JMap extends Tool {
|
||||
break;
|
||||
|
||||
case MODE_HEAP_GRAPH_HPROF_BIN:
|
||||
writeHeapHprofBin(dumpfile);
|
||||
writeHeapHprofBin(dumpfile, gzLevel);
|
||||
return;
|
||||
|
||||
case MODE_HEAP_GRAPH_GXL:
|
||||
@ -151,6 +157,26 @@ public class JMap extends Tool {
|
||||
System.exit(1);
|
||||
}
|
||||
dumpfile = keyValue[1];
|
||||
} else if (keyValue[0].equals("gz")) {
|
||||
if (mode == MODE_HEAP_GRAPH_GXL) {
|
||||
System.err.println("\"gz\" option is not compatible with heap dump in GXL format");
|
||||
System.exit(1);
|
||||
}
|
||||
if (keyValue.length == 1) {
|
||||
System.err.println("Argument is expected for \"gz\"");
|
||||
System.exit(1);
|
||||
}
|
||||
String level = keyValue[1];
|
||||
try {
|
||||
gzLevel = Integer.parseInt(level);
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println("\"gz\" option value not an integer ("+level+")");
|
||||
System.exit(1);
|
||||
}
|
||||
if (gzLevel < 1 || gzLevel > 9) {
|
||||
System.err.println("compression level out of range (1-9): " + level);
|
||||
System.exit(1);
|
||||
}
|
||||
} else {
|
||||
System.err.println("unknown option:" + keyValue[0]);
|
||||
|
||||
@ -176,9 +202,17 @@ public class JMap extends Tool {
|
||||
jmap.execute(args);
|
||||
}
|
||||
|
||||
public boolean writeHeapHprofBin(String fileName) {
|
||||
public boolean writeHeapHprofBin(String fileName, int gzLevel) {
|
||||
try {
|
||||
HeapGraphWriter hgw = new HeapHprofBinWriter();
|
||||
HeapGraphWriter hgw;
|
||||
if (gzLevel == 0) {
|
||||
hgw = new HeapHprofBinWriter();
|
||||
} else if (gzLevel >=1 && gzLevel <= 9) {
|
||||
hgw = new HeapHprofBinWriter(gzLevel);
|
||||
} else {
|
||||
System.err.println("Illegal compression level: " + gzLevel);
|
||||
return false;
|
||||
}
|
||||
hgw.write(fileName);
|
||||
System.out.println("heap written to " + fileName);
|
||||
return true;
|
||||
@ -188,7 +222,7 @@ public class JMap extends Tool {
|
||||
}
|
||||
|
||||
public boolean writeHeapHprofBin() {
|
||||
return writeHeapHprofBin("heap.bin");
|
||||
return writeHeapHprofBin("heap.bin", -1);
|
||||
}
|
||||
|
||||
private boolean writeHeapGXL(String fileName) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2004, 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,8 +25,11 @@
|
||||
package sun.jvm.hotspot.utilities;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.zip.*;
|
||||
import sun.jvm.hotspot.debugger.*;
|
||||
import sun.jvm.hotspot.memory.*;
|
||||
import sun.jvm.hotspot.oops.*;
|
||||
@ -386,15 +389,39 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
||||
public HeapHprofBinWriter() {
|
||||
this.KlassMap = new ArrayList<Klass>();
|
||||
this.names = new HashSet<Symbol>();
|
||||
this.gzLevel = 0;
|
||||
}
|
||||
|
||||
public HeapHprofBinWriter(int gzLevel) {
|
||||
this.KlassMap = new ArrayList<Klass>();
|
||||
this.names = new HashSet<Symbol>();
|
||||
this.gzLevel = gzLevel;
|
||||
}
|
||||
|
||||
public synchronized void write(String fileName) throws IOException {
|
||||
VM vm = VM.getVM();
|
||||
|
||||
// Check whether we should dump the heap as segments
|
||||
useSegmentedHeapDump = isCompression() ||
|
||||
(vm.getUniverse().heap().used() > HPROF_SEGMENTED_HEAP_DUMP_THRESHOLD);
|
||||
|
||||
// open file stream and create buffered data output stream
|
||||
fos = new FileOutputStream(fileName);
|
||||
out = new DataOutputStream(new BufferedOutputStream(fos));
|
||||
|
||||
hprofBufferedOut = null;
|
||||
OutputStream dataOut = fos;
|
||||
if (useSegmentedHeapDump) {
|
||||
if (isCompression()) {
|
||||
dataOut = new GZIPOutputStream(fos) {
|
||||
{
|
||||
this.def.setLevel(gzLevel);
|
||||
}
|
||||
};
|
||||
}
|
||||
hprofBufferedOut = new SegmentedOutputStream(dataOut);
|
||||
} else {
|
||||
hprofBufferedOut = new SegmentedOutputStream(fos, false /* allowSegmented */);
|
||||
}
|
||||
out = new DataOutputStream(hprofBufferedOut);
|
||||
dbg = vm.getDebugger();
|
||||
objectHeap = vm.getObjectHeap();
|
||||
|
||||
@ -419,9 +446,6 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
||||
FLOAT_SIZE = objectHeap.getFloatSize();
|
||||
DOUBLE_SIZE = objectHeap.getDoubleSize();
|
||||
|
||||
// Check weather we should dump the heap as segments
|
||||
useSegmentedHeapDump = vm.getUniverse().heap().used() > HPROF_SEGMENTED_HEAP_DUMP_THRESHOLD;
|
||||
|
||||
// hprof bin format header
|
||||
writeFileHeader();
|
||||
|
||||
@ -447,10 +471,11 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
||||
// flush buffer stream.
|
||||
out.flush();
|
||||
|
||||
// Fill in final length
|
||||
fillInHeapRecordLength();
|
||||
|
||||
if (useSegmentedHeapDump) {
|
||||
if (!useSegmentedHeapDump) {
|
||||
// Fill in final length.
|
||||
fillInHeapRecordLength();
|
||||
} else {
|
||||
hprofBufferedOut.finish();
|
||||
// Write heap segment-end record
|
||||
out.writeByte((byte) HPROF_HEAP_DUMP_END);
|
||||
out.writeInt(0);
|
||||
@ -459,19 +484,18 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
||||
|
||||
// flush buffer stream and throw it.
|
||||
out.flush();
|
||||
out.close();
|
||||
out = null;
|
||||
|
||||
// close the file stream
|
||||
fos.close();
|
||||
hprofBufferedOut = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeHeapRecordPrologue() throws IOException {
|
||||
if (currentSegmentStart == 0) {
|
||||
// write heap data header, depending on heap size use segmented heap
|
||||
// format
|
||||
out.writeByte((byte) (useSegmentedHeapDump ? HPROF_HEAP_DUMP_SEGMENT
|
||||
: HPROF_HEAP_DUMP));
|
||||
if (useSegmentedHeapDump) {
|
||||
hprofBufferedOut.enterSegmentMode();
|
||||
} else if (currentSegmentStart == 0) {
|
||||
// write heap data header
|
||||
out.writeByte((byte) (HPROF_HEAP_DUMP));
|
||||
out.writeInt(0);
|
||||
|
||||
// remember position of dump length, we will fixup
|
||||
@ -486,15 +510,12 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
||||
@Override
|
||||
protected void writeHeapRecordEpilogue() throws IOException {
|
||||
if (useSegmentedHeapDump) {
|
||||
out.flush();
|
||||
if ((fos.getChannel().position() - currentSegmentStart - 4L) >= HPROF_SEGMENTED_HEAP_DUMP_SEGMENT_SIZE) {
|
||||
fillInHeapRecordLength();
|
||||
currentSegmentStart = 0;
|
||||
}
|
||||
hprofBufferedOut.exitSegmentMode();
|
||||
}
|
||||
}
|
||||
|
||||
private void fillInHeapRecordLength() throws IOException {
|
||||
assert !useSegmentedHeapDump : "fillInHeapRecordLength is not supported for segmented heap dump";
|
||||
|
||||
// now get the current position to calculate length
|
||||
long dumpEnd = fos.getChannel().position();
|
||||
@ -513,13 +534,10 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
||||
// seek the position to write length
|
||||
fos.getChannel().position(currentSegmentStart);
|
||||
|
||||
// write length
|
||||
int dumpLen = (int) dumpLenLong;
|
||||
|
||||
// write length as integer
|
||||
fos.write((dumpLen >>> 24) & 0xFF);
|
||||
fos.write((dumpLen >>> 16) & 0xFF);
|
||||
fos.write((dumpLen >>> 8) & 0xFF);
|
||||
fos.write((dumpLen >>> 0) & 0xFF);
|
||||
byte[] lenBytes = genByteArrayFromInt(dumpLen);
|
||||
fos.write(lenBytes);
|
||||
|
||||
//Reset to previous current position
|
||||
fos.getChannel().position(currentPosition);
|
||||
@ -569,8 +587,10 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
||||
long originalLengthInBytes = originalArrayLength * typeSize;
|
||||
|
||||
// calculate the length of heap data
|
||||
// only process when segmented heap dump is not used, since SegmentedOutputStream
|
||||
// could create segment automatically.
|
||||
long currentRecordLength = (dumpEnd - currentSegmentStart - 4L);
|
||||
if (currentRecordLength > 0 &&
|
||||
if ((!useSegmentedHeapDump) && currentRecordLength > 0 &&
|
||||
(currentRecordLength + headerSize + originalLengthInBytes) > MAX_U4_VALUE) {
|
||||
fillInHeapRecordLength();
|
||||
currentSegmentStart = 0;
|
||||
@ -1226,6 +1246,18 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
||||
return size;
|
||||
}
|
||||
|
||||
private boolean isCompression() {
|
||||
return (gzLevel >= 1 && gzLevel <= 9);
|
||||
}
|
||||
|
||||
// Convert integer to byte array with BIG_ENDIAN byte order.
|
||||
private static byte[] genByteArrayFromInt(int value) {
|
||||
ByteBuffer intBuffer = ByteBuffer.allocate(4);
|
||||
intBuffer.order(ByteOrder.BIG_ENDIAN);
|
||||
intBuffer.putInt(value);
|
||||
return intBuffer.array();
|
||||
}
|
||||
|
||||
// We don't have allocation site info. We write a dummy
|
||||
// stack trace with this id.
|
||||
private static final int DUMMY_STACK_TRACE_ID = 1;
|
||||
@ -1233,9 +1265,11 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
||||
|
||||
private DataOutputStream out;
|
||||
private FileOutputStream fos;
|
||||
private SegmentedOutputStream hprofBufferedOut;
|
||||
private Debugger dbg;
|
||||
private ObjectHeap objectHeap;
|
||||
private ArrayList<Klass> KlassMap;
|
||||
private int gzLevel;
|
||||
|
||||
// oopSize of the debuggee
|
||||
private int OBJ_ID_SIZE;
|
||||
@ -1274,4 +1308,221 @@ public class HeapHprofBinWriter extends AbstractHeapGraphWriter {
|
||||
}
|
||||
|
||||
private Map<InstanceKlass, ClassData> classDataCache = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The class implements a buffered output stream for segmented data dump.
|
||||
* It is used inside HeapHprofBinWritter only for heap dump.
|
||||
* Because the current implementation of segmented heap dump needs to update
|
||||
* the segment size at segment header, and because it is hard to modify the
|
||||
* compressed data after they are written to file, this class first saves the
|
||||
* uncompressed data into an internal buffer, and then writes through to the
|
||||
* GZIPOutputStream when the whole segmented data are ready and the size is updated.
|
||||
* If the data to be written are larger than internal buffer, or the internal buffer
|
||||
* is full, the internal buffer will be extend to a larger one.
|
||||
* This class defines a switch to turn on/off the segmented mode. If turned off,
|
||||
* it behaves the same as BufferedOutputStream.
|
||||
* */
|
||||
private class SegmentedOutputStream extends BufferedOutputStream {
|
||||
/**
|
||||
* Creates a new buffered output stream to support segmented heap dump data.
|
||||
*
|
||||
* @param out the underlying output stream.
|
||||
* @param allowSegmented whether allow segmental dump.
|
||||
*/
|
||||
public SegmentedOutputStream(OutputStream out, boolean allowSegmented) {
|
||||
super(out, 8192);
|
||||
segmentMode = false;
|
||||
this.allowSegmented = allowSegmented;
|
||||
segmentBuffer = new byte[SEGMENT_BUFFER_SIZE];
|
||||
segmentWritten = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new buffered output stream to support segmented heap dump data.
|
||||
*
|
||||
* @param out the underlying output stream.
|
||||
*/
|
||||
public SegmentedOutputStream(OutputStream out) {
|
||||
this(out, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified byte to this buffered output stream.
|
||||
*
|
||||
* @param b the byte to be written.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void write(int b) throws IOException {
|
||||
if (segmentMode) {
|
||||
if (segmentWritten == 0) {
|
||||
// At the begining of the segment.
|
||||
writeSegmentHeader();
|
||||
} else if (segmentWritten == segmentBuffer.length) {
|
||||
// Internal buffer is full, extend a larger one.
|
||||
int newSize = segmentBuffer.length + SEGMENT_BUFFER_INC_SIZE;
|
||||
byte newBuf[] = new byte[newSize];
|
||||
System.arraycopy(segmentBuffer, 0, newBuf, 0, segmentWritten);
|
||||
segmentBuffer = newBuf;
|
||||
}
|
||||
segmentBuffer[segmentWritten++] = (byte)b;
|
||||
return;
|
||||
}
|
||||
super.write(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes {@code len} bytes from the specified byte array
|
||||
* starting at offset {@code off} to this output stream.
|
||||
*
|
||||
* @param b the data.
|
||||
* @param off the start offset in the data.
|
||||
* @param len the number of bytes to write.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void write(byte b[], int off, int len) throws IOException {
|
||||
if (segmentMode) {
|
||||
if (segmentWritten == 0) {
|
||||
writeSegmentHeader();
|
||||
}
|
||||
// Data size is larger than segment buffer length, extend segment buffer.
|
||||
if (segmentWritten + len > segmentBuffer.length) {
|
||||
int newSize = segmentBuffer.length + Math.max(SEGMENT_BUFFER_INC_SIZE, len);
|
||||
byte newBuf[] = new byte[newSize];
|
||||
System.arraycopy(segmentBuffer, 0, newBuf, 0, segmentWritten);
|
||||
segmentBuffer = newBuf;
|
||||
}
|
||||
System.arraycopy(b, off, segmentBuffer, segmentWritten, len);
|
||||
segmentWritten += len;
|
||||
return;
|
||||
}
|
||||
super.write(b, off, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes this buffered output stream. This forces any buffered
|
||||
* output bytes to be written out to the underlying output stream.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs.
|
||||
* @see java.io.FilterOutputStream#out
|
||||
*/
|
||||
@Override
|
||||
public synchronized void flush() throws IOException {
|
||||
if (segmentMode) {
|
||||
// The case that nothing has been written in segment.
|
||||
if (segmentWritten == 0) return;
|
||||
// There must be more data than just header size written for non-empty segment.
|
||||
assert segmentWritten > SEGMENT_HEADER_SIZE
|
||||
: "invalid header in segmented mode";
|
||||
|
||||
if (segmentWritten > (segmentBuffer.length)) {
|
||||
throw new RuntimeException("Heap segment size overflow.");
|
||||
}
|
||||
|
||||
if (segmentWritten > SEGMENT_HEADER_SIZE) {
|
||||
fillSegmentSize(segmentWritten - SEGMENT_HEADER_SIZE);
|
||||
super.write(segmentBuffer, 0, segmentWritten);
|
||||
super.flush();
|
||||
segmentWritten = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
super.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enters segmented mode, flush buffered data and set flag.
|
||||
*/
|
||||
public void enterSegmentMode() throws IOException {
|
||||
if (allowSegmented && !segmentMode && segmentWritten == 0) {
|
||||
super.flush();
|
||||
segmentMode = true;
|
||||
segmentWritten = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Before finish, flush all data in buffer.
|
||||
*/
|
||||
public void finish() throws IOException {
|
||||
if (allowSegmented && segmentMode) {
|
||||
flush();
|
||||
assert segmentWritten == 0;
|
||||
segmentMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exits segmented mode, flush segmented data.
|
||||
* @param force flush data regardless whether the buffer is full
|
||||
*/
|
||||
public void exitSegmentMode() throws IOException {
|
||||
if (allowSegmented && segmentMode && shouldFlush()) {
|
||||
flush();
|
||||
assert segmentWritten == 0;
|
||||
segmentMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the data should be flush based on data saved in
|
||||
* segmentBuffer.
|
||||
* This method is used to control the segments number and the memory usage.
|
||||
* If segment is too small, there will be lots segments in final dump file.
|
||||
* If it is too large, lots of memory is used in RAM.
|
||||
*/
|
||||
private boolean shouldFlush() {
|
||||
// return true if data in segmentBuffer has been extended.
|
||||
return segmentWritten > SEGMENT_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the write segment header into internal buffer.
|
||||
*/
|
||||
private void writeSegmentHeader() {
|
||||
assert segmentWritten == 0;
|
||||
segmentBuffer[segmentWritten++] = (byte)HPROF_HEAP_DUMP_SEGMENT;
|
||||
writeInteger(0);
|
||||
// segment size, write dummy length of 0 and we'll fix it later.
|
||||
writeInteger(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the segmented data size into the header.
|
||||
*/
|
||||
private void fillSegmentSize(int size) {
|
||||
byte[] lenBytes = genByteArrayFromInt(size);
|
||||
System.arraycopy(lenBytes, 0, segmentBuffer, 5, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an {@code int} to the internal segment buffer
|
||||
* {@code written} is incremented by {@code 4}.
|
||||
*/
|
||||
private final void writeInteger(int value) {
|
||||
byte[] intBytes = genByteArrayFromInt(value);
|
||||
System.arraycopy(intBytes, 0, segmentBuffer, segmentWritten, 4);
|
||||
segmentWritten += 4;
|
||||
}
|
||||
|
||||
// The buffer size for segmentBuffer.
|
||||
// Since it is hard to calculate and fill the data size of an segment in compressed
|
||||
// data, making the segmented data stored in this buffer could help rewrite the data
|
||||
// size before the segmented data are written to underlying GZIPOutputStream.
|
||||
private static final int SEGMENT_BUFFER_SIZE = 1 << 20;
|
||||
// Buffer size used to extend the segment buffer.
|
||||
private static final int SEGMENT_BUFFER_INC_SIZE = 1 << 10;
|
||||
// Headers:
|
||||
// 1 byte for HPROF_HEAP_DUMP_SEGMENT
|
||||
// 4 bytes for timestamp
|
||||
// 4 bytes for size
|
||||
private static final int SEGMENT_HEADER_SIZE = 9;
|
||||
// Segment support.
|
||||
private boolean segmentMode;
|
||||
private boolean allowSegmented;
|
||||
private byte segmentBuffer[];
|
||||
private int segmentWritten;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -24,7 +24,13 @@
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import static jdk.test.lib.Asserts.assertTrue;
|
||||
import static jdk.test.lib.Asserts.assertFalse;
|
||||
import jdk.test.lib.hprof.HprofParser;
|
||||
import jdk.test.lib.apps.LingeredApp;
|
||||
import jdk.test.lib.hprof.parser.HprofReader;
|
||||
import jtreg.SkippedException;
|
||||
@ -39,6 +45,10 @@ import jtreg.SkippedException;
|
||||
*/
|
||||
|
||||
public class ClhsdbDumpheap {
|
||||
// The default heap dump file name defined in JDK.
|
||||
private static final String HEAP_DUMP_FILENAME_DEFAULT = "heap.bin";
|
||||
private static final String HEAP_DUMP_GZIPED_FILENAME_DEFAULT = "heap.bin.gz";
|
||||
|
||||
public static void printStackTraces(String file) {
|
||||
try {
|
||||
System.out.println("HprofReader.getStack() output:");
|
||||
@ -51,26 +61,116 @@ public class ClhsdbDumpheap {
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyDumpFile(File dump) throws Exception {
|
||||
assertTrue(dump.exists() && dump.isFile(), "Could not create dump file " + dump.getAbsolutePath());
|
||||
printStackTraces(dump.getAbsolutePath());
|
||||
}
|
||||
|
||||
private static class SubTest {
|
||||
private String cmd;
|
||||
private String fileName;
|
||||
private String expectedOutput;
|
||||
boolean compression;
|
||||
boolean needVerify;
|
||||
|
||||
public SubTest(String comm, String fName, boolean isComp, boolean verify, String expected) {
|
||||
cmd = comm;
|
||||
fileName = fName;
|
||||
expectedOutput = expected;
|
||||
compression = isComp;
|
||||
needVerify = verify;
|
||||
}
|
||||
|
||||
public String getCmd() { return cmd; }
|
||||
public String getFileName() { return fileName; }
|
||||
public String getExpectedOutput() { return expectedOutput; }
|
||||
public boolean isCompression() { return compression; }
|
||||
public boolean needVerify() { return needVerify; }
|
||||
}
|
||||
|
||||
private static void runTest(long appPid, SubTest subtest) throws Exception {
|
||||
ClhsdbLauncher test = new ClhsdbLauncher();
|
||||
String fileName = subtest.getFileName();
|
||||
String cmd = subtest.getCmd();
|
||||
String expectedOutput = subtest.getExpectedOutput();
|
||||
boolean compression = subtest.isCompression();
|
||||
/* The expected generated file, used to distinguish with fileName in case fileName is blank or null */
|
||||
String expectedFileName = fileName;
|
||||
if (fileName == null || fileName.length() == 0) {
|
||||
if (!compression) {
|
||||
expectedFileName = HEAP_DUMP_FILENAME_DEFAULT;
|
||||
} else {
|
||||
expectedFileName = HEAP_DUMP_GZIPED_FILENAME_DEFAULT;
|
||||
}
|
||||
}
|
||||
assertTrue (expectedFileName != null && expectedFileName.length() > 0,
|
||||
"Expected generated file name must have value");
|
||||
File file = new File(expectedFileName);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
String command = cmd + fileName;
|
||||
List<String> cmds = List.of(command);
|
||||
Map<String, List<String>> expStrMap = new HashMap<>();
|
||||
expStrMap.put(command, List.of(expectedOutput));
|
||||
test.run(appPid, cmds, expStrMap, null);
|
||||
if (subtest.needVerify()) {
|
||||
verifyDumpFile(file);
|
||||
}
|
||||
file.delete();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("Starting ClhsdbDumpheap test");
|
||||
|
||||
LingeredApp theApp = null;
|
||||
try {
|
||||
// Use file name different with JDK's default value "heap.bin".
|
||||
String heapDumpFileName = "heapdump.bin";
|
||||
ClhsdbLauncher test = new ClhsdbLauncher();
|
||||
String heapDumpFileNameGz = "heapdump.bin.gz";
|
||||
|
||||
theApp = new LingeredApp();
|
||||
LingeredApp.startApp(theApp);
|
||||
System.out.println("Started LingeredApp with pid " + theApp.getPid());
|
||||
|
||||
List<String> cmds = List.of("dumpheap " + heapDumpFileName);
|
||||
|
||||
Map<String, List<String>> expStrMap = new HashMap<>();
|
||||
expStrMap.put("dumpheap", List.of(
|
||||
"heap written to " + heapDumpFileName));
|
||||
|
||||
test.run(theApp.getPid(), cmds, expStrMap, null);
|
||||
printStackTraces(heapDumpFileName);
|
||||
SubTest[] subtests = new SubTest[] {
|
||||
new SubTest("dumpheap ", heapDumpFileName, false/*compression*/, true,/*verify*/
|
||||
"heap written to " + heapDumpFileName),
|
||||
new SubTest("dumpheap gz=1 ", heapDumpFileNameGz, true, true,
|
||||
"heap written to " + heapDumpFileNameGz),
|
||||
new SubTest("dumpheap gz=9 ", heapDumpFileNameGz, true, true,
|
||||
"heap written to " + heapDumpFileNameGz),
|
||||
new SubTest("dumpheap gz=0 ", heapDumpFileNameGz, true, false,
|
||||
"Usage: dumpheap \\[gz=<1-9>\\] \\[filename\\]"),
|
||||
new SubTest("dumpheap gz=100 ", heapDumpFileNameGz, true, false,
|
||||
"Usage: dumpheap \\[gz=<1-9>\\] \\[filename\\]"),
|
||||
new SubTest("dumpheap gz= ", heapDumpFileNameGz, true, false,
|
||||
"Usage: dumpheap \\[gz=<1-9>\\] \\[filename\\]"),
|
||||
new SubTest("dumpheap gz ", heapDumpFileNameGz, true, false,
|
||||
"Usage: dumpheap \\[gz=<1-9>\\] \\[filename\\]"),
|
||||
new SubTest("dumpheap", "", false, true,
|
||||
"heap written to " + HEAP_DUMP_FILENAME_DEFAULT),
|
||||
new SubTest("dumpheap gz=1", "", true, true,
|
||||
"heap written to " + HEAP_DUMP_GZIPED_FILENAME_DEFAULT),
|
||||
new SubTest("dumpheap gz=9", "", true, true,
|
||||
"heap written to " + HEAP_DUMP_GZIPED_FILENAME_DEFAULT),
|
||||
new SubTest("dumpheap gz=0", "", true, false,
|
||||
"Usage: dumpheap \\[gz=<1-9>\\] \\[filename\\]"),
|
||||
new SubTest("dumpheap gz=100", "", true, false,
|
||||
"Usage: dumpheap \\[gz=<1-9>\\] \\[filename\\]"),
|
||||
// Command "dumpheap gz=".
|
||||
new SubTest("dumpheap ", "gz=", true, false,
|
||||
"Usage: dumpheap \\[gz=<1-9>\\] \\[filename\\]"),
|
||||
// Command "dumpheap gz".
|
||||
new SubTest("dumpheap ", "gz", false, true, "heap written to gz"),
|
||||
// Command "dump heap gz=1 gz=2".
|
||||
new SubTest("dumpheap gz=1", "gz=2", true, false,
|
||||
"Usage: dumpheap \\[gz=<1-9>\\] \\[filename\\]")
|
||||
};
|
||||
// Run subtests
|
||||
for (int i = 0; i < subtests.length;i++) {
|
||||
runTest(theApp.getPid(), subtests[i]);
|
||||
}
|
||||
} catch (SkippedException se) {
|
||||
throw se;
|
||||
} catch (Exception ex) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 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
|
||||
@ -49,13 +49,13 @@ import jdk.test.lib.SA.SATestUtils;
|
||||
public class HeapDumpTest {
|
||||
|
||||
private static LingeredAppWithExtendedChars theApp = null;
|
||||
|
||||
private static final String SUCCESS_STRING = "heap written to";
|
||||
/**
|
||||
*
|
||||
* @param vmArgs - tool arguments to launch jhsdb
|
||||
* @return exit code of tool
|
||||
*/
|
||||
public static void launch(String expectedMessage, List<String> toolArgs)
|
||||
public static void launch(int expectedExitValue, List<String> toolArgs)
|
||||
throws IOException {
|
||||
|
||||
System.out.println("Starting LingeredApp");
|
||||
@ -81,9 +81,12 @@ public class HeapDumpTest {
|
||||
System.out.println(output.getStdout());
|
||||
System.out.println("stderr:");
|
||||
System.out.println(output.getStderr());
|
||||
output.shouldContain(expectedMessage);
|
||||
output.shouldHaveExitValue(0);
|
||||
|
||||
output.shouldHaveExitValue(expectedExitValue);
|
||||
if (expectedExitValue == 0) {
|
||||
output.shouldContain(SUCCESS_STRING);
|
||||
} else {
|
||||
output.stdoutShouldNotContain(SUCCESS_STRING);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Test ERROR " + ex, ex);
|
||||
} finally {
|
||||
@ -91,10 +94,9 @@ public class HeapDumpTest {
|
||||
}
|
||||
}
|
||||
|
||||
public static void launch(String expectedMessage, String... toolArgs)
|
||||
public static void launch(int expectedExitValue, String... toolArgs)
|
||||
throws IOException {
|
||||
|
||||
launch(expectedMessage, Arrays.asList(toolArgs));
|
||||
launch(expectedExitValue, Arrays.asList(toolArgs));
|
||||
}
|
||||
|
||||
public static void printStackTraces(String file) throws IOException {
|
||||
@ -108,31 +110,66 @@ public class HeapDumpTest {
|
||||
}
|
||||
}
|
||||
|
||||
public static void testHeapDump() throws IOException {
|
||||
public static void testHeapDump(SubTest subtest) throws IOException {
|
||||
String gzOption = subtest.getGzOption();
|
||||
boolean checkSuccess = subtest.needCheckSuccess();
|
||||
int expectedExitValue = checkSuccess ? 0 : 1;
|
||||
|
||||
File dump = new File("jhsdb.jmap.heap." +
|
||||
System.currentTimeMillis() + ".hprof");
|
||||
if (dump.exists()) {
|
||||
dump.delete();
|
||||
}
|
||||
if (gzOption == null || gzOption.length() == 0) {
|
||||
launch(expectedExitValue, "jmap",
|
||||
"--binaryheap", "--dumpfile=" + dump.getAbsolutePath());
|
||||
} else {
|
||||
launch(expectedExitValue, "jmap",
|
||||
"--binaryheap", gzOption, "--dumpfile=" + dump.getAbsolutePath());
|
||||
}
|
||||
|
||||
launch("heap written to", "jmap",
|
||||
"--binaryheap", "--dumpfile=" + dump.getAbsolutePath());
|
||||
if (checkSuccess) {
|
||||
assertTrue(dump.exists() && dump.isFile(),
|
||||
"Could not create dump file " + dump.getAbsolutePath());
|
||||
|
||||
assertTrue(dump.exists() && dump.isFile(),
|
||||
"Could not create dump file " + dump.getAbsolutePath());
|
||||
|
||||
printStackTraces(dump.getAbsolutePath());
|
||||
|
||||
dump.delete();
|
||||
printStackTraces(dump.getAbsolutePath());
|
||||
dump.delete();
|
||||
} else {
|
||||
assertTrue(!dump.exists(), "Unexpected file created: " + dump.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work.
|
||||
testHeapDump();
|
||||
|
||||
SubTest[] subtests = new SubTest[] {
|
||||
new SubTest("", true/*checkSuccess*/),
|
||||
new SubTest("--gz=1", true),
|
||||
new SubTest("--gz=9", true),
|
||||
new SubTest("--gz=0", false),
|
||||
new SubTest("--gz=100", false),
|
||||
new SubTest("--gz=", false),
|
||||
new SubTest("--gz", false),
|
||||
};
|
||||
// Run subtests
|
||||
for (int i = 0; i < subtests.length;i++) {
|
||||
testHeapDump(subtests[i]);
|
||||
}
|
||||
// The test throws RuntimeException on error.
|
||||
// IOException is thrown if LingeredApp can't start because of some bad
|
||||
// environment condition
|
||||
System.out.println("Test PASSED");
|
||||
}
|
||||
|
||||
private static class SubTest {
|
||||
private String gzOption;
|
||||
boolean needCheckSuccess;
|
||||
|
||||
public SubTest(String gzOpt, boolean checkSuccess) {
|
||||
gzOption = gzOpt;
|
||||
needCheckSuccess = checkSuccess;
|
||||
}
|
||||
|
||||
public String getGzOption() { return gzOption; }
|
||||
public boolean needCheckSuccess() { return needCheckSuccess; }
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 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
|
||||
@ -33,6 +33,7 @@
|
||||
package jdk.test.lib.hprof.parser;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import jdk.test.lib.hprof.model.*;
|
||||
|
||||
/**
|
||||
@ -45,6 +46,8 @@ import jdk.test.lib.hprof.model.*;
|
||||
|
||||
public abstract class Reader {
|
||||
protected PositionDataInputStream in;
|
||||
// Magic number of gzip dump file header.
|
||||
private static final int GZIP_HEADER_MAGIC = 0x1f8b08;
|
||||
|
||||
protected Reader(PositionDataInputStream in) {
|
||||
this.in = in;
|
||||
@ -142,9 +145,39 @@ public abstract class Reader {
|
||||
true, debugLevel);
|
||||
r.read();
|
||||
return r.printStackTraces();
|
||||
} else if ((i >>> 8) == GZIP_HEADER_MAGIC) {
|
||||
// Possible gziped file, try decompress it and get the stack trace.
|
||||
in.close();
|
||||
String deCompressedFile = "heapdump" + System.currentTimeMillis() + ".hprof";
|
||||
File out = new File(deCompressedFile);
|
||||
// Decompress to get dump file.
|
||||
try {
|
||||
GZIPInputStream gis = new GZIPInputStream(new FileInputStream(heapFile));
|
||||
FileOutputStream fos = new FileOutputStream(out);
|
||||
byte[] buffer = new byte[1024 * 1024];
|
||||
int len = 0;
|
||||
while ((len = gis.read(buffer)) > 0) {
|
||||
fos.write(buffer, 0, len);
|
||||
}
|
||||
// Check dump data header and print stack trace.
|
||||
PositionDataInputStream in2 = new PositionDataInputStream(
|
||||
new BufferedInputStream(new FileInputStream(out)));
|
||||
i = in2.readInt();
|
||||
if (i == HprofReader.MAGIC_NUMBER) {
|
||||
HprofReader r
|
||||
= new HprofReader(deCompressedFile, in2, dumpNumber,
|
||||
true, debugLevel);
|
||||
r.read();
|
||||
return r.printStackTraces();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Can not decompress the compressed hprof file", e);
|
||||
}
|
||||
out.delete();
|
||||
} else {
|
||||
throw new IOException("Unrecognized magic number: " + i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user