Skip to content

Commit

Permalink
[RAS] Mini-heap dump support
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Chuansheng Lu authored and shiyue.xw committed Sep 2, 2019
1 parent b7d0997 commit 422ef9f
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 18 deletions.
21 changes: 18 additions & 3 deletions src/share/vm/services/attachListener.cpp
@@ -1,5 +1,5 @@
/*
* 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.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -173,6 +173,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') {
Expand All @@ -188,13 +189,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;
Expand Down
13 changes: 10 additions & 3 deletions src/share/vm/services/diagnosticCommand.cpp
@@ -1,5 +1,5 @@
/*
* 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.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -339,19 +339,26 @@ 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);
}

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;
Expand Down
3 changes: 2 additions & 1 deletion src/share/vm/services/diagnosticCommand.hpp
@@ -1,5 +1,5 @@
/*
* 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.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -245,6 +245,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() {
Expand Down
35 changes: 27 additions & 8 deletions src/share/vm/services/heapDumper.cpp
@@ -1,5 +1,5 @@
/*
* 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
Expand Down Expand Up @@ -638,7 +638,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);
};
Expand Down Expand Up @@ -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
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();

writer->write_u1(HPROF_GC_PRIM_ARRAY_DUMP);
writer->write_objectID(array);
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));

// nothing to copy
if (array->length() == 0) {
if (array->length() == 0 || minidump) {
return;
}

Expand Down Expand Up @@ -1317,6 +1321,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;
Expand Down Expand Up @@ -1348,7 +1354,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();
}
}
Expand All @@ -1368,6 +1378,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; }
Expand Down Expand Up @@ -1422,7 +1434,7 @@ class VM_HeapDumper : public VM_GC_Operation {
void end_of_dump();

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 */,
Expand All @@ -1434,6 +1446,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
Expand Down Expand Up @@ -1461,11 +1474,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;
}
Expand Down Expand Up @@ -1903,7 +1922,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();
Expand Down
13 changes: 10 additions & 3 deletions src/share/vm/services/heapDumper.hpp
@@ -1,5 +1,5 @@
/*
* 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
Expand Down Expand Up @@ -47,6 +47,7 @@ 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) :
Expand All @@ -65,8 +66,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();

Expand All @@ -79,6 +84,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
127 changes: 127 additions & 0 deletions test/serviceability/sa/jmap-minidump/TestMiniDump.sh
@@ -0,0 +1,127 @@
#
# 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

0 comments on commit 422ef9f

Please sign in to comment.