6760477: Update SA to include stack traces in the heap dump
Summary: Update SA to include HPROF_TRACE and HPROF_FRAME records in the heap dump
Reviewed-by: dsamersoff
Contributed-by: sharath.ballal@oracle.com
--- a/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java Wed Jul 05 23:33:53 2017 +0200
+++ b/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java Fri Jun 02 12:30:49 2017 +0530
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2017, 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,6 +50,7 @@
private static AddressField osThreadField;
private static AddressField stackBaseField;
private static CIntegerField stackSizeField;
+ private static CIntegerField terminatedField;
private static JavaThreadPDAccess access;
@@ -66,6 +67,9 @@
private static int BLOCKED;
private static int BLOCKED_TRANS;
+ private static int NOT_TERMINATED;
+ private static int EXITING;
+
static {
VM.registerVMInitializedObserver(new Observer() {
public void update(Observable o, Object data) {
@@ -87,6 +91,7 @@
osThreadField = type.getAddressField("_osthread");
stackBaseField = type.getAddressField("_stack_base");
stackSizeField = type.getCIntegerField("_stack_size");
+ terminatedField = type.getCIntegerField("_terminated");
UNINITIALIZED = db.lookupIntConstant("_thread_uninitialized").intValue();
NEW = db.lookupIntConstant("_thread_new").intValue();
@@ -99,6 +104,10 @@
IN_JAVA_TRANS = db.lookupIntConstant("_thread_in_Java_trans").intValue();
BLOCKED = db.lookupIntConstant("_thread_blocked").intValue();
BLOCKED_TRANS = db.lookupIntConstant("_thread_blocked_trans").intValue();
+
+ NOT_TERMINATED = db.lookupIntConstant("JavaThread::_not_terminated").intValue();
+ EXITING = db.lookupIntConstant("JavaThread::_thread_exiting").intValue();
+
}
public JavaThread(Address addr) {
@@ -128,6 +137,14 @@
example, "SolarisSPARCCompilerThread".) */
public boolean isJavaThread() { return true; }
+ public boolean isExiting () {
+ return (getTerminated() == EXITING) || isTerminated();
+ }
+
+ public boolean isTerminated() {
+ return (getTerminated() != NOT_TERMINATED) && (getTerminated() != EXITING);
+ }
+
public static AddressField getAnchorField() { return anchorField; }
/** Get the last Java stack pointer */
@@ -329,6 +346,10 @@
return stackSizeField.getValue(addr);
}
+ public int getTerminated() {
+ return (int) terminatedField.getValue(addr);
+ }
+
/** Gets the Java-side thread object for this JavaThread */
public Oop getThreadObj() {
Oop obj = null;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StackFrameInfo.java Fri Jun 02 12:30:49 2017 +0530
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package sun.jvm.hotspot.runtime;
+
+import sun.jvm.hotspot.oops.*;
+
+public class StackFrameInfo {
+ private Method method;
+ int bci;
+ Oop classHolder;
+
+ public StackFrameInfo(JavaVFrame vf) {
+ this.method = vf.getMethod();
+ this.bci = vf.getBCI();
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public int getBCI() {
+ return bci;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadStackTrace.java Fri Jun 02 12:30:49 2017 +0530
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2017, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package sun.jvm.hotspot.runtime;
+
+import java.util.*;
+
+public class ThreadStackTrace {
+ private JavaThread thread;
+ private int depth; // number of stack frames added
+ private ArrayList<StackFrameInfo> frames;
+
+ public ThreadStackTrace(JavaThread t) {
+ this.thread = t;
+ this.depth = 0;
+ this.frames = new ArrayList<StackFrameInfo>();
+ }
+
+ public int getStackDepth() {
+ return depth;
+ }
+
+ public StackFrameInfo stackFrameAt(int index) {
+ return frames.get(index);
+ }
+
+ public void dumpStack(int maxDepth) {
+ if (!thread.isJavaThread()) {
+ System.out.println("dumpStack: not java Thread returning");
+ return;
+ }
+ try {
+ for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
+ StackFrameInfo frame = new StackFrameInfo(vf);
+ frames.add(frame);
+ depth++;
+
+ if (maxDepth > 0 && depth == maxDepth) {
+ // Skip frames if more than maxDepth
+ break;
+ }
+ }
+ } catch (Exception e) {
+ System.out.println("Error occurred during stack walking:");
+ e.printStackTrace();
+ }
+ }
+}
--- a/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java Wed Jul 05 23:33:53 2017 +0200
+++ b/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java Fri Jun 02 12:30:49 2017 +0530
@@ -379,6 +379,10 @@
private static final long MAX_U4_VALUE = 0xFFFFFFFFL;
int serialNum = 1;
+ public HeapHprofBinWriter() {
+ this.KlassMap = new ArrayList<Klass>();
+ }
+
public synchronized void write(String fileName) throws IOException {
// open file stream and create buffered data output stream
fos = new FileOutputStream(fileName);
@@ -426,6 +430,9 @@
// HPROF_LOAD_CLASS records for all classes
writeClasses();
+ // write HPROF_FRAME and HPROF_TRACE records
+ dumpStackTraces();
+
// write CLASS_DUMP records
writeClassDumpRecords();
@@ -700,6 +707,67 @@
}
}
+ private void dumpStackTraces() throws IOException {
+ // write a HPROF_TRACE record without any frames to be referenced as object alloc sites
+ writeHeader(HPROF_TRACE, 3 * (int)INT_SIZE );
+ out.writeInt(DUMMY_STACK_TRACE_ID);
+ out.writeInt(0); // thread number
+ out.writeInt(0); // frame count
+
+ int frameSerialNum = 0;
+ int numThreads = 0;
+ Threads threads = VM.getVM().getThreads();
+
+ for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
+ Oop threadObj = thread.getThreadObj();
+ if (threadObj != null && !thread.isExiting() && !thread.isHiddenFromExternalView()) {
+
+ // dump thread stack trace
+ ThreadStackTrace st = new ThreadStackTrace(thread);
+ st.dumpStack(-1);
+ numThreads++;
+
+ // write HPROF_FRAME records for this thread's stack trace
+ int depth = st.getStackDepth();
+ int threadFrameStart = frameSerialNum;
+ for (int j=0; j < depth; j++) {
+ StackFrameInfo frame = st.stackFrameAt(j);
+ Method m = frame.getMethod();
+ int classSerialNum = KlassMap.indexOf(m.getMethodHolder()) + 1;
+ // the class serial number starts from 1
+ assert classSerialNum > 0:"class not found";
+ dumpStackFrame(++frameSerialNum, classSerialNum, m, frame.getBCI());
+ }
+
+ // write HPROF_TRACE record for one thread
+ writeHeader(HPROF_TRACE, 3 * (int)INT_SIZE + depth * (int)VM.getVM().getOopSize());
+ int stackSerialNum = numThreads + DUMMY_STACK_TRACE_ID;
+ out.writeInt(stackSerialNum); // stack trace serial number
+ out.writeInt(numThreads); // thread serial number
+ out.writeInt(depth); // frame count
+ for (int j=1; j <= depth; j++) {
+ writeObjectID(threadFrameStart + j);
+ }
+ }
+ }
+ }
+
+ private void dumpStackFrame(int frameSN, int classSN, Method m, int bci) throws IOException {
+ int lineNumber;
+ if (m.isNative()) {
+ lineNumber = -3; // native frame
+ } else {
+ lineNumber = m.getLineNumberFromBCI(bci);
+ }
+ writeHeader(HPROF_FRAME, 4 * (int)VM.getVM().getOopSize() + 2 * (int)INT_SIZE);
+ writeObjectID(frameSN); // frame serial number
+ writeSymbolID(m.getName()); // method's name
+ writeSymbolID(m.getSignature()); // method's signature
+ writeSymbolID(m.getMethodHolder().getSourceFileName()); // source file name
+ out.writeInt(classSN); // class serial number
+ out.writeInt(lineNumber); // line number
+ }
+
protected void writeJavaThread(JavaThread jt, int index) throws IOException {
out.writeByte((byte) HPROF_GC_ROOT_THREAD_OBJ);
writeObjectID(jt.getThreadObj());
@@ -1030,6 +1098,7 @@
writeHeader(HPROF_LOAD_CLASS, 2 * (OBJ_ID_SIZE + 4));
out.writeInt(serialNum);
writeObjectID(clazz);
+ KlassMap.add(serialNum - 1, k);
out.writeInt(DUMMY_STACK_TRACE_ID);
writeSymbolID(k.getName());
serialNum++;
@@ -1045,6 +1114,7 @@
writeHeader(HPROF_LOAD_CLASS, 2 * (OBJ_ID_SIZE + 4));
out.writeInt(serialNum);
writeObjectID(clazz);
+ KlassMap.add(serialNum - 1, k);
out.writeInt(DUMMY_STACK_TRACE_ID);
writeSymbolID(k.getName());
serialNum++;
@@ -1157,6 +1227,7 @@
private Debugger dbg;
private ObjectHeap objectHeap;
private SymbolTable symTbl;
+ private ArrayList<Klass> KlassMap;
// oopSize of the debuggee
private int OBJ_ID_SIZE;
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp Wed Jul 05 23:33:53 2017 +0200
+++ b/hotspot/src/share/vm/runtime/vmStructs.cpp Fri Jun 02 12:30:49 2017 +0530
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2017, 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
@@ -971,6 +971,7 @@
nonstatic_field(JavaThread, _vframe_array_last, vframeArray*) \
nonstatic_field(JavaThread, _satb_mark_queue, SATBMarkQueue) \
nonstatic_field(JavaThread, _dirty_card_queue, DirtyCardQueue) \
+ volatile_nonstatic_field(JavaThread, _terminated, JavaThread::TerminatedTypes) \
nonstatic_field(Thread, _resource_area, ResourceArea*) \
nonstatic_field(CompilerThread, _env, ciEnv*) \
\
@@ -2213,6 +2214,7 @@
declare_toplevel_type(JavaThread*) \
declare_toplevel_type(java_lang_Class) \
declare_integer_type(JavaThread::AsyncRequests) \
+ declare_integer_type(JavaThread::TerminatedTypes) \
declare_toplevel_type(jbyte*) \
declare_toplevel_type(jbyte**) \
declare_toplevel_type(jint*) \
@@ -2435,6 +2437,8 @@
declare_constant(_thread_in_Java_trans) \
declare_constant(_thread_blocked) \
declare_constant(_thread_blocked_trans) \
+ declare_constant(JavaThread::_not_terminated) \
+ declare_constant(JavaThread::_thread_exiting) \
\
/******************************/ \
/* Klass misc. enum constants */ \