--- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java 2017-11-09 22:14:03.385155060 +0900 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java 2017-11-09 22:14:03.174153909 +0900 @@ -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; } --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/java_lang_Class.java 2017-11-09 22:14:03.893157829 +0900 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/java_lang_Class.java 2017-11-09 22:14:03.679156663 +0900 @@ -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 + 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(); + } + } + } --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java 2017-11-09 22:14:04.393160555 +0900 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java 2017-11-09 22:14:04.180159394 +0900 @@ -46,23 +46,23 @@ public static final int tConflict = 19; public static final int tIllegal = 99; - public static final BasicType T_BOOLEAN = new BasicType(tBoolean); - public static final BasicType T_CHAR = new BasicType(tChar); - public static final BasicType T_FLOAT = new BasicType(tFloat); - public static final BasicType T_DOUBLE = new BasicType(tDouble); - public static final BasicType T_BYTE = new BasicType(tByte); - public static final BasicType T_SHORT = new BasicType(tShort); - public static final BasicType T_INT = new BasicType(tInt); - public static final BasicType T_LONG = new BasicType(tLong); - public static final BasicType T_OBJECT = new BasicType(tObject); - public static final BasicType T_ARRAY = new BasicType(tArray); - public static final BasicType T_VOID = new BasicType(tVoid); - public static final BasicType T_ADDRESS = new BasicType(tAddress); - public static final BasicType T_NARROWOOP = new BasicType(tNarrowOop); - public static final BasicType T_METADATA = new BasicType(tMetadata); - public static final BasicType T_NARROWKLASS = new BasicType(tNarrowKlass); - public static final BasicType T_CONFLICT = new BasicType(tConflict); - public static final BasicType T_ILLEGAL = new BasicType(tIllegal); + public static final BasicType T_BOOLEAN = new BasicType(tBoolean, "boolean"); + public static final BasicType T_CHAR = new BasicType(tChar, "char"); + public static final BasicType T_FLOAT = new BasicType(tFloat, "float"); + public static final BasicType T_DOUBLE = new BasicType(tDouble, "double"); + public static final BasicType T_BYTE = new BasicType(tByte, "byte"); + public static final BasicType T_SHORT = new BasicType(tShort, "short"); + public static final BasicType T_INT = new BasicType(tInt, "int"); + public static final BasicType T_LONG = new BasicType(tLong, "long"); + public static final BasicType T_OBJECT = new BasicType(tObject, "object"); + public static final BasicType T_ARRAY = new BasicType(tArray, "array"); + public static final BasicType T_VOID = new BasicType(tVoid, "void"); + public static final BasicType T_ADDRESS = new BasicType(tAddress, "address"); + public static final BasicType T_NARROWOOP = new BasicType(tNarrowOop, "narrow oop"); + public static final BasicType T_METADATA = new BasicType(tMetadata, "metadata"); + public static final BasicType T_NARROWKLASS = new BasicType(tNarrowKlass, "narrow klass"); + public static final BasicType T_CONFLICT = new BasicType(tConflict, "conflict"); + public static final BasicType T_ILLEGAL = new BasicType(tIllegal, "ILLEGAL TYPE"); public static int getTBoolean() { return tBoolean; @@ -133,6 +133,28 @@ 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; + } + + return T_ILLEGAL; + } + public static BasicType charToBasicType(char c) { switch( c ) { case 'B': return T_BYTE; @@ -158,10 +180,16 @@ return type; } + public String getName() { + return name; + } + //-- Internals only below this point - private BasicType(int type) { + private BasicType(int type, String name) { this.type = type; + this.name = name; } private int type; + private String name; } --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java 2017-11-09 22:14:04.901163325 +0900 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java 2017-11-09 22:14:04.690162175 +0900 @@ -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 */ - public List getMonitors() { + public List getMonitors() { List monitors = getScope().getMonitors(); if (monitors == null) { - return new ArrayList(); + return new ArrayList<>(); } - List result = new ArrayList(monitors.size()); + List result = new ArrayList<>(monitors.size()); for (int i = 0; i < monitors.size(); i++) { MonitorValue mv = (MonitorValue) monitors.get(i); ScopeValue ov = mv.owner(); --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java 2017-11-09 22:14:05.403166062 +0900 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java 2017-11-09 22:14:05.191164906 +0900 @@ -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 */ - public List getMonitors() { - List result = new ArrayList(5); + public List getMonitors() { + List result = new ArrayList<>(5); for (BasicObjectLock current = getFrame().interpreterFrameMonitorEnd(); current.address().lessThan(getFrame().interpreterFrameMonitorBegin().address()); current = getFrame().nextMonitorInInterpreterFrame(current)) { --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java 2017-11-09 22:14:05.906168804 +0900 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java 2017-11-09 22:14:05.692167638 +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 */ @@ -73,22 +194,6 @@ printStackValuesOn(tty, "locals", getLocals()); printStackValuesOn(tty, "expressions", getExpressions()); - - // List - // 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) { --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java 2017-11-09 22:14:06.407171536 +0900 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java 2017-11-09 22:14:06.194170375 +0900 @@ -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:"); --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java 2017-11-09 22:14:06.907174262 +0900 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java 2017-11-09 22:14:06.694173101 +0900 @@ -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 2017-11-09 22:06:11.407765700 +0900 +++ new/test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java 2017-11-09 22:14:07.216175947 +0900 @@ -0,0 +1,54 @@ +/* + * 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 2017-11-09 22:06:11.407765700 +0900 +++ new/test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java 2017-11-09 22:14:07.735178776 +0900 @@ -0,0 +1,173 @@ +/* + * 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 vmArgs = new ArrayList(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 2017-11-09 22:06:11.407765700 +0900 +++ new/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java 2017-11-09 22:14:08.246181562 +0900 @@ -0,0 +1,87 @@ +/* + * 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 vmArgs = new ArrayList(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); + } + } +}