/*
* Copyright (c) 2008, 2011, 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. Oracle 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.
*
* 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 com.sun.tools.visualvm.modules.saplugin;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author Tomas Hurka
*/
class StackTrace {
private VM vm;
private SAObject heap;
private SAObject objectClass;
StackTrace(VM v) throws IllegalAccessException, InvocationTargetException {
vm = v;
heap = vm.getObjectHeap();
objectClass = vm.getSystemDictionary().invokeSA("getObjectKlass"); // NOI18N
}
public String getStackTrace() throws IllegalAccessException, InvocationTargetException {
ByteArrayOutputStream data = new ByteArrayOutputStream(4096);
PrintStream out = new PrintStream(data);
SAObject threads = vm.getThreads();
SAObject curThread = threads.invokeSA("first"); // NOI18N
for (;!curThread.isNull();curThread=curThread.invokeSA("next")) { // NOI18N
try {
Boolean isJavaThread = (Boolean) curThread.invoke("isJavaThread"); // NOI18N
if (!isJavaThread.booleanValue()) {
out.print("VM "); // NOI18N
}
out.print("Thread "); // NOI18N
curThread.invoke("printThreadIDOn",out); // NOI18N
out.print(" \""+curThread.invoke("getThreadName")+"\""); // NOI18N
out.print(": (state = "); // NOI18N
out.print(curThread.invoke("getThreadState")); // NOI18N
out.println(")");
if (isJavaThread.booleanValue()) { // Java thread
SAObject javaFrame = curThread.invokeSA("getLastJavaVFrameDbg"); // NOI18N
Object waitingToLockMonitor = curThread.invoke("getCurrentPendingMonitor"); // NOI18N
boolean objectWaitFrame = isJavaLangObjectWaitFrame(javaFrame);
for (;!javaFrame.isNull();javaFrame=javaFrame.invokeSA("javaSender")) { // NOI18N
printJavaFrame(out, javaFrame);
printMonitors(out, javaFrame, waitingToLockMonitor, objectWaitFrame);
waitingToLockMonitor = null;
objectWaitFrame = false;
}
}
} catch (Exception ex) {
out.println("\t-- Error occurred during stack walking");
Logger.getLogger(StackTrace.class.getName()).log(Level.INFO,"getStackTrace",ex);
}
out.println();
}
return data.toString();
}
private boolean isJavaLangObjectWaitFrame(SAObject javaFrame) throws IllegalAccessException, InvocationTargetException {
if (!javaFrame.isNull()) {
SAObject method = javaFrame.invokeSA("getMethod"); // NOI18N
SAObject klass = method.invokeSA("getMethodHolder"); // NOI18N
Boolean isNative = (Boolean) method.invoke("isNative"); // NOI18N
if (objectClass.equals(klass) && isNative.booleanValue()) {
if ("wait".equals(method.invokeSA("getName").invoke("asString"))) { // NOI18N
return true;
}
}
}
return false;
}
private void printMonitors(
final PrintStream out, final SAObject javaFrame,
Object waitingToLockMonitor, boolean objectWaitFrame)
throws IllegalAccessException, InvocationTargetException {
if (objectWaitFrame) {
SAObject stackValueCollection = javaFrame.invokeSA("getLocals"); // NOI18N
Boolean isEmpty = (Boolean) stackValueCollection.invoke("isEmpty"); // NOI18N
if (!isEmpty.booleanValue()) {
Object oopHandle = stackValueCollection.invoke("oopHandleAt", 0); // NOI18N
printMonitor(out, oopHandle, "waiting on"); // NOI18N
}
}
try {
List mList = (List) javaFrame.invoke("getMonitors"); // NOI18N
Object[] monitors = mList.toArray();
for (int i = monitors.length - 1; i >= 0; i--) {
SAObject monitorInfo = new SAObject(monitors[i]);
Object ownerHandle = monitorInfo.invoke("owner"); // NOI18N
if (ownerHandle != null) {
String state = "locked"; // NOI18N
if (waitingToLockMonitor != null) {
Object objectHandle = new SAObject(waitingToLockMonitor).invoke("object"); // NOI18N
if (objectHandle.equals(ownerHandle)) {
state = "waiting to lock"; // NOI18N
}
}
printMonitor(out, ownerHandle, state);
}
}
} catch (Exception e) {
// Ignore...
}
}
private void printMonitor(
final PrintStream out,
final Object ownerHandle,
final String state) {
try {
StringBuilder sb = new StringBuilder();
sb.append("\t- " + state + " <" + ownerHandle + "> "); // NOI18N
printOop(sb, ownerHandle);
out.println(sb.toString());
} catch (Exception e) {
// Ignore...
}
}
private void printOop(StringBuilder sb, Object oopHandle)
throws IllegalAccessException, InvocationTargetException {
SAObject oop = heap.invokeSA("newOop", oopHandle); // NOI18N
if (!oop.isNull()) {
sb.append("(a "); // NOI18N
String monitorClassName = (String) oop.invokeSA("getKlass").invokeSA("getName").invoke("asString"); // NOI18N
sb.append(monitorClassName.replace('/', '.'));
sb.append(")");
} else {
sb.append("(Raw Monitor)"); // NOI18N
}
}
private void printJavaFrame(final PrintStream out, final SAObject javaFrame) throws IllegalAccessException, InvocationTargetException {
SAObject method = javaFrame.invokeSA("getMethod"); // NOI18N
out.print("\tat "); // NOI18N
SAObject klass = method.invokeSA("getMethodHolder"); // NOI18N
String className = (String) klass.invokeSA("getName").invoke("asString"); // NOI18N
out.print(className.replace('/','.'));
out.print(".");
out.print(method.invokeSA("getName").invoke("asString")); // NOI18N
Integer bci = (Integer) javaFrame.invoke("getBCI"); // NOI18N
out.print("(");
if (((Boolean)method.invoke("isNative")).booleanValue()) { // NOI18N
out.print("Native Method"); // NOI18N
} else {
Integer lineNumber = (Integer) method.invoke("getLineNumberFromBCI",bci); // NOI18N
SAObject sourceName = klass.invokeSA("getSourceFileName"); // NOI18N
if (lineNumber.intValue()!=-1 && !sourceName.isNull()) {
out.print(sourceName.invoke("asString")); // NOI18N
out.print(":");
out.print(lineNumber);
} else {
out.print("bci="); // NOI18N
out.print(bci);
}
}
out.println(")");
}
}