提交 976d69ba 编写于 作者: C Chuansheng Lu 提交者: Jonathan Lu

[Misc] Port mini-heapdump to Dragonwell 11

Summary: port mini-heapdump feature to Dragonwell 11

Test Plan: test/jdk/tools, test/hotspot/serviceability

Reviewed-by: kuaiwei

Issue: #5
上级 ab268384
/*
* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2019, 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
......@@ -217,6 +217,7 @@ static jint jcmd(AttachOperation* op, outputStream* out) {
// Input arguments :-
// arg0: Name of the dump file
// arg1: "-live" or "-all"
// arg2: "-mini" or not exist
jint dump_heap(AttachOperation* op, outputStream* out) {
const char* path = op->arg(0);
if (path == NULL || path[0] == '\0') {
......@@ -232,13 +233,27 @@ jint dump_heap(AttachOperation* op, outputStream* out) {
live_objects_only = strcmp(arg1, "-live") == 0;
}
bool mini_heap_dump = false;
const char* arg2 = op->arg(2);
if (arg2 != NULL && (strlen(arg2) > 0)) {
if (strcmp(arg2, "-mini") != 0) {
out->print_cr("Invalid argument to dumpheap operation: %s", arg2);
return JNI_ERR;
}
mini_heap_dump = true;
}
// Request a full GC before heap dump if live_objects_only = true
// This helps reduces the amount of unreachable objects in the dump
// and makes it easier to browse.
HeapDumper dumper(live_objects_only /* request GC */);
HeapDumper dumper(live_objects_only /* request GC */, mini_heap_dump);
int res = dumper.dump(op->arg(0));
if (res == 0) {
out->print_cr("Heap dump file created");
if (mini_heap_dump) {
out->print_cr("Mini-heap dump file created");
} else {
out->print_cr("Heap dump file created");
}
} else {
// heap dump failed
ResourceMark rm;
......
/*
* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2019, 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
......@@ -503,8 +503,11 @@ HeapDumpDCmd::HeapDumpDCmd(outputStream* output, bool heap) :
DCmdWithParser(output, heap),
_filename("filename","Name of the dump file", "STRING",true),
_all("-all", "Dump all objects, including unreachable objects",
"BOOLEAN", false, "false"),
_mini_dump("-mini", "Use mini-dump format",
"BOOLEAN", false, "false") {
_dcmdparser.add_dcmd_option(&_all);
_dcmdparser.add_dcmd_option(&_mini_dump);
_dcmdparser.add_dcmd_argument(&_filename);
}
......@@ -512,10 +515,14 @@ void HeapDumpDCmd::execute(DCmdSource source, TRAPS) {
// Request a full GC before heap dump if _all is false
// This helps reduces the amount of unreachable objects in the dump
// and makes it easier to browse.
HeapDumper dumper(!_all.value() /* request GC if _all is false*/);
HeapDumper dumper(!_all.value() /* request GC if _all is false*/, _mini_dump.value());
int res = dumper.dump(_filename.value());
if (res == 0) {
output()->print_cr("Heap dump file created");
if (_mini_dump.value()) {
output()->print_cr("Mini heap dump file created");
} else {
output()->print_cr("Heap dump file created");
}
} else {
// heap dump failed
ResourceMark rm;
......
/*
* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2019, 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
......@@ -331,6 +331,7 @@ class HeapDumpDCmd : public DCmdWithParser {
protected:
DCmdArgument<char*> _filename;
DCmdArgument<bool> _all;
DCmdArgument<bool> _mini_dump;
public:
HeapDumpDCmd(outputStream* output, bool heap);
static const char* name() {
......
/*
* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2019, 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
......@@ -668,7 +668,7 @@ class DumperSupport : AllStatic {
// creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array
static void dump_object_array(DumpWriter* writer, objArrayOop array);
// creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array
static void dump_prim_array(DumpWriter* writer, typeArrayOop array);
static void dump_prim_array(DumpWriter* writer, typeArrayOop array, bool minidump = false);
// create HPROF_FRAME record for the given method and bci
static void dump_stack_frame(DumpWriter* writer, int frame_serial_num, int class_serial_num, Method* m, int bci);
......@@ -1153,13 +1153,13 @@ void DumperSupport::dump_object_array(DumpWriter* writer, objArrayOop array) {
for (int i = 0; i < Length; i++) { writer->write_##Size((Size)Array->Type##_at(i)); }
// creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array
void DumperSupport::dump_prim_array(DumpWriter* writer, typeArrayOop array) {
void DumperSupport::dump_prim_array(DumpWriter* writer, typeArrayOop array, bool minidump) {
BasicType type = TypeArrayKlass::cast(array->klass())->element_type();
// 2 * sizeof(u1) + 2 * sizeof(u4) + sizeof(objectID)
short header_size = 2 * 1 + 2 * 4 + sizeof(address);
int length = calculate_array_max_length(writer, array, header_size);
int length = minidump ? 0 : calculate_array_max_length(writer, array, header_size);
int type_size = type2aelembytes(type);
u4 length_in_bytes = (u4)length * type_size;
......@@ -1406,6 +1406,8 @@ class HeapObjectDumper : public ObjectClosure {
// used to indicate that a record has been writen
void mark_end_of_record();
bool using_minidump();
public:
HeapObjectDumper(VM_HeapDumper* dumper, DumpWriter* writer) {
_dumper = dumper;
......@@ -1434,7 +1436,11 @@ void HeapObjectDumper::do_object(oop o) {
mark_end_of_record();
} else if (o->is_typeArray()) {
// create a HPROF_GC_PRIM_ARRAY_DUMP record for each type array
DumperSupport::dump_prim_array(writer(), typeArrayOop(o));
if (using_minidump()) {
DumperSupport::dump_prim_array(writer(), typeArrayOop(o), true);
} else {
DumperSupport::dump_prim_array(writer(), typeArrayOop(o));
}
mark_end_of_record();
}
}
......@@ -1452,6 +1458,8 @@ class VM_HeapDumper : public VM_GC_Operation {
ThreadStackTrace** _stack_traces;
int _num_threads;
HeapDumper* _heap_dumper;
// accessors and setters
static VM_HeapDumper* dumper() { assert(_global_dumper != NULL, "Error"); return _global_dumper; }
static DumpWriter* writer() { assert(_global_writer != NULL, "Error"); return _global_writer; }
......@@ -1491,7 +1499,7 @@ class VM_HeapDumper : public VM_GC_Operation {
void dump_stack_traces();
public:
VM_HeapDumper(DumpWriter* writer, bool gc_before_heap_dump, bool oome) :
VM_HeapDumper(DumpWriter* writer, bool gc_before_heap_dump, bool oome, HeapDumper* heap_dumper = NULL) :
VM_GC_Operation(0 /* total collections, dummy, ignored */,
GCCause::_heap_dump /* GC Cause */,
0 /* total full collections, dummy, ignored */,
......@@ -1501,6 +1509,7 @@ class VM_HeapDumper : public VM_GC_Operation {
_klass_map = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(INITIAL_CLASS_COUNT, true);
_stack_traces = NULL;
_num_threads = 0;
_heap_dumper = heap_dumper;
if (oome) {
assert(!Thread::current()->is_VM_thread(), "Dump from OutOfMemoryError cannot be called by the VMThread");
// get OutOfMemoryError zero-parameter constructor
......@@ -1528,11 +1537,17 @@ class VM_HeapDumper : public VM_GC_Operation {
// used to mark sub-record boundary
void check_segment_length();
void doit();
HeapDumper* heap_dumper() { return _heap_dumper; }
};
VM_HeapDumper* VM_HeapDumper::_global_dumper = NULL;
DumpWriter* VM_HeapDumper::_global_writer = NULL;
bool HeapObjectDumper::using_minidump() {
return _dumper->heap_dumper()->is_mini_dump();
}
bool VM_HeapDumper::skip_operation() const {
return false;
}
......@@ -1956,7 +1971,7 @@ int HeapDumper::dump(const char* path) {
}
// generate the dump
VM_HeapDumper dumper(&writer, _gc_before_heap_dump, _oome);
VM_HeapDumper dumper(&writer, _gc_before_heap_dump, _oome, this);
if (Thread::current()->is_VM_thread()) {
assert(SafepointSynchronize::is_at_safepoint(), "Expected to be called at a safepoint");
dumper.doit();
......
/*
* Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2019, 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
......@@ -47,10 +47,12 @@ class HeapDumper : public StackObj {
bool _print_to_tty;
bool _gc_before_heap_dump;
bool _oome;
bool _mini_dump;
elapsedTimer _t;
HeapDumper(bool gc_before_heap_dump, bool print_to_tty, bool oome) :
_gc_before_heap_dump(gc_before_heap_dump), _error(NULL), _print_to_tty(print_to_tty), _oome(oome) { }
_gc_before_heap_dump(gc_before_heap_dump), _error(NULL),
_print_to_tty(print_to_tty), _oome(oome), _mini_dump(false) { }
// string representation of error
char* error() const { return _error; }
......@@ -65,8 +67,12 @@ class HeapDumper : public StackObj {
static void dump_heap(bool oome);
public:
HeapDumper(bool gc_before_heap_dump) :
_gc_before_heap_dump(gc_before_heap_dump), _error(NULL), _print_to_tty(false), _oome(false) { }
HeapDumper(bool gc_before_heap_dump, bool mini_dump = false) :
_gc_before_heap_dump(gc_before_heap_dump),
_error(NULL),
_print_to_tty(false),
_oome(false),
_mini_dump(mini_dump) { }
~HeapDumper();
......@@ -79,6 +85,8 @@ class HeapDumper : public StackObj {
static void dump_heap() NOT_SERVICES_RETURN;
static void dump_heap_from_oome() NOT_SERVICES_RETURN;
inline bool is_mini_dump() const { return _mini_dump; }
};
#endif // SHARE_VM_SERVICES_HEAPDUMPER_HPP
......@@ -170,6 +170,7 @@ public class JMap {
String subopts[] = options.split(",");
String filename = null;
String liveopt = "-all";
String miniopt = null;
for (int i = 0; i < subopts.length; i++) {
String subopt = subopts[i];
......@@ -180,6 +181,8 @@ public class JMap {
if (subopt.length() > 5) {
filename = subopt.substring(5);
}
} else if (subopt.startsWith("mini")) {
miniopt = "-mini";
}
}
......@@ -193,7 +196,11 @@ public class JMap {
// is executed.
filename = new File(filename).getCanonicalPath();
// dumpHeap is not the same as jcmd GC.heap_dump
executeCommandForPid(pid, "dumpheap", filename, liveopt);
if (miniopt == null) {
executeCommandForPid(pid, "dumpheap", filename, liveopt);
} else {
executeCommandForPid(pid, "dumpheap", filename, liveopt, miniopt);
}
}
private static void checkForUnsupportedOptions(String[] args) {
......@@ -259,6 +266,7 @@ public class JMap {
System.err.println(" all objects in the heap are dumped.");
System.err.println(" format=b binary format");
System.err.println(" file=<file> dump heap to <file>");
System.err.println(" mini use minidump format (Dragonwell only)");
System.err.println("");
System.err.println(" Example: jmap -dump:live,format=b,file=heap.bin <pid>");
System.exit(exit);
......
#
# Copyright (c) 2019 Alibaba Group Holding Limited. 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
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Alibaba designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
#
# @test
# @summary Test 'jmap -dump:mini'
# @run shell/timeout=500 TestMiniDump.sh
#
if [ "${TESTSRC}" = "" ]
then
TESTSRC=${PWD}
echo "TESTSRC not set. Using "${TESTSRC}" as default"
fi
echo "TESTSRC=${TESTSRC}"
## Adding common setup Variables for running shell tests.
. ${TESTSRC}/../../../test_env.sh
JAVA=${TESTJAVA}${FS}bin${FS}java
JAVAC=${TESTJAVA}${FS}bin${FS}javac
JMAP=${TESTJAVA}${FS}bin${FS}jmap
JCMD=${TESTJAVA}${FS}bin${FS}jcmd
JPS=${TESTJAVA}${FS}bin${FS}jps
# A simple testcase used to invoke JVM
TEST_CLASS=Loop_$(date +%Y%m%d%H%M%S)
TEST_SOURCE=$TEST_CLASS.java
cat > $TEST_SOURCE << EOF
public class ${TEST_CLASS} {
public static void main(String[] args) {
// allocate 1G temp objects
for (int i = 0; i < 1024 * 1024; ++i) {
Object o = new byte[1024];
}
// keep Java process running
while (true);
}
}
EOF
# compile the test class
$JAVAC $TEST_SOURCE
if [ $? != '0' ]; then
echo "Failed to compile Foo.java"
exit 1
fi
${JAVA} -cp . -Xmx4g -Xms4g -Xmn2g ${TEST_CLASS}&
# wait child java process to allocate memory
sleep 5
PID=$(${JPS} | grep ${TEST_CLASS} | awk '{print $1}')
if [ $? != 0 ] || [ -z "${PID}" ]; then exit 1; fi
# full dump must be > 1G
FULL_DUMP_SIZE_THRESHOLD=$(( 1024 * 1024 * 1024))
# mini dump must be < 30MB
MINI_DUMP_SIZE_THRESHOLD=$(( 30 * 1024 * 1024))
# Test of full heap dump
DUMP="full_heap.bin"
${JMAP} -dump:format=b,file=${DUMP} ${PID}
if [ $? != 0 ] || [ ! -f "${PWD}/${DUMP}" ]; then exit 1; fi
SIZE=$(ls -l | grep ${DUMP} | awk '{print $5}')
if [ $? != 0 ] || [ ${SIZE} -le "${FULL_DUMP_SIZE_THRESHOLD}" ]; then
echo "Full heap dump is too small"
exit 1
fi
# full heap dump from jcmd
DUMP="full_heap2.bin"
${JCMD} ${PID} GC.heap_dump -all ${DUMP}
if [ $? != 0 ] || [ ! -f "${PWD}/${DUMP}" ]; then exit 1; fi
SIZE=$(ls -l | grep ${DUMP} | awk '{print $5}')
if [ $? != 0 ] || [ ${SIZE} -lt "${FULL_DUMP_SIZE_THRESHOLD}" ]; then
echo "Full heap dump is too small"
exit 1
fi
# Test of mini heap dump
DUMP="mini_heap.bin"
${JMAP} -dump:format=b,mini,file=${DUMP} ${PID}
if [ $? != 0 ] || [ ! -f "${PWD}/${DUMP}" ]; then exit 1; fi
SIZE=$(ls -l | grep ${DUMP} | awk '{print $5}')
if [ $? != 0 ] || [ ${SIZE} -ge "${MINI_DUMP_SIZE_THRESHOLD}" ]; then
echo "Mini heap dump is too large"
exit 1
fi
# minidump from jcmd
DUMP="mini_heap2.bin"
${JCMD} ${PID} GC.heap_dump -all -mini ${DUMP}
if [ $? != 0 ] || [ ! -f "${PWD}/${DUMP}" ]; then exit 1; fi
SIZE=$(ls -l | grep ${DUMP} | awk '{print $5}')
if [ $? != 0 ] || [ ${SIZE} -ge "${MINI_DUMP_SIZE_THRESHOLD}" ]; then
echo "Mini heap dump is too large"
exit 1
fi
# clean up
rm -f *.bin
if [ $? != 0 ]; then exit 1; fi
kill -9 ${PID}
if [ $? != 0 ]; then exit 1; fi
exit 0
#
# Copyright (c) 2019 Alibaba Group Holding Limited. 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
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Alibaba designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
#
# @test
# @summary Test jmap options related to mini heap dump
# @run shell/timeout=300 TestMiniHeapDumpOpts.sh
#
if [ "${TESTSRC}" = "" ]
then
TESTSRC=${PWD}
echo "TESTSRC not set. Using "${TESTSRC}" as default"
fi
echo "TESTSRC=${TESTSRC}"
## Adding common setup Variables for running shell tests.
. ${TESTSRC}/../../../test_env.sh
JMAP=${TESTJAVA}${FS}bin${FS}jmap
JAVAC=${TESTJAVA}${FS}bin${FS}javac
JAVA=${TESTJAVA}${FS}bin${FS}java
JPS=${TESTJAVA}${FS}bin${FS}jps
# Basic options
if [ ! -f ${JMAP} ]; then
echo "Cannot find jmap!"
exit 1
fi
if [ -z "$(${JMAP} -h 2>&1 | grep 'mini use minidump format (Dragonwell only)')" ]; then
echo "Cannot find minidump option from 'jmap -h'"
exit 1
fi
# Test if -dump:mini options works without error
cat > Loop.java <<EOF
public class Loop {
public static void main(String[] args) {
while(true);
}
}
EOF
${JAVAC} Loop.java
if [ $? != 0 ]; then exit 1; fi
${JAVA} -cp . Loop&
PID=$(${JPS} | grep 'Loop' | awk '{print $1}')
if [ $? != 0 ] || [ -z "${PID}" ]; then exit 1; fi
${JMAP} -dump:format=b,mini,file=heap.bin ${PID}
if [ $? != 0 ] || [ ! -f "${PWD}/heap.bin" ]; then exit 1; fi
kill -9 ${PID}
if [ $? != 0 ]; then exit 1; fi
exit 0
/*
* Copyright (c) 2019 Alibaba Group Holding Limited. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Alibaba designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import static jdk.test.lib.Asserts.*;
/**
* @test TestNoMinidumpAtFullGC
* @summary Test to enasure mini-heap-dump not triggered when -XX:+HeapDumpBeforeFullGC
* @library /test/lib
* @build TestNoMinidumpAtFullGC
* @run main/othervm/timeout=300 TestNoMinidumpAtFullGC
*/
public class TestNoMinidumpAtFullGC {
private static final int M = 1024 * 1024;
// child process which creates a lot of primitive array and will OOM anyway
public static class OOMWorker {
private static final List<Object> holder = new LinkedList<>();
public static void main(String[] args) {
while (true) {
holder.add(new int[4096]);
}
}
}
public static void main(String[] args) {
try {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-Xmx128m",
"-Xms128m",
"-XX:+HeapDumpBeforeFullGC",
"-XX:+HeapDumpAfterFullGC",
"-XX:+HeapDumpOnOutOfMemoryError",
OOMWorker.class.getName()
);
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldContain("java.lang.OutOfMemoryError");
output.shouldContain("Dumping heap to");
assertNotEquals(0, output.getExitValue());
Files.walk(Paths.get("."))
.filter(p -> p.toString().contains("java_pid"))
// if mini-heap-dump triggered, the dump size is only several mega-bytes;
// otherwise, it would be definitely more than 100MB
.forEach(p -> assertGreaterThan(p.toFile().length(), 100L * M));
} catch (Throwable t) {
fail();
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册