8242263: Diagnose synchronization on primitive wrappers
Added diagnostic flag DiagnoseSyncOnPrimitiveWrappers Reviewed-by: dholmes, mdoerr, dcubed, coleenp, egahlin, mgronlun
This commit is contained in:
parent
fac22ce20c
commit
e56002c7d3
src
hotspot
cpu
aarch64
aarch64.adc1_MacroAssembler_aarch64.cppinterp_masm_aarch64.cppmacroAssembler_aarch64.cppmacroAssembler_aarch64.hpp
arm
c1_MacroAssembler_arm.cppc2_MacroAssembler_arm.cppinterp_masm_arm.cppmacroAssembler_arm.cppmacroAssembler_arm.hpp
ppc
s390
x86
share
classfile
jfr/metadata
logging
oops
runtime
utilities
jdk.jfr/share/conf/jfr
test
hotspot/jtreg/runtime/Monitor
jdk/jdk/jfr/event
lib/jdk/test/lib/jfr
@ -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";
|
||||
|
Loading…
x
Reference in New Issue
Block a user