From 192349eee4b6d50f16d44969eb882875c67d651d Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Thu, 1 Feb 2024 19:24:39 +0000 Subject: [PATCH] 8324066: "clhsdb jstack" should not by default scan for j.u.c locks because it can be very slow Reviewed-by: kevinw, amenkov --- src/jdk.hotspot.agent/doc/clhsdb.html | 4 +- .../sun/jvm/hotspot/CommandProcessor.java | 36 ++++-- .../runtime/ConcurrentLocksPrinter.java | 10 +- .../classes/sun/jvm/hotspot/tools/PStack.java | 4 +- .../sun/jvm/hotspot/tools/StackTrace.java | 8 +- .../jtreg/ProblemList-generational-zgc.txt | 3 +- test/hotspot/jtreg/ProblemList-zgc.txt | 3 +- .../sa/ClhsdbJstackWithConcurrentLock.java | 107 ++++++++++++++++++ .../sa/LingeredAppWithConcurrentLock.java | 72 ++++++++++++ test/lib/jdk/test/lib/apps/LingeredApp.java | 4 +- 10 files changed, 227 insertions(+), 24 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/sa/ClhsdbJstackWithConcurrentLock.java create mode 100644 test/hotspot/jtreg/serviceability/sa/LingeredAppWithConcurrentLock.java diff --git a/src/jdk.hotspot.agent/doc/clhsdb.html b/src/jdk.hotspot.agent/doc/clhsdb.html index bff7b39017a..69fd228a52e 100644 --- a/src/jdk.hotspot.agent/doc/clhsdb.html +++ b/src/jdk.hotspot.agent/doc/clhsdb.html @@ -53,7 +53,7 @@ Available commands: intConstant [ name [ value ] ] print out hotspot integer constant(s) jdis address show bytecode disassembly of a given Method* jhisto show Java heap histogram - jstack [-v] show Java stack trace of all Java threads. -v is verbose mode + jstack [-v] [-l] show Java stack trace of all Java threads. -v is verbose mode. -l includes info on owned java.util.concurrent locks. livenmethods show all live nmethods longConstant [ name [ value ] ] print out hotspot long constant(s)s mem [ -v ] { address[/count] | address,address } show contents of memory range. -v adds "findpc" info for addresses @@ -62,7 +62,7 @@ Available commands: printas type expression print given address as given HotSpot type. eg. print JavaThread <address> printmdo -a | expression print method data oop printstatics [ type ] print static fields of given HotSpot type (or all types if none specified) - pstack [-v] show mixed mode stack trace for all Java, non-Java threads. -v is verbose mode + pstack [-v] [-l] show mixed mode stack trace for all Java, non-Java threads. -v is verbose mode. -l includes info on owned java.util.concurrent locks. quit quit CLHSDB tool reattach detach and re-attach SA to current target revptrs find liveness of oops diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java index f0c0a6c5a79..79709cce132 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java @@ -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()); } }, diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ConcurrentLocksPrinter.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ConcurrentLocksPrinter.java index 8317bd46173..fa28a96e333 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ConcurrentLocksPrinter.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ConcurrentLocksPrinter.java @@ -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> 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 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(); } } } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java index 0978affb516..038c871a9f9 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java @@ -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 diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java index 98ca8fd82d9..2dfdd1ec5a9 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java @@ -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(); } } } diff --git a/test/hotspot/jtreg/ProblemList-generational-zgc.txt b/test/hotspot/jtreg/ProblemList-generational-zgc.txt index b5c35f2cec2..cbe76d68dff 100644 --- a/test/hotspot/jtreg/ProblemList-generational-zgc.txt +++ b/test/hotspot/jtreg/ProblemList-generational-zgc.txt @@ -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 diff --git a/test/hotspot/jtreg/ProblemList-zgc.txt b/test/hotspot/jtreg/ProblemList-zgc.txt index f648cc136e2..b818359a628 100644 --- a/test/hotspot/jtreg/ProblemList-zgc.txt +++ b/test/hotspot/jtreg/ProblemList-zgc.txt @@ -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 diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbJstackWithConcurrentLock.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbJstackWithConcurrentLock.java new file mode 100644 index 00000000000..ca134ffa4ac --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbJstackWithConcurrentLock.java @@ -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 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"); + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/LingeredAppWithConcurrentLock.java b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithConcurrentLock.java new file mode 100644 index 00000000000..cf0e9a48a99 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithConcurrentLock.java @@ -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); + } + } diff --git a/test/lib/jdk/test/lib/apps/LingeredApp.java b/test/lib/jdk/test/lib/apps/LingeredApp.java index b519083999e..d0b97b695d0 100644 --- a/test/lib/jdk/test/lib/apps/LingeredApp.java +++ b/test/lib/jdk/test/lib/apps/LingeredApp.java @@ -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 {