8343741: SA jstack --mixed should print information about VM locks
Reviewed-by: cjplummer
This commit is contained in:
parent
1b2d9cad53
commit
98b667834c
@ -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}
|
||||
};
|
||||
|
@ -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) {}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)) \
|
||||
|
@ -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",
|
||||
|
@ -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; }
|
||||
|
||||
}
|
@ -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; }
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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"));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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('.', '/'));
|
||||
|
Loading…
Reference in New Issue
Block a user