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);
|
return (jstring) JNIHandles::make_local(THREAD, result);
|
||||||
WB_END
|
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 {
|
class WBIsKlassAliveClosure : public LockedClassesDo {
|
||||||
Symbol* _name;
|
Symbol* _name;
|
||||||
int _count;
|
int _count;
|
||||||
@ -2988,6 +2998,7 @@ static JNINativeMethod methods[] = {
|
|||||||
{CC"cleanMetaspaces", CC"()V", (void*)&WB_CleanMetaspaces},
|
{CC"cleanMetaspaces", CC"()V", (void*)&WB_CleanMetaspaces},
|
||||||
{CC"rss", CC"()J", (void*)&WB_Rss},
|
{CC"rss", CC"()J", (void*)&WB_Rss},
|
||||||
{CC"printString", CC"(Ljava/lang/String;I)Ljava/lang/String;", (void*)&WB_PrintString},
|
{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"wordSize", CC"()J", (void*)&WB_WordSize},
|
||||||
{CC"rootChunkWordSize", CC"()J", (void*)&WB_RootChunkWordSize}
|
{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
|
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() {
|
Mutex::~Mutex() {
|
||||||
assert_owner(nullptr);
|
assert_owner(nullptr);
|
||||||
os::free(const_cast<char*>(_name));
|
os::free(const_cast<char*>(_name));
|
||||||
@ -524,6 +534,61 @@ void Mutex::set_owner_implementation(Thread *new_owner) {
|
|||||||
}
|
}
|
||||||
#endif // ASSERT
|
#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) {}
|
RecursiveMutex::RecursiveMutex() : _sem(1), _owner(nullptr), _recursions(0) {}
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
|
|
||||||
class Mutex : public CHeapObj<mtSynchronizer> {
|
class Mutex : public CHeapObj<mtSynchronizer> {
|
||||||
|
|
||||||
|
friend class VMStructs;
|
||||||
public:
|
public:
|
||||||
// Special low level locks are given names and ranges avoid overlap.
|
// Special low level locks are given names and ranges avoid overlap.
|
||||||
enum class Rank {
|
enum class Rank {
|
||||||
@ -103,6 +104,9 @@ class Mutex : public CHeapObj<mtSynchronizer> {
|
|||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
bool _allow_vm_block;
|
bool _allow_vm_block;
|
||||||
#endif
|
#endif
|
||||||
|
static Mutex** _mutex_array;
|
||||||
|
static int _num_mutex;
|
||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
Rank _rank; // rank (to avoid/detect potential deadlocks)
|
Rank _rank; // rank (to avoid/detect potential deadlocks)
|
||||||
Mutex* _next; // Used by a Thread to link up owned locks
|
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; }
|
const char *name() const { return _name; }
|
||||||
|
|
||||||
|
static void add_mutex(Mutex* var);
|
||||||
|
|
||||||
void print_on_error(outputStream* st) const;
|
void print_on_error(outputStream* st) const;
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
void print_on(outputStream* st) const;
|
void print_on(outputStream* st) const;
|
||||||
void print() const;
|
void print() const;
|
||||||
#endif
|
#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 {
|
class Monitor : public Mutex {
|
||||||
|
@ -158,10 +158,6 @@ Monitor* JVMCIRuntime_lock = nullptr;
|
|||||||
// Only one RecursiveMutex
|
// Only one RecursiveMutex
|
||||||
RecursiveMutex* MultiArray_lock = nullptr;
|
RecursiveMutex* MultiArray_lock = nullptr;
|
||||||
|
|
||||||
#define MAX_NUM_MUTEX 128
|
|
||||||
static Mutex* _mutex_array[MAX_NUM_MUTEX];
|
|
||||||
static int _num_mutex;
|
|
||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
void assert_locked_or_safepoint(const Mutex* lock) {
|
void assert_locked_or_safepoint(const Mutex* lock) {
|
||||||
if (DebuggingContext::is_enabled() || VMError::is_error_reported()) return;
|
if (DebuggingContext::is_enabled() || VMError::is_error_reported()) return;
|
||||||
@ -182,18 +178,13 @@ void assert_lock_strong(const Mutex* lock) {
|
|||||||
}
|
}
|
||||||
#endif
|
#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(name) name##_storage
|
||||||
#define MUTEX_STORAGE(name, type) alignas(type) static uint8_t MUTEX_STORAGE_NAME(name)[sizeof(type)]
|
#define MUTEX_STORAGE(name, type) alignas(type) static uint8_t MUTEX_STORAGE_NAME(name)[sizeof(type)]
|
||||||
#define MUTEX_DEF(name, type, pri, ...) { \
|
#define MUTEX_DEF(name, type, pri, ...) { \
|
||||||
assert(name == nullptr, "Mutex/Monitor initialized twice"); \
|
assert(name == nullptr, "Mutex/Monitor initialized twice"); \
|
||||||
MUTEX_STORAGE(name, type); \
|
MUTEX_STORAGE(name, type); \
|
||||||
name = ::new(static_cast<void*>(MUTEX_STORAGE_NAME(name))) type((pri), #name, ##__VA_ARGS__); \
|
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__)
|
#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()) {
|
if (lt.is_enabled()) {
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
LogStream ls(lt);
|
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
|
// order. If their implementations change such that these assumptions
|
||||||
// are violated, a whole lot of code will break.
|
// 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)
|
// for debugging: check that we're already owning this lock (or are at a safepoint / handshake)
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
void assert_locked_or_safepoint(const Mutex* lock);
|
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; }
|
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 {
|
class VM_ClearICs: public VM_Operation {
|
||||||
private:
|
private:
|
||||||
bool _preserve_static_stubs;
|
bool _preserve_static_stubs;
|
||||||
|
@ -659,7 +659,6 @@
|
|||||||
volatile_nonstatic_field(JavaThread, _is_method_handle_return, int) \
|
volatile_nonstatic_field(JavaThread, _is_method_handle_return, int) \
|
||||||
nonstatic_field(JavaThread, _saved_exception_pc, address) \
|
nonstatic_field(JavaThread, _saved_exception_pc, address) \
|
||||||
volatile_nonstatic_field(JavaThread, _thread_state, JavaThreadState) \
|
volatile_nonstatic_field(JavaThread, _thread_state, JavaThreadState) \
|
||||||
nonstatic_field(JavaThread, _osthread, OSThread*) \
|
|
||||||
nonstatic_field(JavaThread, _stack_base, address) \
|
nonstatic_field(JavaThread, _stack_base, address) \
|
||||||
nonstatic_field(JavaThread, _stack_size, size_t) \
|
nonstatic_field(JavaThread, _stack_size, size_t) \
|
||||||
nonstatic_field(JavaThread, _vframe_array_head, vframeArray*) \
|
nonstatic_field(JavaThread, _vframe_array_head, vframeArray*) \
|
||||||
@ -667,6 +666,7 @@
|
|||||||
nonstatic_field(JavaThread, _active_handles, JNIHandleBlock*) \
|
nonstatic_field(JavaThread, _active_handles, JNIHandleBlock*) \
|
||||||
nonstatic_field(JavaThread, _lock_id, int64_t) \
|
nonstatic_field(JavaThread, _lock_id, int64_t) \
|
||||||
volatile_nonstatic_field(JavaThread, _terminated, JavaThread::TerminatedTypes) \
|
volatile_nonstatic_field(JavaThread, _terminated, JavaThread::TerminatedTypes) \
|
||||||
|
nonstatic_field(Thread, _osthread, OSThread*) \
|
||||||
nonstatic_field(Thread, _resource_area, ResourceArea*) \
|
nonstatic_field(Thread, _resource_area, ResourceArea*) \
|
||||||
nonstatic_field(CompilerThread, _env, ciEnv*) \
|
nonstatic_field(CompilerThread, _env, ciEnv*) \
|
||||||
\
|
\
|
||||||
@ -1022,7 +1022,12 @@
|
|||||||
nonstatic_field(elapsedTimer, _active, bool) \
|
nonstatic_field(elapsedTimer, _active, bool) \
|
||||||
nonstatic_field(InvocationCounter, _counter, unsigned int) \
|
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
|
// VM_TYPES
|
||||||
@ -1936,6 +1941,7 @@
|
|||||||
declare_toplevel_type(JNIid) \
|
declare_toplevel_type(JNIid) \
|
||||||
declare_toplevel_type(JNIid*) \
|
declare_toplevel_type(JNIid*) \
|
||||||
declare_toplevel_type(jmethodID*) \
|
declare_toplevel_type(jmethodID*) \
|
||||||
|
declare_toplevel_type(Mutex) \
|
||||||
declare_toplevel_type(Mutex*) \
|
declare_toplevel_type(Mutex*) \
|
||||||
declare_toplevel_type(nmethod*) \
|
declare_toplevel_type(nmethod*) \
|
||||||
COMPILER2_PRESENT(declare_unsigned_integer_type(node_idx_t)) \
|
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)
|
STEP_IF("printing owned locks on error", _verbose)
|
||||||
// mutexes/monitors that currently have an owner
|
// mutexes/monitors that currently have an owner
|
||||||
print_owned_locks_on_error(st);
|
Mutex::print_owned_locks_on_error(st);
|
||||||
st->cr();
|
st->cr();
|
||||||
|
|
||||||
STEP_IF("printing number of OutOfMemoryError and StackOverflow exceptions",
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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 currentPendingMonitorField;
|
||||||
private static AddressField currentWaitingMonitorField;
|
private static AddressField currentWaitingMonitorField;
|
||||||
|
private static AddressField osThreadField;
|
||||||
|
|
||||||
private static JLongField allocatedBytesField;
|
private static JLongField allocatedBytesField;
|
||||||
|
|
||||||
@ -53,6 +54,8 @@ public class Thread extends VMObject {
|
|||||||
Type typeJavaThread = db.lookupType("JavaThread");
|
Type typeJavaThread = db.lookupType("JavaThread");
|
||||||
|
|
||||||
suspendFlagsField = typeJavaThread.getCIntegerField("_suspend_flags");
|
suspendFlagsField = typeJavaThread.getCIntegerField("_suspend_flags");
|
||||||
|
osThreadField = typeThread.getAddressField("_osthread");
|
||||||
|
|
||||||
|
|
||||||
tlabFieldOffset = typeThread.getField("_tlab").getOffset();
|
tlabFieldOffset = typeThread.getField("_tlab").getOffset();
|
||||||
currentPendingMonitorField = typeJavaThread.getAddressField("_current_pending_monitor");
|
currentPendingMonitorField = typeJavaThread.getAddressField("_current_pending_monitor");
|
||||||
@ -123,6 +126,10 @@ public class Thread extends VMObject {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OSThread osThread() {
|
||||||
|
return new OSThread(osThreadField.getValue(addr));
|
||||||
|
}
|
||||||
|
|
||||||
/** Assistance for ObjectMonitor implementation */
|
/** Assistance for ObjectMonitor implementation */
|
||||||
Address threadObjectAddress() { return addr; }
|
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);
|
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();
|
List<ThreadProxy> l = cdbg.getThreadList();
|
||||||
if (l.isEmpty() && PlatformInfo.getOS().equals("darwin")) {
|
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
|
// 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/TestJhsdbJstackLineNumbers.java 8307393 generic-all
|
||||||
serviceability/sa/TestJhsdbJstackLock.java 8307393 generic-all
|
serviceability/sa/TestJhsdbJstackLock.java 8307393 generic-all
|
||||||
serviceability/sa/TestJhsdbJstackMixed.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/TestJhsdbJstackUpcall.java 8307393 generic-all
|
||||||
serviceability/sa/TestJmapCore.java 8307393 generic-all
|
serviceability/sa/TestJmapCore.java 8307393 generic-all
|
||||||
serviceability/sa/TestJmapCoreMetaspace.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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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 _methods Array<Method*>*",
|
||||||
"field InstanceKlass _constants ConstantPool*",
|
"field InstanceKlass _constants ConstantPool*",
|
||||||
"field Klass _name Symbol*",
|
"field Klass _name Symbol*",
|
||||||
"field JavaThread _osthread OSThread*",
|
"field Thread _osthread OSThread*",
|
||||||
"field TenuredGeneration _the_space ContiguousSpace*",
|
"field TenuredGeneration _the_space ContiguousSpace*",
|
||||||
"field VirtualSpace _low_boundary char*",
|
"field VirtualSpace _low_boundary char*",
|
||||||
"field MethodCounters _backedge_counter InvocationCounter",
|
"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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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 InstanceKlass _constants ConstantPool*",
|
||||||
"field Klass _name Symbol*",
|
"field Klass _name Symbol*",
|
||||||
"type ClassLoaderData* null",
|
"type ClassLoaderData* null",
|
||||||
"field JavaThread _osthread OSThread*",
|
"field Thread _osthread OSThread*",
|
||||||
"type TenuredGeneration Generation",
|
"type TenuredGeneration Generation",
|
||||||
"type Universe null",
|
"type Universe null",
|
||||||
"type ConstantPoolCache MetaspaceObj"));
|
"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 {};
|
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".
|
* This part is the application itself. First arg is optional "forceCrash".
|
||||||
* Following arg is the lock file name.
|
* Following arg is the lock file name.
|
||||||
@ -627,6 +633,7 @@ public class LingeredApp {
|
|||||||
while (Files.exists(path)) {
|
while (Files.exists(path)) {
|
||||||
// Touch the lock to indicate our readiness
|
// Touch the lock to indicate our readiness
|
||||||
setLastModified(theLockFileName, epoch());
|
setLastModified(theLockFileName, epoch());
|
||||||
|
isReady = true;
|
||||||
Thread.sleep(spinDelay);
|
Thread.sleep(spinDelay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,8 @@ public class WhiteBox {
|
|||||||
// printed by the VM.
|
// printed by the VM.
|
||||||
public native String printString(String str, int maxLength);
|
public native String printString(String str, int maxLength);
|
||||||
|
|
||||||
|
public native void lockAndStuckInSafepoint();
|
||||||
|
|
||||||
public int countAliveClasses(String name) {
|
public int countAliveClasses(String name) {
|
||||||
// Make sure class name is in the correct format
|
// Make sure class name is in the correct format
|
||||||
return countAliveClasses0(name.replace('.', '/'));
|
return countAliveClasses0(name.replace('.', '/'));
|
||||||
|
Loading…
Reference in New Issue
Block a user