8343741: SA jstack --mixed should print information about VM locks

Reviewed-by: cjplummer
This commit is contained in:
Leonid Mesnik 2024-11-22 19:21:09 +00:00
parent 1b2d9cad53
commit 98b667834c
19 changed files with 424 additions and 79 deletions

View File

@ -186,6 +186,16 @@ WB_ENTRY(jstring, WB_PrintString(JNIEnv* env, jobject wb, jstring str, jint max_
return (jstring) JNIHandles::make_local(THREAD, result);
WB_END
WB_ENTRY(jint, WB_TakeLockAndHangInSafepoint(JNIEnv* env, jobject wb))
JavaThread* self = JavaThread::current();
// VMStatistic_lock is used to minimize interference with VM locking
MutexLocker mu(VMStatistic_lock);
VM_HangInSafepoint force_safepoint_stuck_op;
VMThread::execute(&force_safepoint_stuck_op);
ShouldNotReachHere();
return 0;
WB_END
class WBIsKlassAliveClosure : public LockedClassesDo {
Symbol* _name;
int _count;
@ -2988,6 +2998,7 @@ static JNINativeMethod methods[] = {
{CC"cleanMetaspaces", CC"()V", (void*)&WB_CleanMetaspaces},
{CC"rss", CC"()J", (void*)&WB_Rss},
{CC"printString", CC"(Ljava/lang/String;I)Ljava/lang/String;", (void*)&WB_PrintString},
{CC"lockAndStuckInSafepoint", CC"()V", (void*)&WB_TakeLockAndHangInSafepoint},
{CC"wordSize", CC"()J", (void*)&WB_WordSize},
{CC"rootChunkWordSize", CC"()J", (void*)&WB_RootChunkWordSize}
};

View File

@ -267,6 +267,16 @@ bool Monitor::wait(uint64_t timeout) {
return wait_status != 0; // return true IFF timeout
}
static const int MAX_NUM_MUTEX = 1204;
static Mutex* _internal_mutex_arr[MAX_NUM_MUTEX];
Mutex** Mutex::_mutex_array = _internal_mutex_arr;
int Mutex::_num_mutex = 0;
void Mutex::add_mutex(Mutex* var) {
assert(Mutex::_num_mutex < MAX_NUM_MUTEX, "increase MAX_NUM_MUTEX");
Mutex::_mutex_array[_num_mutex++] = var;
}
Mutex::~Mutex() {
assert_owner(nullptr);
os::free(const_cast<char*>(_name));
@ -524,6 +534,61 @@ void Mutex::set_owner_implementation(Thread *new_owner) {
}
#endif // ASSERT
// Print all mutexes/monitors that are currently owned by a thread; called
// by fatal error handler.
void Mutex::print_owned_locks_on_error(outputStream* st) {
st->print("VM Mutex/Monitor currently owned by a thread: ");
bool none = true;
for (int i = 0; i < _num_mutex; i++) {
// see if it has an owner
if (_mutex_array[i]->owner() != nullptr) {
if (none) {
// print format used by Mutex::print_on_error()
st->print_cr(" ([mutex/lock_event])");
none = false;
}
_mutex_array[i]->print_on_error(st);
st->cr();
}
}
if (none) st->print_cr("None");
}
void Mutex::print_lock_ranks(outputStream* st) {
st->print_cr("VM Mutex/Monitor ranks: ");
#ifdef ASSERT
// Be extra defensive and figure out the bounds on
// ranks right here. This also saves a bit of time
// in the #ranks*#mutexes loop below.
int min_rank = INT_MAX;
int max_rank = INT_MIN;
for (int i = 0; i < _num_mutex; i++) {
Mutex* m = _mutex_array[i];
int r = (int) m->rank();
if (min_rank > r) min_rank = r;
if (max_rank < r) max_rank = r;
}
// Print the listings rank by rank
for (int r = min_rank; r <= max_rank; r++) {
bool first = true;
for (int i = 0; i < _num_mutex; i++) {
Mutex* m = _mutex_array[i];
if (r != (int) m->rank()) continue;
if (first) {
st->cr();
st->print_cr("Rank \"%s\":", m->rank_name());
first = false;
}
st->print_cr(" %s", m->name());
}
}
#else
st->print_cr(" Only known in debug builds.");
#endif // ASSERT
}
RecursiveMutex::RecursiveMutex() : _sem(1), _owner(nullptr), _recursions(0) {}

View File

@ -51,6 +51,7 @@
class Mutex : public CHeapObj<mtSynchronizer> {
friend class VMStructs;
public:
// Special low level locks are given names and ranges avoid overlap.
enum class Rank {
@ -103,6 +104,9 @@ class Mutex : public CHeapObj<mtSynchronizer> {
#ifndef PRODUCT
bool _allow_vm_block;
#endif
static Mutex** _mutex_array;
static int _num_mutex;
#ifdef ASSERT
Rank _rank; // rank (to avoid/detect potential deadlocks)
Mutex* _next; // Used by a Thread to link up owned locks
@ -194,11 +198,18 @@ class Mutex : public CHeapObj<mtSynchronizer> {
const char *name() const { return _name; }
static void add_mutex(Mutex* var);
void print_on_error(outputStream* st) const;
#ifndef PRODUCT
void print_on(outputStream* st) const;
void print() const;
#endif
// Print all mutexes/monitors that are currently owned by a thread; called
// by fatal error handler.
static void print_owned_locks_on_error(outputStream* st);
static void print_lock_ranks(outputStream* st);
};
class Monitor : public Mutex {

View File

@ -158,10 +158,6 @@ Monitor* JVMCIRuntime_lock = nullptr;
// Only one RecursiveMutex
RecursiveMutex* MultiArray_lock = nullptr;
#define MAX_NUM_MUTEX 128
static Mutex* _mutex_array[MAX_NUM_MUTEX];
static int _num_mutex;
#ifdef ASSERT
void assert_locked_or_safepoint(const Mutex* lock) {
if (DebuggingContext::is_enabled() || VMError::is_error_reported()) return;
@ -182,18 +178,13 @@ void assert_lock_strong(const Mutex* lock) {
}
#endif
static void add_mutex(Mutex* var) {
assert(_num_mutex < MAX_NUM_MUTEX, "increase MAX_NUM_MUTEX");
_mutex_array[_num_mutex++] = var;
}
#define MUTEX_STORAGE_NAME(name) name##_storage
#define MUTEX_STORAGE(name, type) alignas(type) static uint8_t MUTEX_STORAGE_NAME(name)[sizeof(type)]
#define MUTEX_DEF(name, type, pri, ...) { \
assert(name == nullptr, "Mutex/Monitor initialized twice"); \
MUTEX_STORAGE(name, type); \
name = ::new(static_cast<void*>(MUTEX_STORAGE_NAME(name))) type((pri), #name, ##__VA_ARGS__); \
add_mutex(name); \
Mutex::add_mutex(name); \
}
#define MUTEX_DEFN(name, type, pri, ...) MUTEX_DEF(name, type, Mutex::pri, ##__VA_ARGS__)
@ -371,7 +362,7 @@ void MutexLockerImpl::post_initialize() {
if (lt.is_enabled()) {
ResourceMark rm;
LogStream ls(lt);
print_lock_ranks(&ls);
Mutex::print_lock_ranks(&ls);
}
}
@ -385,58 +376,3 @@ GCMutexLocker::GCMutexLocker(Mutex* mutex) {
}
}
// Print all mutexes/monitors that are currently owned by a thread; called
// by fatal error handler.
void print_owned_locks_on_error(outputStream* st) {
st->print("VM Mutex/Monitor currently owned by a thread: ");
bool none = true;
for (int i = 0; i < _num_mutex; i++) {
// see if it has an owner
if (_mutex_array[i]->owner() != nullptr) {
if (none) {
// print format used by Mutex::print_on_error()
st->print_cr(" ([mutex/lock_event])");
none = false;
}
_mutex_array[i]->print_on_error(st);
st->cr();
}
}
if (none) st->print_cr("None");
}
void print_lock_ranks(outputStream* st) {
st->print_cr("VM Mutex/Monitor ranks: ");
#ifdef ASSERT
// Be extra defensive and figure out the bounds on
// ranks right here. This also saves a bit of time
// in the #ranks*#mutexes loop below.
int min_rank = INT_MAX;
int max_rank = INT_MIN;
for (int i = 0; i < _num_mutex; i++) {
Mutex* m = _mutex_array[i];
int r = (int) m->rank();
if (min_rank > r) min_rank = r;
if (max_rank < r) max_rank = r;
}
// Print the listings rank by rank
for (int r = min_rank; r <= max_rank; r++) {
bool first = true;
for (int i = 0; i < _num_mutex; i++) {
Mutex* m = _mutex_array[i];
if (r != (int) m->rank()) continue;
if (first) {
st->cr();
st->print_cr("Rank \"%s\":", m->rank_name());
first = false;
}
st->print_cr(" %s", m->name());
}
}
#else
st->print_cr(" Only known in debug builds.");
#endif // ASSERT
}

View File

@ -171,11 +171,6 @@ extern Mutex* tty_lock; // lock to synchronize output
// order. If their implementations change such that these assumptions
// are violated, a whole lot of code will break.
// Print all mutexes/monitors that are currently owned by a thread; called
// by fatal error handler.
void print_owned_locks_on_error(outputStream* st);
void print_lock_ranks(outputStream* st);
// for debugging: check that we're already owning this lock (or are at a safepoint / handshake)
#ifdef ASSERT
void assert_locked_or_safepoint(const Mutex* lock);

View File

@ -60,6 +60,18 @@ class VM_ForceSafepoint: public VM_EmptyOperation {
VMOp_Type type() const { return VMOp_ForceSafepoint; }
};
// used by whitebox API to emulate VM issues
// when VM can't operate and doesn't respond to jcmd
class VM_HangInSafepoint: public VM_Operation {
public:
VMOp_Type type() const { return VMOp_ForceSafepoint; }
void doit() {
while(true) {
os::naked_short_sleep(10);
}
}
};
class VM_ClearICs: public VM_Operation {
private:
bool _preserve_static_stubs;

View File

@ -659,7 +659,6 @@
volatile_nonstatic_field(JavaThread, _is_method_handle_return, int) \
nonstatic_field(JavaThread, _saved_exception_pc, address) \
volatile_nonstatic_field(JavaThread, _thread_state, JavaThreadState) \
nonstatic_field(JavaThread, _osthread, OSThread*) \
nonstatic_field(JavaThread, _stack_base, address) \
nonstatic_field(JavaThread, _stack_size, size_t) \
nonstatic_field(JavaThread, _vframe_array_head, vframeArray*) \
@ -667,6 +666,7 @@
nonstatic_field(JavaThread, _active_handles, JNIHandleBlock*) \
nonstatic_field(JavaThread, _lock_id, int64_t) \
volatile_nonstatic_field(JavaThread, _terminated, JavaThread::TerminatedTypes) \
nonstatic_field(Thread, _osthread, OSThread*) \
nonstatic_field(Thread, _resource_area, ResourceArea*) \
nonstatic_field(CompilerThread, _env, ciEnv*) \
\
@ -1022,7 +1022,12 @@
nonstatic_field(elapsedTimer, _active, bool) \
nonstatic_field(InvocationCounter, _counter, unsigned int) \
\
nonstatic_field(UpcallStub::FrameData, jfa, JavaFrameAnchor)
nonstatic_field(UpcallStub::FrameData, jfa, JavaFrameAnchor) \
\
nonstatic_field(Mutex, _name, const char*) \
static_field(Mutex, _mutex_array, Mutex**) \
static_field(Mutex, _num_mutex, int) \
volatile_nonstatic_field(Mutex, _owner, Thread*)
//--------------------------------------------------------------------------------
// VM_TYPES
@ -1936,6 +1941,7 @@
declare_toplevel_type(JNIid) \
declare_toplevel_type(JNIid*) \
declare_toplevel_type(jmethodID*) \
declare_toplevel_type(Mutex) \
declare_toplevel_type(Mutex*) \
declare_toplevel_type(nmethod*) \
COMPILER2_PRESENT(declare_unsigned_integer_type(node_idx_t)) \

View File

@ -1223,7 +1223,7 @@ void VMError::report(outputStream* st, bool _verbose) {
STEP_IF("printing owned locks on error", _verbose)
// mutexes/monitors that currently have an owner
print_owned_locks_on_error(st);
Mutex::print_owned_locks_on_error(st);
st->cr();
STEP_IF("printing number of OutOfMemoryError and StackOverflow exceptions",

View File

@ -0,0 +1,75 @@
/*
* 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.
*
*/
package sun.jvm.hotspot.runtime;
import sun.jvm.hotspot.debugger.Address;
import sun.jvm.hotspot.types.AddressField;
import sun.jvm.hotspot.types.Type;
import sun.jvm.hotspot.types.TypeDataBase;
import sun.jvm.hotspot.types.WrongTypeException;
import sun.jvm.hotspot.utilities.*;
public class Mutex extends VMObject {
private static long nameFieldOffset;
private static long ownerFieldOffset;
private static AddressField mutex_array;
private static int maxNum;
private static final long addrSize = VM.getVM().getAddressSize();
static {
VM.registerVMInitializedObserver(new Observer() {
public void update(Observable o, Object data) {
initialize(VM.getVM().getTypeDataBase());
}
});
}
private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
Type type = db.lookupType("Mutex");
sun.jvm.hotspot.types.Field nameField = type.getField("_name");
nameFieldOffset = nameField.getOffset();
sun.jvm.hotspot.types.Field ownerField = type.getField("_owner");
ownerFieldOffset = ownerField.getOffset();
mutex_array = type.getAddressField("_mutex_array");
maxNum = type.getCIntegerField("_num_mutex").getJInt();
}
public Mutex(Address addr) {
super(addr);
}
public String name() { return CStringUtilities.getString(addr.getAddressAt(nameFieldOffset)); }
public Address owner() { return addr.getAddressAt(ownerFieldOffset); }
public static Address at(int i) { return mutex_array.getValue().getAddressAt(i * addrSize); }
public static int maxNum() { return maxNum; }
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
@ -37,6 +37,7 @@ public class Thread extends VMObject {
private static AddressField currentPendingMonitorField;
private static AddressField currentWaitingMonitorField;
private static AddressField osThreadField;
private static JLongField allocatedBytesField;
@ -53,6 +54,8 @@ public class Thread extends VMObject {
Type typeJavaThread = db.lookupType("JavaThread");
suspendFlagsField = typeJavaThread.getCIntegerField("_suspend_flags");
osThreadField = typeThread.getAddressField("_osthread");
tlabFieldOffset = typeThread.getField("_tlab").getOffset();
currentPendingMonitorField = typeJavaThread.getAddressField("_current_pending_monitor");
@ -123,6 +126,10 @@ public class Thread extends VMObject {
return false;
}
public OSThread osThread() {
return new OSThread(osThreadField.getValue(addr));
}
/** Assistance for ObjectMonitor implementation */
Address threadObjectAddress() { return addr; }
}

View File

@ -0,0 +1,70 @@
/*
* 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.
*
*/
package sun.jvm.hotspot.runtime;
import sun.jvm.hotspot.debugger.Address;
import sun.jvm.hotspot.memory.SystemDictionary;
import sun.jvm.hotspot.oops.DefaultHeapVisitor;
import sun.jvm.hotspot.oops.Klass;
import sun.jvm.hotspot.oops.ObjectHeap;
import sun.jvm.hotspot.oops.Oop;
import sun.jvm.hotspot.oops.OopUtilities;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class VMLocksPrinter {
private PrintStream tty;
private Threads threads = VM.getVM().getThreads();
public VMLocksPrinter(PrintStream tty) {
this.tty = tty;
}
private String ownerThreadName(Address addr) {
try {
JavaThread thread = VM.getVM().getThreads().createJavaThreadWrapper(addr);
return thread.getThreadName();
} catch (Exception e) {
return "Unknown thread";
}
}
public void printVMLocks() {
int maxNum = Mutex.maxNum();
for (int i = 0; i < maxNum; i++) {
Mutex mutex = new Mutex(Mutex.at(i));
if (mutex.owner() != null) {
sun.jvm.hotspot.runtime.Thread t = new sun.jvm.hotspot.runtime.Thread(mutex.owner());
int nativeThreadId = t.osThread().threadId();
tty.println("Internal VM Mutex " + mutex.name() + " is owned by " + ownerThreadName(mutex.owner())
+ ", nid=" + nativeThreadId + ", address=" + mutex.owner());
}
}
}
}

View File

@ -81,6 +81,13 @@ public class PStack extends Tool {
out.println("can't print deadlock information: " + exp);
}
try {
VMLocksPrinter vmLocksPrinter = new VMLocksPrinter(out);
vmLocksPrinter.printVMLocks();
} catch (Exception e) {
out.println("can't print VM locks information: " + e);
}
List<ThreadProxy> l = cdbg.getThreadList();
if (l.isEmpty() && PlatformInfo.getOS().equals("darwin")) {
// If the list is empty, we assume we attached to a process, and on OSX we can only

View File

@ -92,6 +92,7 @@ serviceability/sa/TestIntConstant.java 8307393 generic-
serviceability/sa/TestJhsdbJstackLineNumbers.java 8307393 generic-all
serviceability/sa/TestJhsdbJstackLock.java 8307393 generic-all
serviceability/sa/TestJhsdbJstackMixed.java 8307393 generic-all
serviceability/sa/TestJhsdbJstackPrintVMLocks.java 8307393 generic-all
serviceability/sa/TestJhsdbJstackUpcall.java 8307393 generic-all
serviceability/sa/TestJmapCore.java 8307393 generic-all
serviceability/sa/TestJmapCoreMetaspace.java 8307393 generic-all

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 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
@ -55,7 +55,7 @@ public class ClhsdbField {
"field InstanceKlass _methods Array<Method*>*",
"field InstanceKlass _constants ConstantPool*",
"field Klass _name Symbol*",
"field JavaThread _osthread OSThread*",
"field Thread _osthread OSThread*",
"field TenuredGeneration _the_space ContiguousSpace*",
"field VirtualSpace _low_boundary char*",
"field MethodCounters _backedge_counter InvocationCounter",

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 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
@ -56,7 +56,7 @@ public class ClhsdbVmStructsDump {
"field InstanceKlass _constants ConstantPool*",
"field Klass _name Symbol*",
"type ClassLoaderData* null",
"field JavaThread _osthread OSThread*",
"field Thread _osthread OSThread*",
"type TenuredGeneration Generation",
"type Universe null",
"type ConstantPoolCache MetaspaceObj"));

View File

@ -0,0 +1,55 @@
/*
* 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.whitebox.WhiteBox;
import jdk.test.lib.apps.LingeredApp;
public class LingeredAppWithLockInVM extends LingeredApp {
private static class LockerThread implements Runnable {
public void run() {
while (!isReady()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
WhiteBox wb = WhiteBox.getWhiteBox();
wb.lockAndStuckInSafepoint();
}
}
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("Lock file name is not specified");
System.exit(7);
}
Thread t = new Thread(new LockerThread());
t.setName("LockerThread");
t.start();
LingeredApp.main(args);
}
}

View File

@ -0,0 +1,85 @@
/*
* 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.JDKToolLauncher;
import jdk.test.lib.SA.SATestUtils;
import jdk.test.lib.apps.LingeredApp;
import jdk.test.lib.process.OutputAnalyzer;
import jtreg.SkippedException;
/**
* @test
* @summary Test verifies that jstack --mixed prints information about VM locks
* @requires vm.hasSA
* @library /test/lib
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run driver TestJhsdbJstackPrintVMLocks
*/
public class TestJhsdbJstackPrintVMLocks {
final static int MAX_ATTEMPTS = 5;
public static void main(String[] args) throws Exception {
SATestUtils.skipIfCannotAttach(); // throws SkippedException if attach not expected to work.
LingeredApp theApp = null;
try {
theApp = new LingeredAppWithLockInVM();
LingeredApp.startApp(theApp,
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
"-Xbootclasspath/a:.");
System.out.println("Started LingeredApp with pid " + theApp.getPid());
theApp.waitAppReadyOrCrashed();
for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
JDKToolLauncher launcher = JDKToolLauncher
.createUsingTestJDK("jhsdb");
launcher.addToolArg("jstack");
launcher.addToolArg("--mixed");
launcher.addToolArg("--pid");
launcher.addToolArg(Long.toString(theApp.getPid()));
ProcessBuilder pb = SATestUtils.createProcessBuilder(launcher);
Process jhsdb = pb.start();
OutputAnalyzer out = new OutputAnalyzer(jhsdb);
jhsdb.waitFor();
System.out.println(out.getStdout());
System.err.println(out.getStderr());
if (out.contains("Mutex VMStatistic_lock is owned by LockerThread")) {
System.out.println("Test PASSED");
return;
}
Thread.sleep(attempt * 2000);
}
throw new RuntimeException("Not able to find lock");
} finally {
theApp.getProcess().destroyForcibly();
}
}
}

View File

@ -589,6 +589,12 @@ public class LingeredApp {
static class SteadyStateLock {};
private static volatile boolean isReady = false;
protected static boolean isReady() {
return isReady;
}
/**
* This part is the application itself. First arg is optional "forceCrash".
* Following arg is the lock file name.
@ -627,6 +633,7 @@ public class LingeredApp {
while (Files.exists(path)) {
// Touch the lock to indicate our readiness
setLastModified(theLockFileName, epoch());
isReady = true;
Thread.sleep(spinDelay);
}
}

View File

@ -99,6 +99,8 @@ public class WhiteBox {
// printed by the VM.
public native String printString(String str, int maxLength);
public native void lockAndStuckInSafepoint();
public int countAliveClasses(String name) {
// Make sure class name is in the correct format
return countAliveClasses0(name.replace('.', '/'));