Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/hotspot/share/prims/whitebox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,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;
Expand Down Expand Up @@ -2994,6 +3004,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}
};
Expand Down
65 changes: 65 additions & 0 deletions src/hotspot/share/runtime/mutex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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) {}

Expand Down
11 changes: 11 additions & 0 deletions src/hotspot/share/runtime/mutex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
68 changes: 2 additions & 66 deletions src/hotspot/share/runtime/mutexLocker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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__)

Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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
}
5 changes: 0 additions & 5 deletions src/hotspot/share/runtime/mutexLocker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions src/hotspot/share/runtime/vmOperations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 8 additions & 2 deletions src/hotspot/share/runtime/vmStructs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -659,14 +659,14 @@
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*) \
nonstatic_field(JavaThread, _vframe_array_last, vframeArray*) \
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*) \
\
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1937,6 +1942,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)) \
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/utilities/vmError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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; }

}
Loading