--- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java 2017-08-24 22:52:51.406888470 +0900 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java 2017-08-24 22:52:51.291888149 +0900 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2007, 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 @@ -28,14 +28,19 @@ import java.util.*; import sun.jvm.hotspot.oops.*; import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.debugger.*; public abstract class JavaVFrame extends VFrame { + + private static final String ADDRESS_FORMAT = VM.getVM().isLP64() ? "0x%016x" + : "0x%08x"; + /** JVM state */ public abstract Method getMethod(); public abstract int getBCI(); public abstract StackValueCollection getLocals(); public abstract StackValueCollection getExpressions(); - public abstract List getMonitors(); // List + public abstract List getMonitors(); /** Test operation */ public boolean isJavaFrame() { return true; } @@ -49,9 +54,125 @@ // FIXME: not yet implemented // public Address getPendingMonitor(int frameCount); + public void printLockedObjectClassName(PrintStream tty, + OopHandle hobj, String lockState) { + if (hobj.asLongValue() != 0L) { + tty.format("\t- %s <" + ADDRESS_FORMAT + "> ", + lockState, hobj.asLongValue()); + + Klass klass = Oop.getKlassForOopHandle(hobj); + String klassName = klass.getName().asString(); + tty.print("(a "); + if (klassName.equals("java/lang/Class")) { + Oop obj = VM.getVM().getObjectHeap().newOop(hobj); + klassName = java_lang_Class.asExternalName(obj); + tty.print("java.lang.Class for "); + } + tty.println(klassName.replace('/', '.') + ")"); + } + } + /** Printing used during stack dumps */ - // FIXME: not yet implemented - // void print_lock_info(int frame_count); + public void printLockInfo(PrintStream tty, int frameCount) { + // If this is the first frame and it is java.lang.Object.wait(...) + // then print out the receiver. Locals are not always available, + // e.g., compiled native frames have no scope so there are no locals. + if (frameCount == 0) { + if (getMethod().getName().asString().equals("wait") && + getMethod().getMethodHolder().getName().asString().equals("java/lang/Object")) { + String waitState = "waiting on"; // assume we are waiting + // If earlier in the output we reported java.lang.Thread.State == + // "WAITING (on object monitor)" and now we report "waiting on", then + // we are still waiting for notification or timeout. Otherwise if + // we earlier reported java.lang.Thread.State == "BLOCKED (on object + // monitor)", then we are actually waiting to re-lock the monitor. + // At this level we can't distinguish the two cases to report + // "waited on" rather than "waiting on" for the second case. + StackValueCollection locs = getLocals(); + if (!locs.isEmpty()) { + StackValue sv = locs.get(0); + if (sv.getType() == BasicType.getTObject()) { + OopHandle o = sv.getObject(); + printLockedObjectClassName(tty, o, waitState); + } + } else { + tty.println("\t- " + waitState + " "); + } + } else if (thread.getCurrentParkBlocker() != null) { + Oop obj = thread.getCurrentParkBlocker(); + Klass k = obj.getKlass(); + tty.format("\t- parking to wait for <" + ADDRESS_FORMAT + "> (a %s)", + obj.getHandle().asLongValue(), k.getName().asString()); + tty.println(); + } + } + + // Print out all monitors that we have locked, or are trying to lock, + // including re-locking after being notified or timing out in a wait(). + List mons = getMonitors(); + if (!mons.isEmpty()) { + boolean foundFirstMonitor = false; + for (int index = mons.size() - 1; index >= 0; index--) { + MonitorInfo monitor = mons.get(index); + if (monitor.eliminated() && isCompiledFrame()) { // Eliminated in compiled code + if (monitor.ownerIsScalarReplaced()) { + Klass k = Oop.getKlassForOopHandle(monitor.ownerKlass()); + tty.println("\t- eliminated (a " + k.getName().asString() + ")"); + } else if (monitor.owner() != null) { + printLockedObjectClassName(tty, monitor.owner(), "eliminated"); + } + continue; + } + if (monitor.owner() != null) { + // the monitor is associated with an object, i.e., it is locked + + Mark mark = null; + String lockState = "locked"; + if (!foundFirstMonitor && frameCount == 0) { + // If this is the first frame and we haven't found an owned + // monitor before, then we need to see if we have completed + // the lock or if we are blocked trying to acquire it. Only + // an inflated monitor that is first on the monitor list in + // the first frame can block us on a monitor enter. + mark = new Mark(monitor.owner()); + if (mark.hasMonitor() && + ( // we have marked ourself as pending on this monitor + mark.monitor().equals(thread.getCurrentPendingMonitor()) || + // we are not the owner of this monitor + !mark.monitor().isEntered(thread) + )) { + lockState = "waiting to lock"; + } else { + // We own the monitor which is not as interesting so + // disable the extra printing below. + mark = null; + } + } else if (frameCount != 0) { + // This is not the first frame so we either own this monitor + // or we owned the monitor before and called wait(). Because + // wait() could have been called on any monitor in a lower + // numbered frame on the stack, we have to check all the + // monitors on the list for this frame. + mark = new Mark(monitor.owner()); + if (mark.hasMonitor() && + ( // we have marked ourself as pending on this monitor + mark.monitor().equals(thread.getCurrentPendingMonitor()) || + // we are not the owner of this monitor + !mark.monitor().isEntered(thread) + )) { + lockState = "waiting to re-lock in wait()"; + } else { + // We own the monitor which is not as interesting so + // disable the extra printing below. + mark = null; + } + } + printLockedObjectClassName(tty, monitor.owner(), lockState); + foundFirstMonitor = true; + } + } + } + } /** Printing operations */