8242263: Diagnose synchronization on primitive wrappers

Added diagnostic flag DiagnoseSyncOnPrimitiveWrappers

Reviewed-by: dholmes, mdoerr, dcubed, coleenp, egahlin, mgronlun
This commit is contained in:
Patricio Chilano Mateo 2020-08-21 15:04:02 +00:00
parent fac22ce20c
commit e56002c7d3
36 changed files with 528 additions and 88 deletions

@ -3511,6 +3511,13 @@ encode %{
// Load markWord from object into displaced_header.
__ ldr(disp_hdr, Address(oop, oopDesc::mark_offset_in_bytes()));
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
__ load_klass(tmp, oop);
__ ldrw(tmp, Address(tmp, Klass::access_flags_offset()));
__ tstw(tmp, JVM_ACC_IS_BOX_CLASS);
__ br(Assembler::NE, cont);
}
if (UseBiasedLocking && !UseOptoBiasInlining) {
__ biased_locking_enter(box, oop, disp_hdr, tmp, true, cont);
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -73,11 +73,18 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr
// save object being locked into the BasicObjectLock
str(obj, Address(disp_hdr, BasicObjectLock::obj_offset_in_bytes()));
null_check_offset = offset();
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(hdr, obj);
ldrw(hdr, Address(hdr, Klass::access_flags_offset()));
tstw(hdr, JVM_ACC_IS_BOX_CLASS);
br(Assembler::NE, slow_case);
}
if (UseBiasedLocking) {
assert(scratch != noreg, "should have scratch register at this point");
null_check_offset = biased_locking_enter(disp_hdr, obj, hdr, scratch, false, done, &slow_case);
} else {
null_check_offset = offset();
biased_locking_enter(disp_hdr, obj, hdr, scratch, false, done, &slow_case);
}
// Load object header

@ -725,6 +725,13 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg)
// Load object pointer into obj_reg %c_rarg3
ldr(obj_reg, Address(lock_reg, obj_offset));
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(tmp, obj_reg);
ldrw(tmp, Address(tmp, Klass::access_flags_offset()));
tstw(tmp, JVM_ACC_IS_BOX_CLASS);
br(Assembler::NE, slow_case);
}
if (UseBiasedLocking) {
biased_locking_enter(lock_reg, obj_reg, swap_reg, tmp, false, done, &slow_case);
}

