提交 9268cee7 编写于 作者: C Chuansheng Lu 提交者: 云矅

[RAS] Mini-heap dump support

Summary:
- Port D336179 mini-heapdump support to Dragonwell
- Enable HotSpot to skip dumping content of primitive type arrays

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

Reviewers: 井桐

Reviewed By: 井桐

Differential Revision: https://aone.alibaba-inc.com/code/D849312
上级 1db76b71
/* /*
* Copyright (c) 2005, 2016, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -173,6 +173,7 @@ static jint jcmd(AttachOperation* op, outputStream* out) { ...@@ -173,6 +173,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: "-mini" or not exist
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') {
...@@ -188,13 +189,27 @@ jint dump_heap(AttachOperation* op, outputStream* out) { ...@@ -188,13 +189,27 @@ jint dump_heap(AttachOperation* op, outputStream* out) {
live_objects_only = strcmp(arg1, "-live") == 0; 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 // 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 */, mini_heap_dump);
int res = dumper.dump(op->arg(0)); int res = dumper.dump(op->arg(0));
if (res == 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 { } else {
// heap dump failed // heap dump failed
ResourceMark rm; ResourceMark rm;
......
/* /*
* Copyright (c) 2011, 2017, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -339,8 +339,11 @@ HeapDumpDCmd::HeapDumpDCmd(outputStream* output, bool heap) : ...@@ -339,8 +339,11 @@ HeapDumpDCmd::HeapDumpDCmd(outputStream* output, bool heap) :
DCmdWithParser(output, heap), DCmdWithParser(output, heap),
_filename("filename","Name of the dump file", "STRING",true), _filename("filename","Name of the dump file", "STRING",true),
_all("-all", "Dump all objects, including unreachable objects", _all("-all", "Dump all objects, including unreachable objects",
"BOOLEAN", false, "false"),
_mini_dump("-mini", "Use mini-dump format",
"BOOLEAN", false, "false") { "BOOLEAN", false, "false") {
_dcmdparser.add_dcmd_option(&_all); _dcmdparser.add_dcmd_option(&_all);
_dcmdparser.add_dcmd_option(&_mini_dump);
_dcmdparser.add_dcmd_argument(&_filename); _dcmdparser.add_dcmd_argument(&_filename);
} }
...@@ -348,10 +351,14 @@ void HeapDumpDCmd::execute(DCmdSource source, TRAPS) { ...@@ -348,10 +351,14 @@ void HeapDumpDCmd::execute(DCmdSource source, TRAPS) {
// Request a full GC before heap dump if _all is false // Request a full GC before heap dump if _all is false
// 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(!_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()); int res = dumper.dump(_filename.value());
if (res == 0) { 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 { } else {
// heap dump failed // heap dump failed
ResourceMark rm; ResourceMark rm;
......
/* /*
* Copyright (c) 2011, 2017, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -245,6 +245,7 @@ class HeapDumpDCmd : public DCmdWithParser { ...@@ -245,6 +245,7 @@ class HeapDumpDCmd : public DCmdWithParser {
protected: protected:
DCmdArgument<char*> _filename; DCmdArgument<char*> _filename;
DCmdArgument<bool> _all; DCmdArgument<bool> _all;
DCmdArgument<bool> _mini_dump;
public: public:
HeapDumpDCmd(outputStream* output, bool heap); HeapDumpDCmd(outputStream* output, bool heap);
static const char* name() { 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -638,7 +638,7 @@ class DumperSupport : AllStatic { ...@@ -638,7 +638,7 @@ class DumperSupport : AllStatic {
// creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array // creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array
static void dump_object_array(DumpWriter* writer, objArrayOop array); static void dump_object_array(DumpWriter* writer, objArrayOop array);
// creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type 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 // 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); static void dump_stack_frame(DumpWriter* writer, int frame_serial_num, int class_serial_num, Method* m, int bci);
}; };
...@@ -1070,17 +1070,21 @@ void DumperSupport::dump_object_array(DumpWriter* writer, objArrayOop array) { ...@@ -1070,17 +1070,21 @@ void DumperSupport::dump_object_array(DumpWriter* writer, objArrayOop array) {
// creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array // 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(); BasicType type = TypeArrayKlass::cast(array->klass())->element_type();
writer->write_u1(HPROF_GC_PRIM_ARRAY_DUMP); writer->write_u1(HPROF_GC_PRIM_ARRAY_DUMP);
writer->write_objectID(array); writer->write_objectID(array);
writer->write_u4(STACK_TRACE_ID); writer->write_u4(STACK_TRACE_ID);
writer->write_u4((u4)array->length()); if (!minidump) {
writer->write_u4((u4)array->length());
} else {
writer->write_u4(0);
}
writer->write_u1(type2tag(type)); writer->write_u1(type2tag(type));
// nothing to copy // nothing to copy
if (array->length() == 0) { if (array->length() == 0 || minidump) {
return; return;
} }
...@@ -1317,6 +1321,8 @@ class HeapObjectDumper : public ObjectClosure { ...@@ -1317,6 +1321,8 @@ class HeapObjectDumper : public ObjectClosure {
// used to indicate that a record has been writen // used to indicate that a record has been writen
void mark_end_of_record(); void mark_end_of_record();
bool using_minidump();
public: public:
HeapObjectDumper(VM_HeapDumper* dumper, DumpWriter* writer) { HeapObjectDumper(VM_HeapDumper* dumper, DumpWriter* writer) {
_dumper = dumper; _dumper = dumper;
...@@ -1348,7 +1354,11 @@ void HeapObjectDumper::do_object(oop o) { ...@@ -1348,7 +1354,11 @@ void HeapObjectDumper::do_object(oop o) {
mark_end_of_record(); mark_end_of_record();
} else if (o->is_typeArray()) { } else if (o->is_typeArray()) {
// create a HPROF_GC_PRIM_ARRAY_DUMP record for each type array // 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(); mark_end_of_record();
} }
} }
...@@ -1368,6 +1378,8 @@ class VM_HeapDumper : public VM_GC_Operation { ...@@ -1368,6 +1378,8 @@ class VM_HeapDumper : public VM_GC_Operation {
ThreadStackTrace** _stack_traces; ThreadStackTrace** _stack_traces;
int _num_threads; int _num_threads;
HeapDumper* _heap_dumper;
// accessors and setters // accessors and setters
static VM_HeapDumper* dumper() { assert(_global_dumper != NULL, "Error"); return _global_dumper; } static VM_HeapDumper* dumper() { assert(_global_dumper != NULL, "Error"); return _global_dumper; }
static DumpWriter* writer() { assert(_global_writer != NULL, "Error"); return _global_writer; } static DumpWriter* writer() { assert(_global_writer != NULL, "Error"); return _global_writer; }
...@@ -1422,7 +1434,7 @@ class VM_HeapDumper : public VM_GC_Operation { ...@@ -1422,7 +1434,7 @@ class VM_HeapDumper : public VM_GC_Operation {
void end_of_dump(); void end_of_dump();
public: 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 */, VM_GC_Operation(0 /* total collections, dummy, ignored */,
GCCause::_heap_dump /* GC Cause */, GCCause::_heap_dump /* GC Cause */,
0 /* total full collections, dummy, ignored */, 0 /* total full collections, dummy, ignored */,
...@@ -1434,6 +1446,7 @@ class VM_HeapDumper : public VM_GC_Operation { ...@@ -1434,6 +1446,7 @@ class VM_HeapDumper : public VM_GC_Operation {
_klass_map = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(INITIAL_CLASS_COUNT, true); _klass_map = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(INITIAL_CLASS_COUNT, true);
_stack_traces = NULL; _stack_traces = NULL;
_num_threads = 0; _num_threads = 0;
_heap_dumper = heap_dumper;
if (oome) { if (oome) {
assert(!Thread::current()->is_VM_thread(), "Dump from OutOfMemoryError cannot be called by the VMThread"); assert(!Thread::current()->is_VM_thread(), "Dump from OutOfMemoryError cannot be called by the VMThread");
// get OutOfMemoryError zero-parameter constructor // get OutOfMemoryError zero-parameter constructor
...@@ -1461,11 +1474,17 @@ class VM_HeapDumper : public VM_GC_Operation { ...@@ -1461,11 +1474,17 @@ class VM_HeapDumper : public VM_GC_Operation {
// used to mark sub-record boundary // used to mark sub-record boundary
void check_segment_length(); void check_segment_length();
void doit(); void doit();
HeapDumper* heap_dumper() { return _heap_dumper; }
}; };
VM_HeapDumper* VM_HeapDumper::_global_dumper = NULL; VM_HeapDumper* VM_HeapDumper::_global_dumper = NULL;
DumpWriter* VM_HeapDumper::_global_writer = NULL; DumpWriter* VM_HeapDumper::_global_writer = NULL;
bool HeapObjectDumper::using_minidump() {
return _dumper->heap_dumper()->is_mini_dump();
}
bool VM_HeapDumper::skip_operation() const { bool VM_HeapDumper::skip_operation() const {
return false; return false;
} }
...@@ -1903,7 +1922,7 @@ int HeapDumper::dump(const char* path) { ...@@ -1903,7 +1922,7 @@ int HeapDumper::dump(const char* path) {
} }
// generate the dump // 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()) { if (Thread::current()->is_VM_thread()) {
assert(SafepointSynchronize::is_at_safepoint(), "Expected to be called at a safepoint"); assert(SafepointSynchronize::is_at_safepoint(), "Expected to be called at a safepoint");
dumper.doit(); 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -47,6 +47,7 @@ class HeapDumper : public StackObj { ...@@ -47,6 +47,7 @@ class HeapDumper : public StackObj {
bool _print_to_tty; bool _print_to_tty;
bool _gc_before_heap_dump; bool _gc_before_heap_dump;
bool _oome; bool _oome;
bool _mini_dump;
elapsedTimer _t; elapsedTimer _t;
HeapDumper(bool gc_before_heap_dump, bool print_to_tty, bool oome) : HeapDumper(bool gc_before_heap_dump, bool print_to_tty, bool oome) :
...@@ -65,8 +66,12 @@ class HeapDumper : public StackObj { ...@@ -65,8 +66,12 @@ class HeapDumper : public StackObj {
static void dump_heap(bool oome); static void dump_heap(bool oome);
public: public:
HeapDumper(bool gc_before_heap_dump) : 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) { } _gc_before_heap_dump(gc_before_heap_dump),
_error(NULL),
_print_to_tty(false),
_oome(false),
_mini_dump(mini_dump) { }
~HeapDumper(); ~HeapDumper();
...@@ -79,6 +84,8 @@ class HeapDumper : public StackObj { ...@@ -79,6 +84,8 @@ class HeapDumper : public StackObj {
static void dump_heap() NOT_SERVICES_RETURN; static void dump_heap() NOT_SERVICES_RETURN;
static void dump_heap_from_oome() 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 #endif // SHARE_VM_SERVICES_HEAPDUMPER_HPP
#
# 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_THRESHOLDL}" ]; 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
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册