8185796: jstack and clhsdb jstack should show lock objects
Summary: Modifications to display monitor details with SA jstack
Reviewed-by: sspitsyn, jgeorge
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java Sun Nov 26 09:05:13 2017 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java Mon Nov 27 11:20:38 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
@@ -202,8 +202,7 @@
public boolean verify() { return true;}
- // Package-private routine to speed up ObjectHeap.newOop
- static Klass getKlassForOopHandle(OopHandle handle) {
+ public static Klass getKlassForOopHandle(OopHandle handle) {
if (handle == null) {
return null;
}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/java_lang_Class.java Sun Nov 26 09:05:13 2017 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/java_lang_Class.java Mon Nov 27 11:20:38 2017 +0530
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -41,6 +41,7 @@
// java.lang.Class fields
static int klassOffset;
+ static int arrayKlassOffset;
static IntField oopSizeField;
static {
@@ -56,6 +57,7 @@
// find them from InstanceKlass for java.lang.Class.
Type jlc = db.lookupType("java_lang_Class");
klassOffset = (int) jlc.getCIntegerField("_klass_offset").getValue();
+ arrayKlassOffset = (int) jlc.getCIntegerField("_array_klass_offset").getValue();
int oopSizeOffset = (int) jlc.getCIntegerField("_oop_size_offset").getValue();
oopSizeField = new IntField(new NamedFieldIdentifier("oop_size"), oopSizeOffset, true);
}
@@ -69,4 +71,23 @@
public static long getOopSize(Oop aClass) {
return java_lang_Class.oopSizeField.getValue(aClass);
}
+
+ /**
+ * Returns the Java name for this Java mirror
+ */
+ public static String asExternalName(Oop aClass) {
+ Klass k = java_lang_Class.asKlass(aClass);
+ if (k == null) { // primitive array
+ BasicType type = BasicType.T_VOID;
+ ArrayKlass ak = (ArrayKlass)Metadata.instantiateWrapperFor(
+ aClass.getHandle().getAddressAt(arrayKlassOffset));
+ if (ak != null) {
+ type = BasicType.intToBasicType(ak.getElementType());
+ }
+ return type.getName();
+ } else {
+ return k.getName().asString();
+ }
+ }
+
}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java Sun Nov 26 09:05:13 2017 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java Mon Nov 27 11:20:38 2017 +0530
@@ -133,6 +133,27 @@
return tIllegal;
}
+ public static BasicType intToBasicType(int i) {
+ switch(i) {
+ case tBoolean: return T_BOOLEAN;
+ case tChar: return T_CHAR;
+ case tFloat: return T_FLOAT;
+ case tDouble: return T_DOUBLE;
+ case tByte: return T_BYTE;
+ case tShort: return T_SHORT;
+ case tInt: return T_INT;
+ case tLong: return T_LONG;
+ case tObject: return T_OBJECT;
+ case tArray: return T_ARRAY;
+ case tVoid: return T_VOID;
+ case tAddress: return T_ADDRESS;
+ case tNarrowOop: return T_NARROWOOP;
+ case tMetadata: return T_METADATA;
+ case tNarrowKlass: return T_NARROWKLASS;
+ default: return T_ILLEGAL;
+ }
+ }
+
public static BasicType charToBasicType(char c) {
switch( c ) {
case 'B': return T_BYTE;
@@ -158,6 +179,28 @@
return type;
}
+ public String getName() {
+ switch (type) {
+ case tBoolean: return "boolean";
+ case tChar: return "char";
+ case tFloat: return "float";
+ case tDouble: return "double";
+ case tByte: return "byte";
+ case tShort: return "short";
+ case tInt: return "int";
+ case tLong: return "long";
+ case tObject: return "object";
+ case tArray: return "array";
+ case tVoid: return "void";
+ case tAddress: return "address";
+ case tNarrowOop: return "narrow oop";
+ case tMetadata: return "metadata";
+ case tNarrowKlass: return "narrow klass";
+ case tConflict: return "conflict";
+ default: return "ILLEGAL TYPE";
+ }
+ }
+
//-- Internals only below this point
private BasicType(int type) {
this.type = type;
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java Sun Nov 26 09:05:13 2017 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java Mon Nov 27 11:20:38 2017 +0530
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2009, 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
@@ -127,12 +127,12 @@
}
/** Returns List<MonitorInfo> */
- public List getMonitors() {
+ public List<MonitorInfo> getMonitors() {
List monitors = getScope().getMonitors();
if (monitors == null) {
- return new ArrayList();
+ return new ArrayList<>();
}
- List result = new ArrayList(monitors.size());
+ List<MonitorInfo> result = new ArrayList<>(monitors.size());
for (int i = 0; i < monitors.size(); i++) {
MonitorValue mv = (MonitorValue) monitors.get(i);
ScopeValue ov = mv.owner();
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java Sun Nov 26 09:05:13 2017 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java Mon Nov 27 11:20:38 2017 +0530
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2009, 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
@@ -108,8 +108,8 @@
}
/** Returns List<MonitorInfo> */
- public List getMonitors() {
- List result = new ArrayList(5);
+ public List<MonitorInfo> getMonitors() {
+ List<MonitorInfo> result = new ArrayList<>(5);
for (BasicObjectLock current = getFrame().interpreterFrameMonitorEnd();
current.address().lessThan(getFrame().interpreterFrameMonitorBegin().address());
current = getFrame().nextMonitorInInterpreterFrame(current)) {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java Sun Nov 26 09:05:13 2017 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java Mon Nov 27 11:20:38 2017 +0530
@@ -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<MonitorInfo>
+ public abstract List<MonitorInfo> getMonitors();
/** Test operation */
public boolean isJavaFrame() { return true; }
@@ -49,9 +54,112 @@
// 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('/', '.') + ")");
+ }
+ }
+
+ private String identifyLockState(MonitorInfo monitor, String waitingState) {
+ Mark 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)
+ )) {
+ return waitingState;
+ }
+ return "locked";
+ }
+
/** 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 + " <no object reference available>");
+ }
+ } 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<MonitorInfo> 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 <owner is scalar replaced> (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
+ 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.
+ lockState = identifyLockState(monitor, "waiting to lock");
+ } 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.
+ lockState = identifyLockState(monitor, "waiting to re-lock in wait()");
+ }
+ printLockedObjectClassName(tty, monitor.owner(), lockState);
+ foundFirstMonitor = true;
+ }
+ }
+ }
+ }
/** Printing operations */
@@ -73,22 +181,6 @@
printStackValuesOn(tty, "locals", getLocals());
printStackValuesOn(tty, "expressions", getExpressions());
-
- // List<MonitorInfo>
- // FIXME: not yet implemented
- // List list = getMonitors();
- // if (list.isEmpty()) {
- // return;
- // }
- // for (int index = 0; index < list.size(); index++) {
- // MonitorInfo monitor = (MonitorInfo) list.get(index);
- // tty.print("\t obj\t");
- // monitor.getOwner().printValueOn(tty);
- // tty.println();
- // tty.print("\t ");
- // monitor.lock().printOn(tty);
- // tty.println();
- // }
}
public void printActivation(int index) {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java Sun Nov 26 09:05:13 2017 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java Mon Nov 27 11:20:38 2017 +0530
@@ -76,6 +76,8 @@
if (cur.isJavaThread()) {
cur.printThreadInfoOn(tty);
try {
+ int count = 0;
+
for (JavaVFrame vf = cur.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
Method method = vf.getMethod();
tty.print(" - " + method.externalNameAndSignature() +
@@ -109,6 +111,7 @@
}
tty.println(")");
+ vf.printLockInfo(tty, count++);
}
} catch (Exception e) {
tty.println("Error occurred during stack walking:");
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java Sun Nov 26 09:05:13 2017 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java Mon Nov 27 11:20:38 2017 +0530
@@ -1910,6 +1910,7 @@
buf.append(thread.getThreadState().toString());
buf.br();
buf.beginTag("pre");
+ int count = 0;
for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
Method method = vf.getMethod();
buf.append(" - ");
@@ -1954,6 +1955,19 @@
}
buf.append(")");
buf.br();
+
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ PrintStream printStream = new PrintStream(bytes);
+ try (printStream) {
+ vf.printLockInfo(printStream, count++);
+ for (String line : bytes.toString().split("\n")) {
+ if (genHTML) {
+ line = line.replace("<", "<").replace(">", ">");
+ }
+ buf.append(line);
+ buf.br();
+ }
+ }
}
buf.endTag("pre");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java Mon Nov 27 11:20:38 2017 +0530
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+import jdk.test.lib.apps.LingeredApp;
+
+
+public class LingeredAppWithLock extends LingeredApp {
+
+ public static void lockMethod(Object lock) {
+ synchronized (lock) {
+ try {
+ Thread.sleep(300000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public static void main(String args[]) {
+ Thread classLock1 = new Thread(() -> lockMethod(LingeredAppWithLock.class));
+ Thread classLock2 = new Thread(() -> lockMethod(LingeredAppWithLock.class));
+ Thread objectLock = new Thread(() -> lockMethod(classLock1));
+ Thread primitiveLock = new Thread(() -> lockMethod(int.class));
+
+ classLock1.start();
+ classLock2.start();
+ objectLock.start();
+ primitiveLock.start();
+
+ LingeredApp.main(args);
+ }
+ }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java Mon Nov 27 11:20:38 2017 +0530
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.util.List;
+import java.io.File;
+import java.io.IOException;
+import java.util.stream.Collectors;
+import java.io.OutputStream;
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Utils;
+import jdk.test.lib.Asserts;
+
+/*
+ * @test
+ * @library /test/lib
+ * @run main/othervm TestClhsdbJstackLock
+ */
+
+public class TestClhsdbJstackLock {
+
+ private static final String JSTACK_OUT_FILE = "jstack_out.txt";
+
+ private static void verifyJStackOutput() throws Exception {
+
+ Exception unexpected = null;
+ File jstackFile = new File(JSTACK_OUT_FILE);
+ Asserts.assertTrue(jstackFile.exists() && jstackFile.isFile(),
+ "File with jstack output not created: " +
+ jstackFile.getAbsolutePath());
+ try {
+ Scanner scanner = new Scanner(jstackFile);
+
+ boolean classLockOwnerFound = false;
+ boolean classLockWaiterFound = false;
+ boolean objectLockOwnerFound = false;
+ boolean primitiveLockOwnerFound = false;
+
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine();
+ System.out.println(line);
+
+ if (line.contains("missing reason for ")) {
+ unexpected = new RuntimeException("Unexpected msg: missing reason for ");
+ break;
+ }
+ if (line.matches("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Class for LingeredAppWithLock\\)$")) {
+ classLockOwnerFound = true;
+ }
+ if (line.matches("^\\s+- waiting to lock <0x[0-9a-f]+> \\(a java\\.lang\\.Class for LingeredAppWithLock\\)$")) {
+ classLockWaiterFound = true;
+ }
+ if (line.matches("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Thread\\)$")) {
+ objectLockOwnerFound = true;
+ }
+ if (line.matches("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Class for int\\)$")) {
+ primitiveLockOwnerFound = true;
+ }
+ }
+
+ if (!classLockOwnerFound || !classLockWaiterFound ||
+ !objectLockOwnerFound || !primitiveLockOwnerFound) {
+ unexpected = new RuntimeException(
+ "classLockOwnerFound = " + classLockOwnerFound +
+ ", classLockWaiterFound = " + classLockWaiterFound +
+ ", objectLockOwnerFound = " + objectLockOwnerFound +
+ ", primitiveLockOwnerFound = " + primitiveLockOwnerFound);
+ }
+ if (unexpected != null) {
+ throw unexpected;
+ }
+ } catch (Exception ex) {
+ throw new RuntimeException("Test ERROR " + ex, ex);
+ } finally {
+ jstackFile.delete();
+ }
+ }
+
+ private static void startClhsdbForLock(long lingeredAppPid) throws Exception {
+
+ Process p;
+ JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb");
+ launcher.addToolArg("clhsdb");
+ launcher.addToolArg("--pid");
+ launcher.addToolArg(Long.toString(lingeredAppPid));
+
+ ProcessBuilder pb = new ProcessBuilder();
+ pb.command(launcher.getCommand());
+ System.out.println(pb.command().stream().collect(Collectors.joining(" ")));
+
+ try {
+ p = pb.start();
+ } catch (Exception attachE) {
+ throw new Error("Couldn't start jhsdb or attach to LingeredApp : " + attachE);
+ }
+
+ // Issue the 'jstack' input at the clhsdb prompt.
+ OutputStream input = p.getOutputStream();
+ String str = "jstack > " + JSTACK_OUT_FILE + "\nquit\n";
+ try {
+ input.write(str.getBytes());
+ input.flush();
+ } catch (IOException ioe) {
+ throw new Error("Problem issuing the jstack command: " + str, ioe);
+ }
+
+ try {
+ p.waitFor();
+ } catch (InterruptedException ie) {
+ throw new Error("Problem awaiting the child process: " + ie, ie);
+ }
+
+ int exitValue = p.exitValue();
+ if (exitValue != 0) {
+ String output;
+ try {
+ output = new OutputAnalyzer(p).getOutput();
+ } catch (IOException ioe) {
+ throw new Error("Can't get failed clhsdb process output: " + ioe, ioe);
+ }
+ throw new AssertionError("clhsdb wasn't run successfully: " + output);
+ }
+ }
+
+ public static void main (String... args) throws Exception {
+
+ LingeredApp app = null;
+
+ if (!Platform.shouldSAAttach()) {
+ System.out.println("SA attach not expected to work - test skipped.");
+ return;
+ }
+
+ try {
+ List<String> vmArgs = new ArrayList<String>(Utils.getVmOptions());
+
+ app = new LingeredAppWithLock();
+ LingeredApp.startApp(vmArgs, app);
+ System.out.println ("Started LingeredApp with pid " + app.getPid());
+ startClhsdbForLock(app.getPid());
+ verifyJStackOutput();
+ } finally {
+ LingeredApp.stopApp(app);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java Mon Nov 27 11:20:38 2017 +0530
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.Utils;
+
+/*
+ * @test
+ * @library /test/lib
+ * @run main/othervm TestJhsdbJstackLock
+ */
+
+public class TestJhsdbJstackLock {
+
+ public static void main (String... args) throws Exception {
+
+ LingeredApp app = null;
+
+ if (!Platform.shouldSAAttach()) {
+ System.out.println("SA attach not expected to work - test skipped.");
+ return;
+ }
+
+ try {
+ List<String> vmArgs = new ArrayList<String>(Utils.getVmOptions());
+
+ app = new LingeredAppWithLock();
+ LingeredApp.startApp(vmArgs, app);
+ System.out.println ("Started LingeredApp with pid " + app.getPid());
+
+ JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb");
+ launcher.addToolArg("jstack");
+ launcher.addToolArg("--pid");
+ launcher.addToolArg(Long.toString(app.getPid()));
+
+ ProcessBuilder pb = new ProcessBuilder();
+ pb.command(launcher.getCommand());
+ Process jhsdb = pb.start();
+
+ jhsdb.waitFor();
+
+ OutputAnalyzer out = new OutputAnalyzer(jhsdb);
+ System.out.println(out.getStdout());
+ System.err.println(out.getStderr());
+
+ out.shouldMatch("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Class for LingeredAppWithLock\\)$");
+ out.shouldMatch("^\\s+- waiting to lock <0x[0-9a-f]+> \\(a java\\.lang\\.Class for LingeredAppWithLock\\)$");
+ out.shouldMatch("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Thread\\)$");
+ out.shouldMatch("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Class for int\\)$");
+ out.stderrShouldBeEmpty();
+
+ System.out.println("Test Completed");
+ } finally {
+ LingeredApp.stopApp(app);
+ }
+ }
+}