@ -444,14 +444,14 @@ void MacroAssembler::reserved_stack_check() {
bind(no_reserved_zone_enabling);
}
int MacroAssembler::biased_locking_enter(Register lock_reg,
Register obj_reg,
Register swap_reg,
Register tmp_reg,
bool swap_reg_contains_mark,
Label& done,
Label* slow_case,
BiasedLockingCounters* counters) {
void MacroAssembler::biased_locking_enter(Register lock_reg,
Register obj_reg,
Register swap_reg,
Register tmp_reg,
bool swap_reg_contains_mark,
Label& done,
Label* slow_case,
BiasedLockingCounters* counters) {
assert(UseBiasedLocking, "why call this otherwise?");
assert_different_registers(lock_reg, obj_reg, swap_reg);
@ -471,9 +471,7 @@ int MacroAssembler::biased_locking_enter(Register lock_reg,
// pointers to allow age to be placed into low bits
// First check to see whether biasing is even enabled for this object
Label cas_label;
int null_check_offset = -1;
if (!swap_reg_contains_mark) {
null_check_offset = offset();
ldr(swap_reg, mark_addr);
}
andr(tmp_reg, swap_reg, markWord::biased_lock_mask_in_place);
@ -601,8 +599,6 @@ int MacroAssembler::biased_locking_enter(Register lock_reg,
}
bind(cas_label);
return null_check_offset;
}
void MacroAssembler::biased_locking_exit(Register obj_reg, Register temp_reg, Label& done) {

@ -111,15 +111,11 @@ class MacroAssembler: public Assembler {
// tmp_reg must be supplied and must not be rscratch1 or rscratch2
// Optional slow case is for implementations (interpreter and C1) which branch to
// slow case directly. Leaves condition codes set for C2's Fast_Lock node.
// Returns offset of first potentially-faulting instruction for null
// check info (currently consumed only by C1). If
// swap_reg_contains_mark is true then returns -1 as it is assumed
// the calling code has already passed any potential faults.
int biased_locking_enter(Register lock_reg, Register obj_reg,
Register swap_reg, Register tmp_reg,
bool swap_reg_contains_mark,
Label& done, Label* slow_case = NULL,
BiasedLockingCounters* counters = NULL);
void biased_locking_enter(Register lock_reg, Register obj_reg,
Register swap_reg, Register tmp_reg,
bool swap_reg_contains_mark,
Label& done, Label* slow_case = NULL,
BiasedLockingCounters* counters = NULL);
void biased_locking_exit (Register obj_reg, Register temp_reg, Label& done);

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2020, 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
@ -200,26 +200,29 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj,
const int obj_offset = BasicObjectLock::obj_offset_in_bytes();
const int mark_offset = BasicLock::displaced_header_offset_in_bytes();
str(obj, Address(disp_hdr, obj_offset));
null_check_offset = offset();
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(tmp1, obj);
ldr_u32(tmp1, Address(tmp1, Klass::access_flags_offset()));
tst(tmp1, JVM_ACC_IS_BOX_CLASS);
b(slow_case, ne);
}
if (UseBiasedLocking) {
// load object
str(obj, Address(disp_hdr, obj_offset));
null_check_offset = biased_locking_enter(obj, hdr/*scratched*/, tmp1, false, tmp2, done, slow_case);
biased_locking_enter(obj, hdr/*scratched*/, tmp1, false, tmp2, done, slow_case);
}
assert(oopDesc::mark_offset_in_bytes() == 0, "Required by atomic instructions");
if (!UseBiasedLocking) {
null_check_offset = offset();
}
// On MP platforms the next load could return a 'stale' value if the memory location has been modified by another thread.
// That would be acceptable as ether CAS or slow case path is taken in that case.
// Must be the first instruction here, because implicit null check relies on it
ldr(hdr, Address(obj, oopDesc::mark_offset_in_bytes()));
str(obj, Address(disp_hdr, obj_offset));
tst(hdr, markWord::unlocked_value);
b(fast_lock, ne);

@ -90,6 +90,13 @@ void C2_MacroAssembler::fast_lock(Register Roop, Register Rbox, Register Rscratc
Label fast_lock, done;
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(Rscratch, Roop);
ldr_u32(Rscratch, Address(Rscratch, Klass::access_flags_offset()));
tst(Rscratch, JVM_ACC_IS_BOX_CLASS);
b(done, ne);
}
if (UseBiasedLocking && !UseOptoBiasInlining) {
assert(scratch3 != noreg, "need extra temporary for -XX:-UseOptoBiasInlining");
biased_locking_enter(Roop, Rmark, Rscratch, false, scratch3, done, done);

@ -883,6 +883,13 @@ void InterpreterMacroAssembler::lock_object(Register Rlock) {
// Load object pointer
ldr(Robj, Address(Rlock, obj_offset));
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(R0, Robj);
ldr_u32(R0, Address(R0, Klass::access_flags_offset()));
tst(R0, JVM_ACC_IS_BOX_CLASS);
b(slow_case, ne);
}
if (UseBiasedLocking) {
biased_locking_enter(Robj, Rmark/*scratched*/, R0, false, Rtemp, done, slow_case);
}

@ -1322,11 +1322,11 @@ void MacroAssembler::biased_locking_enter_with_cas(Register obj_reg, Register ol
#endif // !PRODUCT
}
int MacroAssembler::biased_locking_enter(Register obj_reg, Register swap_reg, Register tmp_reg,
bool swap_reg_contains_mark,
Register tmp2,
Label& done, Label& slow_case,
BiasedLockingCounters* counters) {
void MacroAssembler::biased_locking_enter(Register obj_reg, Register swap_reg, Register tmp_reg,
bool swap_reg_contains_mark,
Register tmp2,
Label& done, Label& slow_case,
BiasedLockingCounters* counters) {
// obj_reg must be preserved (at least) if the bias locking fails
// tmp_reg is a temporary register
// swap_reg was used as a temporary but contained a value
@ -1357,10 +1357,6 @@ int MacroAssembler::biased_locking_enter(Register obj_reg, Register swap_reg, Re
// First check to see whether biasing is even enabled for this object
Label cas_label;
// The null check applies to the mark loading, if we need to load it.
// If the mark has already been loaded in swap_reg then it has already
// been performed and the offset is irrelevant.
int null_check_offset = offset();
if (!swap_reg_contains_mark) {
ldr(swap_reg, mark_addr);
}
@ -1504,8 +1500,6 @@ int MacroAssembler::biased_locking_enter(Register obj_reg, Register swap_reg, Re
// removing the bias bit from the object's header.
bind(cas_label);
return null_check_offset;
}

@ -375,18 +375,14 @@ public:
// biased and we acquired it. Slow case label is branched to with
// condition code NE set if the lock is biased but we failed to acquire
// it. Otherwise fall through.
// Returns offset of first potentially-faulting instruction for null
// check info (currently consumed only by C1). If
// swap_reg_contains_mark is true then returns -1 as it is assumed
// the calling code has already passed any potential faults.
// Notes:
// - swap_reg and tmp_reg are scratched
// - Rtemp was (implicitly) scratched and can now be specified as the tmp2
int biased_locking_enter(Register obj_reg, Register swap_reg, Register tmp_reg,
bool swap_reg_contains_mark,
Register tmp2,
Label& done, Label& slow_case,
BiasedLockingCounters* counters = NULL);
void biased_locking_enter(Register obj_reg, Register swap_reg, Register tmp_reg,
bool swap_reg_contains_mark,
Register tmp2,
Label& done, Label& slow_case,
BiasedLockingCounters* counters = NULL);
void biased_locking_exit(Register obj_reg, Register temp_reg, Label& done);
// Building block for CAS cases of biased locking: makes CAS and records statistics.

@ -105,6 +105,13 @@ void C1_MacroAssembler::lock_object(Register Rmark, Register Roop, Register Rbox
// Save object being locked into the BasicObjectLock...
std(Roop, BasicObjectLock::obj_offset_in_bytes(), Rbox);
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(Rscratch, Roop);
lwz(Rscratch, in_bytes(Klass::access_flags_offset()), Rscratch);
testbitdi(CCR0, R0, Rscratch, exact_log2(JVM_ACC_IS_BOX_CLASS));
bne(CCR0, slow_int);
}
if (UseBiasedLocking) {
biased_locking_enter(CCR0, Roop, Rmark, Rscratch, R0, done, &slow_int);
}

@ -910,6 +910,13 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) {
// Load markWord from object into displaced_header.
ld(displaced_header, oopDesc::mark_offset_in_bytes(), object);
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(tmp, object);
lwz(tmp, in_bytes(Klass::access_flags_offset()), tmp);
testbitdi(CCR0, R0, tmp, exact_log2(JVM_ACC_IS_BOX_CLASS));
bne(CCR0, slow_case);
}
if (UseBiasedLocking) {
biased_locking_enter(CCR0, object, displaced_header, tmp, current_header, done, &slow_case);
}

@ -2836,6 +2836,12 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register
// Load markWord from object into displaced_header.
ld(displaced_header, oopDesc::mark_offset_in_bytes(), oop);
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(temp, oop);
lwz(temp, in_bytes(Klass::access_flags_offset()), temp);
testbitdi(flag, R0, temp, exact_log2(JVM_ACC_IS_BOX_CLASS));
bne(flag, cont);
}
if (try_bias) {
biased_locking_enter(flag, oop, displaced_header, temp, current_header, cont);

@ -91,6 +91,12 @@ void C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hd
// Save object being locked into the BasicObjectLock...
z_stg(obj, Address(disp_hdr, BasicObjectLock::obj_offset_in_bytes()));
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(Z_R1_scratch, obj);
testbit(Address(Z_R1_scratch, Klass::access_flags_offset()), exact_log2(JVM_ACC_IS_BOX_CLASS));
z_btrue(slow_case);
}
if (UseBiasedLocking) {
biased_locking_enter(obj, hdr, Z_R1_scratch, Z_R0_scratch, done, &slow_case);
}

@ -1000,6 +1000,12 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) {
// Load markWord from object into displaced_header.
z_lg(displaced_header, oopDesc::mark_offset_in_bytes(), object);
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(Z_R1_scratch, object);
testbit(Address(Z_R1_scratch, Klass::access_flags_offset()), exact_log2(JVM_ACC_IS_BOX_CLASS));
z_btrue(slow_case);
}
if (UseBiasedLocking) {
biased_locking_enter(object, displaced_header, Z_R1, Z_R0, done, &slow_case);
}

@ -3358,6 +3358,14 @@ void MacroAssembler::compiler_fast_lock_object(Register oop, Register box, Regis
// Load markWord from oop into mark.
z_lg(displacedHeader, 0, oop);
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(Z_R1_scratch, oop);
z_l(Z_R1_scratch, Address(Z_R1_scratch, Klass::access_flags_offset()));
assert((JVM_ACC_IS_BOX_CLASS & 0xFFFF) == 0, "or change following instruction");
z_nilh(Z_R1_scratch, JVM_ACC_IS_BOX_CLASS >> 16);
z_brne(done);
}
if (try_bias) {
biased_locking_enter(oop, displacedHeader, temp, Z_R0, done);
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2020, 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
@ -39,6 +39,7 @@
#include "runtime/stubRoutines.hpp"
int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr, Register scratch, Label& slow_case) {
const Register rklass_decode_tmp = LP64_ONLY(rscratch1) NOT_LP64(noreg);
const int aligned_mask = BytesPerWord -1;
const int hdr_offset = oopDesc::mark_offset_in_bytes();
assert(hdr == rax, "hdr must be rax, for the cmpxchg instruction");
@ -51,12 +52,18 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr
// save object being locked into the BasicObjectLock
movptr(Address(disp_hdr, BasicObjectLock::obj_offset_in_bytes()), obj);
null_check_offset = offset();
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(hdr, obj, rklass_decode_tmp);
movl(hdr, Address(hdr, Klass::access_flags_offset()));
testl(hdr, JVM_ACC_IS_BOX_CLASS);
jcc(Assembler::notZero, slow_case);
}
if (UseBiasedLocking) {
assert(scratch != noreg, "should have scratch register at this point");
Register rklass_decode_tmp = LP64_ONLY(rscratch1) NOT_LP64(noreg);
null_check_offset = biased_locking_enter(disp_hdr, obj, hdr, scratch, rklass_decode_tmp, false, done, &slow_case);
} else {
null_check_offset = offset();
biased_locking_enter(disp_hdr, obj, hdr, scratch, rklass_decode_tmp, false, done, &slow_case);
}
// Load object header

@ -470,6 +470,13 @@ void C2_MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmp
Label IsInflated, DONE_LABEL;
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(tmpReg, objReg, cx1Reg);
movl(tmpReg, Address(tmpReg, Klass::access_flags_offset()));
testl(tmpReg, JVM_ACC_IS_BOX_CLASS);
jcc(Assembler::notZero, DONE_LABEL);
}
// it's stack-locked, biased or neutral
// TODO: optimize away redundant LDs of obj->mark and improve the markword triage
// order to reduce the number of conditional branches in the most common cases.

@ -1186,6 +1186,7 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) {
const Register tmp_reg = rbx; // Will be passed to biased_locking_enter to avoid a
// problematic case where tmp_reg = no_reg.
const Register obj_reg = LP64_ONLY(c_rarg3) NOT_LP64(rcx); // Will contain the oop
const Register rklass_decode_tmp = LP64_ONLY(rscratch1) NOT_LP64(noreg);
const int obj_offset = BasicObjectLock::obj_offset_in_bytes();
const int lock_offset = BasicObjectLock::lock_offset_in_bytes ();
@ -1197,8 +1198,14 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) {
// Load object pointer into obj_reg
movptr(obj_reg, Address(lock_reg, obj_offset));
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
load_klass(tmp_reg, obj_reg, rklass_decode_tmp);
movl(tmp_reg, Address(tmp_reg, Klass::access_flags_offset()));
testl(tmp_reg, JVM_ACC_IS_BOX_CLASS);
jcc(Assembler::notZero, slow_case);
}
if (UseBiasedLocking) {
Register rklass_decode_tmp = LP64_ONLY(rscratch1) NOT_LP64(noreg);
biased_locking_enter(lock_reg, obj_reg, swap_reg, tmp_reg, rklass_decode_tmp, false, done, &slow_case);
}

@ -1081,15 +1081,15 @@ void MacroAssembler::reserved_stack_check() {
bind(no_reserved_zone_enabling);
}
int MacroAssembler::biased_locking_enter(Register lock_reg,
Register obj_reg,
Register swap_reg,
Register tmp_reg,
Register tmp_reg2,
bool swap_reg_contains_mark,
Label& done,
Label* slow_case,
BiasedLockingCounters* counters) {
void MacroAssembler::biased_locking_enter(Register lock_reg,
Register obj_reg,
Register swap_reg,
Register tmp_reg,
Register tmp_reg2,
bool swap_reg_contains_mark,
Label& done,
Label* slow_case,
BiasedLockingCounters* counters) {
assert(UseBiasedLocking, "why call this otherwise?");
assert(swap_reg == rax, "swap_reg must be rax for cmpxchgq");
assert(tmp_reg != noreg, "tmp_reg must be supplied");
@ -1108,9 +1108,7 @@ int MacroAssembler::biased_locking_enter(Register lock_reg,
// pointers to allow age to be placed into low bits
// First check to see whether biasing is even enabled for this object
Label cas_label;
int null_check_offset = -1;
if (!swap_reg_contains_mark) {
null_check_offset = offset();
movptr(swap_reg, mark_addr);
}
movptr(tmp_reg, swap_reg);
@ -1127,9 +1125,6 @@ int MacroAssembler::biased_locking_enter(Register lock_reg,
// simpler.
movptr(saved_mark_addr, swap_reg);
#endif
if (swap_reg_contains_mark) {
null_check_offset = offset();
}
load_prototype_header(tmp_reg, obj_reg, tmp_reg2);
#ifdef _LP64
orptr(tmp_reg, r15_thread);
@ -1263,8 +1258,6 @@ int MacroAssembler::biased_locking_enter(Register lock_reg,
}
bind(cas_label);
return null_check_offset;
}
void MacroAssembler::biased_locking_exit(Register obj_reg, Register temp_reg, Label& done) {

@ -661,15 +661,11 @@ class MacroAssembler: public Assembler {
// allocate a temporary (inefficient, avoid if possible).
// Optional slow case is for implementations (interpreter and C1) which branch to
// slow case directly. Leaves condition codes set for C2's Fast_Lock node.
// Returns offset of first potentially-faulting instruction for null
// check info (currently consumed only by C1). If
// swap_reg_contains_mark is true then returns -1 as it is assumed
// the calling code has already passed any potential faults.
int biased_locking_enter(Register lock_reg, Register obj_reg,
Register swap_reg, Register tmp_reg,
Register tmp_reg2, bool swap_reg_contains_mark,
Label& done, Label* slow_case = NULL,
BiasedLockingCounters* counters = NULL);
void biased_locking_enter(Register lock_reg, Register obj_reg,
Register swap_reg, Register tmp_reg,
Register tmp_reg2, bool swap_reg_contains_mark,
Label& done, Label* slow_case = NULL,
BiasedLockingCounters* counters = NULL);
void biased_locking_exit (Register obj_reg, Register temp_reg, Label& done);
Condition negate_condition(Condition cond);

@ -2184,6 +2184,14 @@ void SystemDictionary::resolve_well_known_classes(TRAPS) {
//_box_klasses[T_OBJECT] = WK_KLASS(object_klass);
//_box_klasses[T_ARRAY] = WK_KLASS(object_klass);
if (DiagnoseSyncOnPrimitiveWrappers != 0) {
for (int i = T_BOOLEAN; i < T_LONG + 1; i++) {
assert(_box_klasses[i] != NULL, "NULL box class");
_box_klasses[i]->set_is_box();
_box_klasses[i]->set_prototype_header(markWord::prototype());
}
}
#ifdef ASSERT
if (UseSharedSpaces) {
JVMTI_ONLY(assert(JvmtiExport::is_early_phase(),

@ -66,6 +66,10 @@
<Field type="InflateCause" name="cause" label="Monitor Inflation Cause" description="Cause of inflation" />
</Event>
<Event name="SyncOnPrimitiveWrapper" category="Java Virtual Machine, Diagnostics" label="Primitive Wrapper Synchronization" thread="true" stackTrace="true" startTime="false" experimental="true">
<Field type="Class" name="boxClass" label="Boxing Class" />
</Event>
<Event name="BiasedLockRevocation" category="Java Virtual Machine, Runtime" label="Biased Lock Revocation" description="Revoked bias of object" thread="true"
stackTrace="true">
<Field type="Class" name="lockClass" label="Lock Class" description="Class of object whose biased lock was revoked" />

@ -130,6 +130,7 @@
LOG_TAG(phases) \
LOG_TAG(plab) \
LOG_TAG(preview) /* Trace loading of preview feature types */ \
LOG_TAG(primitivewrappers) \
LOG_TAG(promotion) \
LOG_TAG(preorder) /* Trace all classes loaded in order referenced (not loaded) */ \
LOG_TAG(protectiondomain) /* "Trace protection domain verification" */ \

@ -632,6 +632,8 @@ protected:
void set_is_hidden() { _access_flags.set_is_hidden_class(); }
bool is_non_strong_hidden() const { return access_flags().is_hidden_class() &&
class_loader_data()->has_class_mirror_holder(); }
bool is_box() const { return access_flags().is_box_class(); }
void set_is_box() { _access_flags.set_is_box_class(); }
bool is_cloneable() const;
void set_is_cloneable();
@ -641,6 +643,7 @@ protected:
// prototype markWord. If biased locking is enabled it may further be
// biasable and have an epoch.
markWord prototype_header() const { return _prototype_header; }
// NOTE: once instances of this klass are floating around in the
// system, this header must only be updated at a safepoint.
// NOTE 2: currently we only ever set the prototype header to the

@ -4193,6 +4193,11 @@ jint Arguments::apply_ergo() {
}
#endif
if (FLAG_IS_CMDLINE(DiagnoseSyncOnPrimitiveWrappers)) {
if (DiagnoseSyncOnPrimitiveWrappers == ObjectSynchronizer::LOG_WARNING && !log_is_enabled(Info, primitivewrappers)) {
LogConfiguration::configure_stdout(LogLevel::Info, true, LOG_TAGS(primitivewrappers));
}
}
return JNI_OK;
}

@ -808,6 +808,16 @@ const size_t minimumSymbolTableSize = 1024;
range(500, max_intx) \
constraint(BiasedLockingDecayTimeFunc,AfterErgo) \
\
diagnostic(intx, DiagnoseSyncOnPrimitiveWrappers, 0, \
"Detect and take action upon identifying synchronization on " \
"primitive wrappers. Modes: " \
"0: off; " \
"1: exit with fatal error; " \
"2: log message to stdout. Output file can be specified with " \
" -Xlog:primitivewrappers. If JFR is running it will " \
" also generate JFR events.") \
range(0, 2) \
\
product(bool, ExitOnOutOfMemoryError, false, \
"JVM exits on the first occurrence of an out-of-memory error") \
\

@ -504,6 +504,10 @@ bool ObjectSynchronizer::quick_enter(oop obj, Thread* self,
NoSafepointVerifier nsv;
if (obj == NULL) return false; // Need to throw NPE
if (DiagnoseSyncOnPrimitiveWrappers != 0 && obj->klass()->is_box()) {
return false;
}
const markWord mark = obj->mark();
if (mark.has_monitor()) {
@ -554,6 +558,52 @@ bool ObjectSynchronizer::quick_enter(oop obj, Thread* self,
return false; // revert to slow-path
}
// Handle notifications when synchronizing on primitive wrappers
void ObjectSynchronizer::handle_sync_on_primitive_wrapper(Handle obj, Thread* current) {
assert(current->is_Java_thread(), "must be for java object synchronization");
JavaThread* self = (JavaThread*) current;
frame last_frame = self->last_frame();
if (last_frame.is_interpreted_frame()) {
// adjust bcp to point back to monitorenter so that we print the correct line numbers
last_frame.interpreter_frame_set_bcp(last_frame.interpreter_frame_bcp() - 1);
}
if (DiagnoseSyncOnPrimitiveWrappers == FATAL_EXIT) {
ResourceMark rm(self);
stringStream ss;
self->print_stack_on(&ss);
char* base = (char*)strstr(ss.base(), "at");
char* newline = (char*)strchr(ss.base(), '\n');
if (newline != NULL) {
*newline = '\0';
}
fatal("Synchronizing on object " INTPTR_FORMAT " of klass %s %s", p2i(obj()), obj->klass()->external_name(), base);
} else {
assert(DiagnoseSyncOnPrimitiveWrappers == LOG_WARNING, "invalid value for DiagnoseSyncOnPrimitiveWrappers");
ResourceMark rm(self);
Log(primitivewrappers) pwlog;
pwlog.info("Synchronizing on object " INTPTR_FORMAT " of klass %s", p2i(obj()), obj->klass()->external_name());
if (self->has_last_Java_frame()) {
LogStream info_stream(pwlog.info());
self->print_stack_on(&info_stream);
} else {
pwlog.info("Cannot find the last Java frame");
}
EventSyncOnPrimitiveWrapper event;
if (event.should_commit()) {
event.set_boxClass(obj->klass());
event.commit();
}
}
if (last_frame.is_interpreted_frame()) {
last_frame.interpreter_frame_set_bcp(last_frame.interpreter_frame_bcp() + 1);
}
}
// -----------------------------------------------------------------------------
// Monitor Enter/Exit
// The interpreter and compiler assembly code tries to lock using the fast path
@ -561,6 +611,10 @@ bool ObjectSynchronizer::quick_enter(oop obj, Thread* self,
// changed. The implementation is extremely sensitive to race condition. Be careful.
void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, TRAPS) {
if (DiagnoseSyncOnPrimitiveWrappers != 0 && obj->klass()->is_box()) {
handle_sync_on_primitive_wrapper(obj, THREAD);
}
if (UseBiasedLocking) {
if (!SafepointSynchronize::is_at_safepoint()) {
BiasedLocking::revoke(obj, THREAD);
@ -704,6 +758,10 @@ void ObjectSynchronizer::reenter(Handle obj, intx recursions, TRAPS) {
// JNI locks on java objects
// NOTE: must use heavy weight monitor to handle jni monitor enter
void ObjectSynchronizer::jni_enter(Handle obj, TRAPS) {
if (DiagnoseSyncOnPrimitiveWrappers != 0 && obj->klass()->is_box()) {
handle_sync_on_primitive_wrapper(obj, THREAD);
}
// the current locking is from JNI instead of Java code
if (UseBiasedLocking) {
BiasedLocking::revoke(obj, THREAD);

@ -62,6 +62,12 @@ class ObjectSynchronizer : AllStatic {
inflate_cause_nof = 7 // Number of causes
} InflateCause;
typedef enum {
NOT_ENABLED = 0,
FATAL_EXIT = 1,
LOG_WARNING = 2
} SyncDiagnosticOption;
// exit must be implemented non-blocking, since the compiler cannot easily handle
// deoptimization at monitor exit. Hence, it does not take a Handle argument.
@ -194,6 +200,8 @@ class ObjectSynchronizer : AllStatic {
static u_char* get_gvars_hc_sequence_addr();
static size_t get_gvars_size();
static u_char* get_gvars_stw_random_addr();
static void handle_sync_on_primitive_wrapper(Handle obj, Thread* current);
};
// ObjectLocker enforces balanced locking and can never throw an

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2020, 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
@ -67,6 +67,7 @@ enum {
JVM_ACC_HAS_FINAL_METHOD = 0x01000000, // True if klass has final method
JVM_ACC_IS_SHARED_CLASS = 0x02000000, // True if klass is shared
JVM_ACC_IS_HIDDEN_CLASS = 0x04000000, // True if klass is hidden
JVM_ACC_IS_BOX_CLASS = 0x08000000, // True if klass is primitive wrapper
// Klass* and Method* flags
JVM_ACC_HAS_LOCAL_VARIABLE_TABLE= 0x00200000,
@ -151,6 +152,7 @@ class AccessFlags {
bool is_cloneable_fast () const { return (_flags & JVM_ACC_IS_CLONEABLE_FAST ) != 0; }
bool is_shared_class () const { return (_flags & JVM_ACC_IS_SHARED_CLASS ) != 0; }
bool is_hidden_class () const { return (_flags & JVM_ACC_IS_HIDDEN_CLASS ) != 0; }
bool is_box_class () const { return (_flags & JVM_ACC_IS_BOX_CLASS ) != 0; }
// Klass* and Method* flags
bool has_localvariable_table () const { return (_flags & JVM_ACC_HAS_LOCAL_VARIABLE_TABLE) != 0; }
@ -224,6 +226,7 @@ class AccessFlags {
void set_has_miranda_methods() { atomic_set_bits(JVM_ACC_HAS_MIRANDA_METHODS); }
void set_is_shared_class() { atomic_set_bits(JVM_ACC_IS_SHARED_CLASS); }
void set_is_hidden_class() { atomic_set_bits(JVM_ACC_IS_HIDDEN_CLASS); }
void set_is_box_class() { atomic_set_bits(JVM_ACC_IS_BOX_CLASS); }
public:
// field flags

@ -91,6 +91,11 @@
<setting name="threshold" control="synchronization-threshold">20 ms</setting>
</event>
<event name="jdk.SyncOnPrimitiveWrapper">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.BiasedLockRevocation">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>

@ -91,6 +91,11 @@
<setting name="threshold" control="synchronization-threshold">10 ms</setting>
</event>
<event name="jdk.SyncOnPrimitiveWrapper">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.BiasedLockRevocation">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>

@ -0,0 +1,167 @@
/*
* Copyright (c) 2020, 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.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
import java.util.*;
import java.util.stream.*;
/*
* @test
* @bug 8242263
* @summary Exercise DiagnoseSyncOnPrimitiveWrappers diagnostic flag
* @library /test/lib
* @run driver/timeout=180000 SyncOnPrimitiveWrapperTest
*/
public class SyncOnPrimitiveWrapperTest {
static final int LOOP_COUNT = 3000;
static final int THREAD_COUNT = 2;
static String[] fatalTests[];
static String[] logTests[];
static List<Object> testObjects = new ArrayList<Object>();
private static final String[] specificFlags[] = {
{"-Xint", "-XX:+UseBiasedLocking"},
{"-Xint", "-XX:-UseBiasedLocking"},
{"-Xcomp", "-XX:TieredStopAtLevel=1", "-XX:+UseBiasedLocking"},
{"-Xcomp", "-XX:TieredStopAtLevel=1", "-XX:-UseBiasedLocking"},
{"-Xcomp", "-XX:-TieredCompilation", "-XX:-UseBiasedLocking"},
{"-Xcomp", "-XX:-TieredCompilation", "-XX:+UseBiasedLocking", "-XX:+UseOptoBiasInlining"},
{"-Xcomp", "-XX:-TieredCompilation", "-XX:+UseBiasedLocking", "-XX:-UseOptoBiasInlining"}
};
private static void initTestObjects() {
testObjects.add(Character.valueOf('H'));
testObjects.add(Boolean.valueOf(true));
testObjects.add(Byte.valueOf((byte)0x40));
testObjects.add(Short.valueOf((short)0x4000));
testObjects.add(Integer.valueOf(0x40000000));
testObjects.add(Long.valueOf(0x4000000000000000L));
testObjects.add(Float.valueOf(1.20f));
testObjects.add(Double.valueOf(1.2345));
}
private static void generateTests() {
initTestObjects();
String[] commonFatalTestsFlags = {"-XX:+UnlockDiagnosticVMOptions", "-XX:-CreateCoredumpOnCrash", "-XX:DiagnoseSyncOnPrimitiveWrappers=1"};
fatalTests = new String[specificFlags.length * testObjects.size()][];
for (int i = 0; i < specificFlags.length; i++) {
for (int j = 0; j < testObjects.size(); j++) {
int index = i * testObjects.size() + j;
fatalTests[index] = Stream.of(commonFatalTestsFlags, specificFlags[i], new String[] {"SyncOnPrimitiveWrapperTest$FatalTest", Integer.toString(j)})
.flatMap(Stream::of)
.toArray(String[]::new);
}
}
String[] commonLogTestsFlags = {"-XX:+UnlockDiagnosticVMOptions", "-XX:DiagnoseSyncOnPrimitiveWrappers=2"};
logTests = new String[specificFlags.length][];
for (int i = 0; i < specificFlags.length; i++) {
logTests[i] = Stream.of(commonLogTestsFlags, specificFlags[i], new String[] {"SyncOnPrimitiveWrapperTest$LogTest"})
.flatMap(Stream::of)
.toArray(String[]::new);
}
}
public static void main(String[] args) throws Exception {
generateTests();
for (int i = 0; i < fatalTests.length; i++) {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(fatalTests[i]);
OutputAnalyzer output = ProcessTools.executeProcess(pb);
output.shouldContain("fatal error: Synchronizing on object");
output.shouldNotContain("synchronization on primitive wrapper did not fail");
output.shouldNotHaveExitValue(0);
}
for (int i = 0; i < logTests.length; i++) {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(logTests[i]);
OutputAnalyzer output = ProcessTools.executeProcess(pb);
checkOutput(output);
}
}
private static void checkOutput(OutputAnalyzer output) {
String out = output.getOutput();
assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Character.*"));
assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Boolean.*"));
assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Byte.*"));
assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Short.*"));
assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Integer.*"));
assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Long.*"));
String[] res = out.split("Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Float\\R");
assertTrue(res.length - 1 == (LOOP_COUNT * THREAD_COUNT + 1), res.length - 1);
}
private static void assertTrue(boolean condition) {
if (!condition) {
throw new RuntimeException("No synchronization matches");
}
}
private static void assertTrue(boolean condition, int count) {
if (!condition) {
throw new RuntimeException("Synchronization count was " + count);
}
}
static class FatalTest {
public static void main(String[] args) throws Exception {
initTestObjects();
synchronized (testObjects.get(Integer.valueOf(args[0]))) {
throw new RuntimeException("synchronization on primitive wrapper did not fail");
}
}
}
static class LogTest implements Runnable {
private static long sharedCounter = 0L;
private static Float sharedLock1 = 0.0f;
public static void main(String[] args) throws Exception {
initTestObjects();
for (Object obj : testObjects) {
synchronized (obj) {
sharedCounter++;
}
}
LogTest test = new LogTest();
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(test);
threads[i].start();
}
for (Thread t : threads) {
t.join();
}
}
@Override
public void run() {
for (int i = 0; i < LOOP_COUNT; i++) {
synchronized (sharedLock1) {
sharedCounter++;
}
}
}
}
}

@ -82,7 +82,7 @@ public class TestLookForUntestedEvents {
// Experimental events
private static final Set<String> experimentalEvents = new HashSet<>(
Arrays.asList(
"Flush")
"Flush", "SyncOnPrimitiveWrapper")
);

@ -0,0 +1,92 @@
/*
* Copyright (c) 2020, 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 jdk.jfr.event.runtime;
import java.time.Duration;
import java.util.*;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedThread;
import jdk.test.lib.jfr.EventNames;
import jdk.test.lib.jfr.Events;
/**
* @test
* @bug 8242263
* @requires vm.hasJFR
* @key jfr
* @library /test/lib
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:DiagnoseSyncOnPrimitiveWrappers=2 jdk.jfr.event.runtime.TestSyncOnPrimitiveWrapperEvent
*/
public class TestSyncOnPrimitiveWrapperEvent {
static final String EVENT_NAME = EventNames.SyncOnPrimitiveWrapper;
static String[] classesWanted = {"java/lang/Character", "java/lang/Boolean", "java/lang/Byte", "java/lang/Short",
"java/lang/Integer", "java/lang/Long", "java/lang/Float", "java/lang/Double"};
static List<Object> testObjects = new ArrayList<Object>();
static Integer counter = 0;
private static void initTestObjects() {
testObjects.add(Character.valueOf('H'));
testObjects.add(Boolean.valueOf(true));
testObjects.add(Byte.valueOf((byte)0x40));
testObjects.add(Short.valueOf((short)0x4000));
testObjects.add(Integer.valueOf(0x40000000));
testObjects.add(Long.valueOf(0x4000000000000000L));
testObjects.add(Float.valueOf(1.20f));
testObjects.add(Double.valueOf(1.2345));
}
public static void main(String[] args) throws Throwable {
initTestObjects();
Recording recording = new Recording();
recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0));
recording.start();
for (Object obj : testObjects) {
synchronized (obj) {
counter++;
}
}
recording.stop();
List<String> classesFound = new ArrayList<String>();
List<RecordedEvent> events = Events.fromRecording(recording);
Events.hasEvents(events);
for (RecordedEvent event : Events.fromRecording(recording)) {
String className = Events.assertField(event, "boxClass.name").notEmpty().getValue();
RecordedThread jt = event.getThread();
if (Thread.currentThread().getName().equals(jt.getJavaName())) {
classesFound.add(className);
}
}
for (String classWanted : classesWanted) {
if (!classesFound.contains(classWanted)) {
throw new AssertionError("No matching event SyncOnPrimitiveWrapper with \"boxClass=" + classWanted + "\" and current thread as caller");
}
}
if (classesFound.size() != classesWanted.length) {
throw new AssertionError("Invalid number of SyncOnPrimitiveWrapper events for current thread");
}
}
}

@ -61,6 +61,7 @@ public class EventNames {
public final static String JavaMonitorEnter = PREFIX + "JavaMonitorEnter";
public final static String JavaMonitorWait = PREFIX + "JavaMonitorWait";
public final static String JavaMonitorInflate = PREFIX + "JavaMonitorInflate";
public final static String SyncOnPrimitiveWrapper = PREFIX + "SyncOnPrimitiveWrapper";
public final static String ClassLoad = PREFIX + "ClassLoad";
public final static String ClassDefine = PREFIX + "ClassDefine";
public final static String ClassUnload = PREFIX + "ClassUnload";