8185796: jstack and clhsdb jstack should show lock objects
Modifications to display monitor details with SA jstack Reviewed-by: sspitsyn, jgeorge
This commit is contained in:
parent
fa19052aa4
commit
627a32d672
@ -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 class Oop {
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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 @@ public class java_lang_Class {
|
||||
|
||||
// java.lang.Class fields
|
||||
static int klassOffset;
|
||||
static int arrayKlassOffset;
|
||||
static IntField oopSizeField;
|
||||
|
||||
static {
|
||||
@ -56,6 +57,7 @@ public class java_lang_Class {
|
||||
// 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 class java_lang_Class {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -133,6 +133,27 @@ public class BasicType {
|
||||
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 @@ public class BasicType {
|
||||
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;
|
||||
|
@ -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 @@ public class CompiledVFrame extends JavaVFrame {
|
||||
}
|
||||
|
||||
/** 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();
|
||||
|
@ -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 @@ public class InterpretedVFrame extends JavaVFrame {
|
||||
}
|
||||
|
||||
/** 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)) {
|
||||
|
@ -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.io.*;
|
||||
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 @@ public abstract class JavaVFrame extends VFrame {
|
||||
// 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 @@ public abstract class JavaVFrame extends VFrame {
|
||||
|
||||
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) {
|
||||
|
@ -76,6 +76,8 @@ public class StackTrace extends Tool {
|
||||
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 @@ public class StackTrace extends Tool {
|
||||
}
|
||||
|
||||
tty.println(")");
|
||||
vf.printLockInfo(tty, count++);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
tty.println("Error occurred during stack walking:");
|
||||
|
@ -1910,6 +1910,7 @@ public class HTMLGenerator implements /* imports */ ClassConstants {
|
||||
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 @@ public class HTMLGenerator implements /* imports */ ClassConstants {
|
||||
}
|
||||
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");
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
171
test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java
Normal file
171
test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user