Skip to content

Commit 98b6678

Browse files
committed
8343741: SA jstack --mixed should print information about VM locks
Reviewed-by: cjplummer
1 parent 1b2d9ca commit 98b6678

File tree

19 files changed

+424
-79
lines changed

19 files changed

+424
-79
lines changed

src/hotspot/share/prims/whitebox.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,16 @@ WB_ENTRY(jstring, WB_PrintString(JNIEnv* env, jobject wb, jstring str, jint max_
186186
return (jstring) JNIHandles::make_local(THREAD, result);
187187
WB_END
188188

189+
WB_ENTRY(jint, WB_TakeLockAndHangInSafepoint(JNIEnv* env, jobject wb))
190+
JavaThread* self = JavaThread::current();
191+
// VMStatistic_lock is used to minimize interference with VM locking
192+
MutexLocker mu(VMStatistic_lock);
193+
VM_HangInSafepoint force_safepoint_stuck_op;
194+
VMThread::execute(&force_safepoint_stuck_op);
195+
ShouldNotReachHere();
196+
return 0;
197+
WB_END
198+
189199
class WBIsKlassAliveClosure : public LockedClassesDo {
190200
Symbol* _name;
191201
int _count;
@@ -2988,6 +2998,7 @@ static JNINativeMethod methods[] = {
29882998
{CC"cleanMetaspaces", CC"()V", (void*)&WB_CleanMetaspaces},
29892999
{CC"rss", CC"()J", (void*)&WB_Rss},
29903000
{CC"printString", CC"(Ljava/lang/String;I)Ljava/lang/String;", (void*)&WB_PrintString},
3001+
{CC"lockAndStuckInSafepoint", CC"()V", (void*)&WB_TakeLockAndHangInSafepoint},
29913002
{CC"wordSize", CC"()J", (void*)&WB_WordSize},
29923003
{CC"rootChunkWordSize", CC"()J", (void*)&WB_RootChunkWordSize}
29933004
};

src/hotspot/share/runtime/mutex.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,16 @@ bool Monitor::wait(uint64_t timeout) {
267267
return wait_status != 0; // return true IFF timeout
268268
}
269269

270+
static const int MAX_NUM_MUTEX = 1204;
271+
static Mutex* _internal_mutex_arr[MAX_NUM_MUTEX];
272+
Mutex** Mutex::_mutex_array = _internal_mutex_arr;
273+
int Mutex::_num_mutex = 0;
274+
275+
void Mutex::add_mutex(Mutex* var) {
276+
assert(Mutex::_num_mutex < MAX_NUM_MUTEX, "increase MAX_NUM_MUTEX");
277+
Mutex::_mutex_array[_num_mutex++] = var;
278+
}
279+
270280
Mutex::~Mutex() {
271281
assert_owner(nullptr);
272282
os::free(const_cast<char*>(_name));
@@ -524,6 +534,61 @@ void Mutex::set_owner_implementation(Thread *new_owner) {
524534
}
525535
#endif // ASSERT
526536

537+
// Print all mutexes/monitors that are currently owned by a thread; called
538+
// by fatal error handler.
539+
void Mutex::print_owned_locks_on_error(outputStream* st) {
540+
st->print("VM Mutex/Monitor currently owned by a thread: ");
541+
bool none = true;
542+
for (int i = 0; i < _num_mutex; i++) {
543+
// see if it has an owner
544+
if (_mutex_array[i]->owner() != nullptr) {
545+
if (none) {
546+
// print format used by Mutex::print_on_error()
547+
st->print_cr(" ([mutex/lock_event])");
548+
none = false;
549+
}
550+
_mutex_array[i]->print_on_error(st);
551+
st->cr();
552+
}
553+
}
554+
if (none) st->print_cr("None");
555+
}
556+
557+
void Mutex::print_lock_ranks(outputStream* st) {
558+
st->print_cr("VM Mutex/Monitor ranks: ");
559+
560+
#ifdef ASSERT
561+
// Be extra defensive and figure out the bounds on
562+
// ranks right here. This also saves a bit of time
563+
// in the #ranks*#mutexes loop below.
564+
int min_rank = INT_MAX;
565+
int max_rank = INT_MIN;
566+
for (int i = 0; i < _num_mutex; i++) {
567+
Mutex* m = _mutex_array[i];
568+
int r = (int) m->rank();
569+
if (min_rank > r) min_rank = r;
570+
if (max_rank < r) max_rank = r;
571+
}
572+
573+
// Print the listings rank by rank
574+
for (int r = min_rank; r <= max_rank; r++) {
575+
bool first = true;
576+
for (int i = 0; i < _num_mutex; i++) {
577+
Mutex* m = _mutex_array[i];
578+
if (r != (int) m->rank()) continue;
579+
580+
if (first) {
581+
st->cr();
582+
st->print_cr("Rank \"%s\":", m->rank_name());
583+
first = false;
584+
}
585+
st->print_cr(" %s", m->name());
586+
}
587+
}
588+
#else
589+
st->print_cr(" Only known in debug builds.");
590+
#endif // ASSERT
591+
}
527592

528593
RecursiveMutex::RecursiveMutex() : _sem(1), _owner(nullptr), _recursions(0) {}
529594

src/hotspot/share/runtime/mutex.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151

5252
class Mutex : public CHeapObj<mtSynchronizer> {
5353

54+
friend class VMStructs;
5455
public:
5556
// Special low level locks are given names and ranges avoid overlap.
5657
enum class Rank {
@@ -103,6 +104,9 @@ class Mutex : public CHeapObj<mtSynchronizer> {
103104
#ifndef PRODUCT
104105
bool _allow_vm_block;
105106
#endif
107+
static Mutex** _mutex_array;
108+
static int _num_mutex;
109+
106110
#ifdef ASSERT
107111
Rank _rank; // rank (to avoid/detect potential deadlocks)
108112
Mutex* _next; // Used by a Thread to link up owned locks
@@ -194,11 +198,18 @@ class Mutex : public CHeapObj<mtSynchronizer> {
194198

195199
const char *name() const { return _name; }
196200

201+
static void add_mutex(Mutex* var);
202+
197203
void print_on_error(outputStream* st) const;
198204
#ifndef PRODUCT
199205
void print_on(outputStream* st) const;
200206
void print() const;
201207
#endif
208+
209+
// Print all mutexes/monitors that are currently owned by a thread; called
210+
// by fatal error handler.
211+
static void print_owned_locks_on_error(outputStream* st);
212+
static void print_lock_ranks(outputStream* st);
202213
};
203214

204215
class Monitor : public Mutex {

src/hotspot/share/runtime/mutexLocker.cpp

Lines changed: 2 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,6 @@ Monitor* JVMCIRuntime_lock = nullptr;
158158
// Only one RecursiveMutex
159159
RecursiveMutex* MultiArray_lock = nullptr;
160160

161-
#define MAX_NUM_MUTEX 128
162-
static Mutex* _mutex_array[MAX_NUM_MUTEX];
163-
static int _num_mutex;
164-
165161
#ifdef ASSERT
166162
void assert_locked_or_safepoint(const Mutex* lock) {
167163
if (DebuggingContext::is_enabled() || VMError::is_error_reported()) return;
@@ -182,18 +178,13 @@ void assert_lock_strong(const Mutex* lock) {
182178
}
183179
#endif
184180

185-
static void add_mutex(Mutex* var) {
186-
assert(_num_mutex < MAX_NUM_MUTEX, "increase MAX_NUM_MUTEX");
187-
_mutex_array[_num_mutex++] = var;
188-
}
189-
190181
#define MUTEX_STORAGE_NAME(name) name##_storage
191182
#define MUTEX_STORAGE(name, type) alignas(type) static uint8_t MUTEX_STORAGE_NAME(name)[sizeof(type)]
192183
#define MUTEX_DEF(name, type, pri, ...) { \
193184
assert(name == nullptr, "Mutex/Monitor initialized twice"); \
194185
MUTEX_STORAGE(name, type); \
195186
name = ::new(static_cast<void*>(MUTEX_STORAGE_NAME(name))) type((pri), #name, ##__VA_ARGS__); \
196-
add_mutex(name); \
187+
Mutex::add_mutex(name); \
197188
}
198189
#define MUTEX_DEFN(name, type, pri, ...) MUTEX_DEF(name, type, Mutex::pri, ##__VA_ARGS__)
199190

@@ -371,7 +362,7 @@ void MutexLockerImpl::post_initialize() {
371362
if (lt.is_enabled()) {
372363
ResourceMark rm;
373364
LogStream ls(lt);
374-
print_lock_ranks(&ls);
365+
Mutex::print_lock_ranks(&ls);
375366
}
376367
}
377368

@@ -385,58 +376,3 @@ GCMutexLocker::GCMutexLocker(Mutex* mutex) {
385376
}
386377
}
387378

388-
// Print all mutexes/monitors that are currently owned by a thread; called
389-
// by fatal error handler.
390-
void print_owned_locks_on_error(outputStream* st) {
391-
st->print("VM Mutex/Monitor currently owned by a thread: ");
392-
bool none = true;
393-
for (int i = 0; i < _num_mutex; i++) {
394-
// see if it has an owner
395-
if (_mutex_array[i]->owner() != nullptr) {
396-
if (none) {
397-
// print format used by Mutex::print_on_error()
398-
st->print_cr(" ([mutex/lock_event])");
399-
none = false;
400-
}
401-
_mutex_array[i]->print_on_error(st);
402-
st->cr();
403-
}
404-
}
405-
if (none) st->print_cr("None");
406-
}
407-
408-
void print_lock_ranks(outputStream* st) {
409-
st->print_cr("VM Mutex/Monitor ranks: ");
410-
411-
#ifdef ASSERT
412-
// Be extra defensive and figure out the bounds on
413-
// ranks right here. This also saves a bit of time
414-
// in the #ranks*#mutexes loop below.
415-
int min_rank = INT_MAX;
416-
int max_rank = INT_MIN;
417-
for (int i = 0; i < _num_mutex; i++) {
418-
Mutex* m = _mutex_array[i];
419-
int r = (int) m->rank();
420-
if (min_rank > r) min_rank = r;
421-
if (max_rank < r) max_rank = r;
422-
}
423-
424-
// Print the listings rank by rank
425-
for (int r = min_rank; r <= max_rank; r++) {
426-
bool first = true;
427-
for (int i = 0; i < _num_mutex; i++) {
428-
Mutex* m = _mutex_array[i];
429-
if (r != (int) m->rank()) continue;
430-
431-
if (first) {
432-
st->cr();
433-
st->print_cr("Rank \"%s\":", m->rank_name());
434-
first = false;
435-
}
436-
st->print_cr(" %s", m->name());
437-
}
438-
}
439-
#else
440-
st->print_cr(" Only known in debug builds.");
441-
#endif // ASSERT
442-
}

src/hotspot/share/runtime/mutexLocker.hpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,6 @@ extern Mutex* tty_lock; // lock to synchronize output
171171
// order. If their implementations change such that these assumptions
172172
// are violated, a whole lot of code will break.
173173

174-
// Print all mutexes/monitors that are currently owned by a thread; called
175-
// by fatal error handler.
176-
void print_owned_locks_on_error(outputStream* st);
177-
void print_lock_ranks(outputStream* st);
178-
179174
// for debugging: check that we're already owning this lock (or are at a safepoint / handshake)
180175
#ifdef ASSERT
181176
void assert_locked_or_safepoint(const Mutex* lock);

src/hotspot/share/runtime/vmOperations.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ class VM_ForceSafepoint: public VM_EmptyOperation {
6060
VMOp_Type type() const { return VMOp_ForceSafepoint; }
6161
};
6262

63+
// used by whitebox API to emulate VM issues
64+
// when VM can't operate and doesn't respond to jcmd
65+
class VM_HangInSafepoint: public VM_Operation {
66+
public:
67+
VMOp_Type type() const { return VMOp_ForceSafepoint; }
68+
void doit() {
69+
while(true) {
70+
os::naked_short_sleep(10);
71+
}
72+
}
73+
};
74+
6375
class VM_ClearICs: public VM_Operation {
6476
private:
6577
bool _preserve_static_stubs;

src/hotspot/share/runtime/vmStructs.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -659,14 +659,14 @@
659659
volatile_nonstatic_field(JavaThread, _is_method_handle_return, int) \
660660
nonstatic_field(JavaThread, _saved_exception_pc, address) \
661661
volatile_nonstatic_field(JavaThread, _thread_state, JavaThreadState) \
662-
nonstatic_field(JavaThread, _osthread, OSThread*) \
663662
nonstatic_field(JavaThread, _stack_base, address) \
664663
nonstatic_field(JavaThread, _stack_size, size_t) \
665664
nonstatic_field(JavaThread, _vframe_array_head, vframeArray*) \
666665
nonstatic_field(JavaThread, _vframe_array_last, vframeArray*) \
667666
nonstatic_field(JavaThread, _active_handles, JNIHandleBlock*) \
668667
nonstatic_field(JavaThread, _lock_id, int64_t) \
669668
volatile_nonstatic_field(JavaThread, _terminated, JavaThread::TerminatedTypes) \
669+
nonstatic_field(Thread, _osthread, OSThread*) \
670670
nonstatic_field(Thread, _resource_area, ResourceArea*) \
671671
nonstatic_field(CompilerThread, _env, ciEnv*) \
672672
\
@@ -1022,7 +1022,12 @@
10221022
nonstatic_field(elapsedTimer, _active, bool) \
10231023
nonstatic_field(InvocationCounter, _counter, unsigned int) \
10241024
\
1025-
nonstatic_field(UpcallStub::FrameData, jfa, JavaFrameAnchor)
1025+
nonstatic_field(UpcallStub::FrameData, jfa, JavaFrameAnchor) \
1026+
\
1027+
nonstatic_field(Mutex, _name, const char*) \
1028+
static_field(Mutex, _mutex_array, Mutex**) \
1029+
static_field(Mutex, _num_mutex, int) \
1030+
volatile_nonstatic_field(Mutex, _owner, Thread*)
10261031

10271032
//--------------------------------------------------------------------------------
10281033
// VM_TYPES
@@ -1936,6 +1941,7 @@
19361941
declare_toplevel_type(JNIid) \
19371942
declare_toplevel_type(JNIid*) \
19381943
declare_toplevel_type(jmethodID*) \
1944+
declare_toplevel_type(Mutex) \
19391945
declare_toplevel_type(Mutex*) \
19401946
declare_toplevel_type(nmethod*) \
19411947
COMPILER2_PRESENT(declare_unsigned_integer_type(node_idx_t)) \

src/hotspot/share/utilities/vmError.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1223,7 +1223,7 @@ void VMError::report(outputStream* st, bool _verbose) {
12231223

12241224
STEP_IF("printing owned locks on error", _verbose)
12251225
// mutexes/monitors that currently have an owner
1226-
print_owned_locks_on_error(st);
1226+
Mutex::print_owned_locks_on_error(st);
12271227
st->cr();
12281228

12291229
STEP_IF("printing number of OutOfMemoryError and StackOverflow exceptions",
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
package sun.jvm.hotspot.runtime;
26+
27+
import sun.jvm.hotspot.debugger.Address;
28+
import sun.jvm.hotspot.types.AddressField;
29+
import sun.jvm.hotspot.types.Type;
30+
import sun.jvm.hotspot.types.TypeDataBase;
31+
import sun.jvm.hotspot.types.WrongTypeException;
32+
import sun.jvm.hotspot.utilities.*;
33+
34+
public class Mutex extends VMObject {
35+
private static long nameFieldOffset;
36+
private static long ownerFieldOffset;
37+
38+
private static AddressField mutex_array;
39+
private static int maxNum;
40+
41+
private static final long addrSize = VM.getVM().getAddressSize();
42+
43+
static {
44+
VM.registerVMInitializedObserver(new Observer() {
45+
public void update(Observable o, Object data) {
46+
initialize(VM.getVM().getTypeDataBase());
47+
}
48+
});
49+
}
50+
51+
private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
52+
Type type = db.lookupType("Mutex");
53+
54+
sun.jvm.hotspot.types.Field nameField = type.getField("_name");
55+
nameFieldOffset = nameField.getOffset();
56+
sun.jvm.hotspot.types.Field ownerField = type.getField("_owner");
57+
ownerFieldOffset = ownerField.getOffset();
58+
59+
mutex_array = type.getAddressField("_mutex_array");
60+
maxNum = type.getCIntegerField("_num_mutex").getJInt();
61+
}
62+
63+
public Mutex(Address addr) {
64+
super(addr);
65+
}
66+
67+
public String name() { return CStringUtilities.getString(addr.getAddressAt(nameFieldOffset)); }
68+
69+
public Address owner() { return addr.getAddressAt(ownerFieldOffset); }
70+
71+
public static Address at(int i) { return mutex_array.getValue().getAddressAt(i * addrSize); }
72+
73+
public static int maxNum() { return maxNum; }
74+
75+
}

0 commit comments

Comments
 (0)