From 47e090a45a5381643cc910ed38870bc1340fe2ac Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 29 Jul 2026 20:42:05 +0000 Subject: [PATCH] 8253442: bigapps SIGSEGV in DeadlockCycle::print_on_with --- src/hotspot/share/services/threadService.cpp | 8 +- .../dcmd/thread/PrintRawMonitorLockTest.java | 155 ++++++++++++++++++ .../thread/libPrintRawMonitorLockTest.cpp | 88 ++++++++++ 3 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/dcmd/thread/PrintRawMonitorLockTest.java create mode 100644 test/hotspot/jtreg/serviceability/dcmd/thread/libPrintRawMonitorLockTest.cpp diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index cab6c6a4f27..dd854b0f52e 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -517,6 +517,7 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(ThreadsList * t_list, } previousThread = currentThread; waitingToLockMonitor = (ObjectMonitor*)currentThread->current_pending_monitor(); + waitingToLockRawMonitor = currentThread->current_pending_raw_monitor(); if (concurrent_locks) { waitingToLockBlocker = currentThread->current_park_blocker(); } @@ -1050,7 +1051,8 @@ void DeadlockCycle::print_on_with(ThreadsList * t_list, outputStream* st) const waitingToLockMonitor->owner()); continue; } - } else { + st->print_cr("%s \"%s\"", owner_desc, currentThread->name()); + } else if (waitingToLockBlocker != nullptr) { st->print(" waiting for ownable synchronizer " INTPTR_FORMAT ", (a %s)", p2i(waitingToLockBlocker), waitingToLockBlocker->klass()->external_name()); @@ -1059,8 +1061,10 @@ void DeadlockCycle::print_on_with(ThreadsList * t_list, outputStream* st) const oop ownerObj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(waitingToLockBlocker); currentThread = java_lang_Thread::thread(ownerObj); assert(currentThread != nullptr, "AbstractOwnableSynchronizer owning thread is unexpectedly null"); + st->print_cr("%s \"%s\"", owner_desc, currentThread->name()); + } else { + assert(waitingToLockRawMonitor != nullptr, "some deadlock must have been found"); } - st->print_cr("%s \"%s\"", owner_desc, currentThread->name()); } st->cr(); diff --git a/test/hotspot/jtreg/serviceability/dcmd/thread/PrintRawMonitorLockTest.java b/test/hotspot/jtreg/serviceability/dcmd/thread/PrintRawMonitorLockTest.java new file mode 100644 index 00000000000..a2d99745ce6 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/dcmd/thread/PrintRawMonitorLockTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2026, 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 org.testng.SkipException; +import org.testng.annotations.Test; +import org.testng.Assert; + +import jdk.test.lib.process.OutputAnalyzer; + +import jdk.test.lib.dcmd.CommandExecutor; +import jdk.test.lib.dcmd.JMXExecutor; + +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.locks.ReentrantLock; +import java.util.regex.Pattern; + +/* + * @test + * @summary Test of diagnostic command Thread.print with only JVMTI raw monitor. + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.compiler + * java.management + * jdk.internal.jvmstat/sun.jvmstat.monitor + * @requires vm.jvmti + * @run testng/othervm/native -agentlib:PrintRawMonitorLockTest PrintRawMonitorLockTest + */ +public class PrintRawMonitorLockTest { + + static private void log(String s) { System.out.println(s); } + static private String AGENT_LIB = "PrintRawMonitorLockTest";; + + native static int createRawMonitors(); + native static int rawMonitorEnter(int id); + + static { + try { + System.loadLibrary(AGENT_LIB); + log("Loaded library: " + AGENT_LIB); + } catch (UnsatisfiedLinkError ule) { + log("Failed to load library: " + AGENT_LIB); + log("java.library.path: " + System.getProperty("java.library.path")); + throw ule; + } + } + + CyclicBarrier readyBarrier = new CyclicBarrier(3); + + private void waitForBarrier(CyclicBarrier b) { + try { + b.await(); + } catch (InterruptedException | BrokenBarrierException e) { + Assert.fail("Test error: Caught unexpected exception:", e); + } + } + + class RawMonitorThread extends Thread { + int id; + int otherid; + + RawMonitorThread(int id, int otherid) { + this.id = id; + this.otherid = otherid; + setDaemon(true); + } + + public void run() { + int retCode = rawMonitorEnter(id); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI RawMonitorEnter: " + + "retCode=" + retCode); + } + log("entered lock1"); + + /* Hold lock on "lock" to show up in thread dump */ + /* Signal that we're ready for thread dump */ + waitForBarrier(readyBarrier); + + retCode = rawMonitorEnter(otherid); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI RawMonitorEnter: " + + "retCode=" + retCode); + } + log("tried to enter lock2"); + } + } + + private void setupRawMonitors() { + int retCode = createRawMonitors(); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI CreateRawMonitor: " + + "retCode=" + retCode); + } + log("created threadLock"); + + } + + public void run(CommandExecutor executor) { + setupRawMonitors(); + + RawMonitorThread aThread = new RawMonitorThread(1, 2); + aThread.start(); + + RawMonitorThread bThread = new RawMonitorThread(2, 1); + bThread.start(); + + /* Wait for threads to get ready */ + waitForBarrier(readyBarrier); + + OutputAnalyzer output; + + while (true) { + /* Execute */ + try { + Thread.sleep(200); + } catch (InterruptedException ie) { + } + output = executor.execute("Thread.print -l=true"); + if (output.getOutput().contains("Found 1 deadlock")) { + break; + } + } + + output.shouldContain("waiting to lock JVM TI raw monitor"); + } + + @Test + public void jmx() { + if (Thread.currentThread().isVirtual()) { + throw new SkipException("skipping test since current thread is virtual thread"); + } + run(new JMXExecutor()); + } +} diff --git a/test/hotspot/jtreg/serviceability/dcmd/thread/libPrintRawMonitorLockTest.cpp b/test/hotspot/jtreg/serviceability/dcmd/thread/libPrintRawMonitorLockTest.cpp new file mode 100644 index 00000000000..34adc860471 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/dcmd/thread/libPrintRawMonitorLockTest.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2026, 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. + */ + +#include +#include "jvmti.h" + +extern "C" { + +static jvmtiEnv* jvmti = nullptr; +static jrawMonitorID threadLock1 = nullptr; +static char threadLockName1[] = "threadLock1"; + +static jrawMonitorID threadLock2 = nullptr; +static char threadLockName2[] = "threadLock2"; + +#define LOG(...) \ + do { \ + printf(__VA_ARGS__); \ + printf("\n"); \ + fflush(stdout); \ + } while (0) + +JNIEXPORT jint JNICALL +Java_PrintRawMonitorLockTest_createRawMonitors(JNIEnv *jni, jclass cls) { + if (jvmti->CreateRawMonitor(threadLockName1, &threadLock1) != JNI_OK) { + return JNI_ERR; + } + return jvmti->CreateRawMonitor(threadLockName2, &threadLock2); +} + +JNIEXPORT jint JNICALL +Java_PrintRawMonitorLockTest_rawMonitorEnter(JNIEnv *jni, jclass cls, int id) { + if (id == 1) { + return jvmti->RawMonitorEnter(threadLock1); + } else if (id == 2) { + return jvmti->RawMonitorEnter(threadLock2); + } else { + return JNI_ERR; + } +} + +/** Agent library initialization. */ + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + LOG("\nAgent_OnLoad started"); + + // create JVMTI environment + if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) { + return JNI_ERR; + } + +#if 0 + // add specific capabilities for suspending thread + jvmtiCapabilities suspendCaps; + memset(&suspendCaps, 0, sizeof(suspendCaps)); + suspendCaps.can_suspend = 1; + + jvmtiError err = jvmti->AddCapabilities(&suspendCaps); + if (err != JVMTI_ERROR_NONE) { + return JNI_ERR; + } +#endif + LOG("Agent_OnLoad finished\n"); + return JNI_OK; +} + +}