8324066: "clhsdb jstack" should not by default scan for j.u.c locks because it can be very slow

Reviewed-by: kevinw, amenkov
This commit is contained in:
Chris Plummer 2024-02-01 19:24:39 +00:00
parent 6b09a79d64
commit 192349eee4
10 changed files with 227 additions and 24 deletions

View File

@ -53,7 +53,7 @@ Available commands:
intConstant [ name [ value ] ] <font color="red">print out hotspot integer constant(s)</font>
jdis address <font color="red">show bytecode disassembly of a given Method*</font>
jhisto <font color="red">show Java heap histogram</font>
jstack [-v] <font color="red">show Java stack trace of all Java threads. -v is verbose mode</font>
jstack [-v] [-l] <font color="red">show Java stack trace of all Java threads. -v is verbose mode. -l includes info on owned java.util.concurrent locks.</font>
livenmethods <font color="red">show all live nmethods</font>
longConstant [ name [ value ] ] <font color="red">print out hotspot long constant(s)s</font>
mem [ -v ] { address[/count] | address,address } <font color="red">show contents of memory range. -v adds "findpc" info for addresses</font>
@ -62,7 +62,7 @@ Available commands:
printas type expression <font color="red">print given address as given HotSpot type. eg. print JavaThread &lt;address&gt;</font>
printmdo -a | expression <font color="red">print method data oop</font>
printstatics [ type ] <font color="red">print static fields of given HotSpot type (or all types if none specified)</font>
pstack [-v] <font color="red">show mixed mode stack trace for all Java, non-Java threads. -v is verbose mode</font>
pstack [-v] [-l] <font color="red">show mixed mode stack trace for all Java, non-Java threads. -v is verbose mode. -l includes info on owned java.util.concurrent locks.</font>
quit <font color="red">quit CLHSDB tool</font>
reattach <font color="red">detach and re-attach SA to current target</font>
revptrs <font color="red">find liveness of oops</font>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2024, 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
@ -1143,13 +1143,22 @@ public class CommandProcessor {
histo.run(out, err);
}
},
new Command("jstack", "jstack [-v]", false) {
new Command("jstack", "jstack [-v] [-l]", false) {
public void doit(Tokens t) {
boolean verbose = false;
if (t.countTokens() > 0 && t.nextToken().equals("-v")) {
verbose = true;
boolean concurrentLocks = false;
while (t.countTokens() > 0) {
String arg = t.nextToken();
if (arg.equals("-v")) {
verbose = true;
} else if (arg.equals("-l")) {
concurrentLocks = true;
} else {
usage();
return;
}
}
StackTrace jstack = new StackTrace(verbose, true);
StackTrace jstack = new StackTrace(verbose, concurrentLocks);
jstack.run(out);
}
},
@ -1201,13 +1210,22 @@ public class CommandProcessor {
pmap.run(out, debugger.getAgent().getDebugger());
}
},
new Command("pstack", "pstack [-v]", false) {
new Command("pstack", "pstack [-v] [-l]", false) {
public void doit(Tokens t) {
boolean verbose = false;
if (t.countTokens() > 0 && t.nextToken().equals("-v")) {
verbose = true;
boolean concurrentLocks = false;
while (t.countTokens() > 0) {
String arg = t.nextToken();
if (arg.equals("-v")) {
verbose = true;
} else if (arg.equals("-l")) {
concurrentLocks = true;
} else {
usage();
return;
}
}
PStack pstack = new PStack(verbose, true, debugger.getAgent());
PStack pstack = new PStack(verbose, concurrentLocks, debugger.getAgent());
pstack.run(out, debugger.getAgent().getDebugger());
}
},

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2024, 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
@ -31,12 +31,14 @@ import sun.jvm.hotspot.oops.*;
public class ConcurrentLocksPrinter {
private final Map<JavaThread, List<Oop>> locksMap = new HashMap<>();
private PrintStream tty;
public ConcurrentLocksPrinter() {
public ConcurrentLocksPrinter(PrintStream tty) {
this.tty = tty;
fillLocks();
}
public void print(JavaThread jthread, PrintStream tty) {
public void print(JavaThread jthread) {
List<Oop> locks = locksMap.get(jthread);
tty.println("Locked ownable synchronizers:");
if (locks == null || locks.isEmpty()) {
@ -66,6 +68,7 @@ public class ConcurrentLocksPrinter {
ObjectHeap heap = vm.getObjectHeap();
// may be not loaded at all
if (absOwnSyncKlass != null) {
tty.println("Finding concurrent locks. This might take a while...");
heap.iterateObjectsOfKlass(new DefaultHeapVisitor() {
public boolean doObj(Oop oop) {
JavaThread thread = getOwnerThread(oop);
@ -77,6 +80,7 @@ public class ConcurrentLocksPrinter {
}
}, absOwnSyncKlass, true);
tty.println();
}
}
}

View File

@ -72,7 +72,7 @@ public class PStack extends Tool {
// compute and cache java Vframes.
initJFrameCache();
if (concurrentLocks) {
concLocksPrinter = new ConcurrentLocksPrinter();
concLocksPrinter = new ConcurrentLocksPrinter(out);
}
// print Java level deadlocks
try {
@ -192,7 +192,7 @@ public class PStack extends Tool {
if (concurrentLocks) {
JavaThread jthread = proxyToThread.get(th);
if (jthread != null) {
concLocksPrinter.print(jthread, out);
concLocksPrinter.print(jthread);
}
}
} // for threads

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2024, 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
@ -68,7 +68,7 @@ public class StackTrace extends Tool {
try {
ConcurrentLocksPrinter concLocksPrinter = null;
if (concurrentLocks) {
concLocksPrinter = new ConcurrentLocksPrinter();
concLocksPrinter = new ConcurrentLocksPrinter(tty);
}
Threads threads = VM.getVM().getThreads();
for (int i = 0; i < threads.getNumberOfThreads(); i++) {
@ -123,9 +123,9 @@ public class StackTrace extends Tool {
}
tty.println();
if (concurrentLocks) {
concLocksPrinter.print(cur, tty);
concLocksPrinter.print(cur);
tty.println();
}
tty.println();
}
}
}

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2019, 2024, 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
@ -51,6 +51,7 @@ serviceability/sa/ClhsdbJdis.java 8307393 generic-
serviceability/sa/ClhsdbJhisto.java 8307393 generic-all
serviceability/sa/ClhsdbJstack.java#id0 8307393 generic-all
serviceability/sa/ClhsdbJstack.java#id1 8307393 generic-all
serviceability/sa/ClhsdbJstackWithConcurrentLock.java 8307393 generic-all
serviceability/sa/ClhsdbJstackXcompStress.java 8307393 generic-all
serviceability/sa/ClhsdbLauncher.java 8307393 generic-all
serviceability/sa/ClhsdbLongConstant.java 8307393 generic-all

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2019, 2024, 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
@ -30,6 +30,7 @@
resourcehogs/serviceability/sa/TestHeapDumpForLargeArray.java 8276539 generic-all
serviceability/sa/CDSJMapClstats.java 8276539 generic-all
serviceability/sa/ClhsdbJhisto.java 8276539 generic-all
serviceability/sa/ClhsdbJstackWithConcurrentLock.java 8276539 generic-all
serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java 8276539 generic-all
serviceability/sa/ClhsdbFindPC.java#xcomp-core 8284045 generic-all

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2024, 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.
*/
/**
* @test
* @bug 8324066
* @summary Test the clhsdb 'jstack -l' command for printing concurrent lock information
* @requires vm.hasSA
* @library /test/lib
* @run main/othervm ClhsdbJstackWithConcurrentLock
*/
import java.util.List;
import jdk.test.lib.apps.LingeredApp;
import jtreg.SkippedException;
public class ClhsdbJstackWithConcurrentLock {
public static void main(String[] args) throws Exception {
System.out.println("Starting the ClhsdbJstackWithConcurrentLock test");
LingeredApp theApp = null;
try {
ClhsdbLauncher test = new ClhsdbLauncher();
theApp = new LingeredAppWithConcurrentLock();
// Use a small heap so the scan is quick.
LingeredApp.startApp(theApp, "-Xmx4m");
System.out.println("Started LingeredApp with pid " + theApp.getPid());
// Run the 'jstack -l' command to get the stack and have java.util.concurrent
// lock information included.
List<String> cmds = List.of("jstack -l");
String jstackOutput = test.run(theApp.getPid(), cmds, null, null);
// We are looking for:
// Locked ownable synchronizers:
// - <0x00000000ffc2ed70>, (a java/util/concurrent/locks/ReentrantLock$NonfairSync)
// We want to fetch the address from this line.
String key = ", (a java/util/concurrent/locks/ReentrantLock$NonfairSync)";
String[] lines = jstackOutput.split("\\R");
String addressString = null;
for (String line : lines) {
if (line.contains(key)) {
String[] words = line.split("[, ]");
for (String word : words) {
word = word.replace("<", "").replace(">", "");
if (word.startsWith("0x")) {
addressString = word;
break;
}
}
if (addressString != null)
break;
}
}
if (addressString == null) {
throw new RuntimeException("Token '" + key + "' not found in jstack output");
}
// We are looking for the following java frame:
// - jdk.internal.misc.Unsafe.park(boolean, long)...
// - parking to wait for <0x00000000ffc2ed70> (a java/util/concurrent/locks/ReentrantLock$NonfairSync)
// Note the address matches the one we found above.
key = "- parking to wait for <" + addressString +
"> (a java/util/concurrent/locks/ReentrantLock$NonfairSync)";
boolean found = false;
for (String line : lines) {
if (line.contains(key)) {
found = true;
break;
}
}
if (!found) {
throw new RuntimeException("Token '" + key + "' not found in jstack output");
}
} catch (SkippedException e) {
throw e;
} catch (Exception ex) {
throw new RuntimeException("Test ERROR " + ex, ex);
} finally {
LingeredApp.stopApp(theApp);
System.out.println("OUTPUT: " + theApp.getOutput());
}
System.out.println("Test PASSED");
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2024, 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;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LingeredAppWithConcurrentLock extends LingeredApp {
private static final Lock lock = new ReentrantLock();
public static void lockMethod(Lock lock) {
lock.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(lock));
Thread classLock2 = new Thread(() -> lockMethod(lock));
Thread classLock3 = new Thread(() -> lockMethod(lock));
classLock1.start();
classLock2.start();
classLock3.start();
// Wait until all threads have reached their blocked or timed wait state
while ((classLock1.getState() != Thread.State.WAITING &&
classLock1.getState() != Thread.State.TIMED_WAITING) ||
(classLock2.getState() != Thread.State.WAITING &&
classLock2.getState() != Thread.State.TIMED_WAITING) ||
(classLock3.getState() != Thread.State.WAITING &&
classLock3.getState() != Thread.State.TIMED_WAITING)) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
}
}
System.out.println("classLock1 state: " + classLock1.getState());
System.out.println("classLock2 state: " + classLock2.getState());
System.out.println("classLock3 state: " + classLock3.getState());
LingeredApp.main(args);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, 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
@ -79,7 +79,7 @@ import jdk.test.lib.util.CoreUtils;
*
* After app termination (stopApp/waitAppTermination) its output is available
*
* output = a.getAppOutput();
* output = a.getOutput();
*
*/
public class LingeredApp {