Skip to content

Commit c2a4fed

Browse files
author
Markus Grönlund
committedMar 26, 2025
8348907: Stress times out when is executed with ZGC
Reviewed-by: egahlin, aboldtch, eosterlund
1 parent 5392674 commit c2a4fed

File tree

10 files changed

+129
-23
lines changed

10 files changed

+129
-23
lines changed
 

‎make/src/classes/build/tools/jfr/GenerateJfrFiles.java

+34-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -768,9 +768,10 @@ private static void printJfrEventClassesHpp(Metadata metadata, File outputFile)
768768
out.write("#ifndef JFRFILES_JFREVENTCLASSES_HPP");
769769
out.write("#define JFRFILES_JFREVENTCLASSES_HPP");
770770
out.write("");
771-
out.write("#include \"oops/klass.hpp\"");
772771
out.write("#include \"jfrfiles/jfrTypes.hpp\"");
773772
out.write("#include \"jfr/utilities/jfrTypes.hpp\"");
773+
out.write("#include \"oops/klass.hpp\"");
774+
out.write("#include \"runtime/thread.hpp\"");
774775
out.write("#include \"utilities/macros.hpp\"");
775776
out.write("#include \"utilities/ticks.hpp\"");
776777
out.write("#if INCLUDE_JFR");
@@ -789,6 +790,7 @@ private static void printJfrEventClassesHpp(Metadata metadata, File outputFile)
789790
out.write(" */");
790791
out.write("");
791792
printTypes(out, metadata, false);
793+
printHelpers(out, false);
792794
out.write("");
793795
out.write("");
794796
out.write("#else // !INCLUDE_JFR");
@@ -806,13 +808,43 @@ private static void printJfrEventClassesHpp(Metadata metadata, File outputFile)
806808
out.write("};");
807809
out.write("");
808810
printTypes(out, metadata, true);
811+
printHelpers(out, true);
809812
out.write("");
810813
out.write("");
811814
out.write("#endif // INCLUDE_JFR");
812815
out.write("#endif // JFRFILES_JFREVENTCLASSES_HPP");
813816
}
814817
}
815818

819+
private static void printHelpers(Printer out, boolean empty) {
820+
out.write("template <typename EventType>");
821+
out.write("class JfrNonReentrant : public EventType {");
822+
if (!empty) {
823+
out.write(" private:");
824+
out.write(" Thread* const _thread;");
825+
out.write(" int32_t _previous_nesting;");
826+
}
827+
out.write(" public:");
828+
out.write(" JfrNonReentrant(EventStartTime timing = TIMED)");
829+
if (empty) {
830+
out.write(" {}");
831+
} else {
832+
out.write(" : EventType(timing), _thread(Thread::current()), _previous_nesting(JfrThreadLocal::make_non_reentrant(_thread)) {}");
833+
out.write("");
834+
out.write(" JfrNonReentrant(Thread* thread, EventStartTime timing = TIMED)");
835+
out.write(" : EventType(timing), _thread(thread), _previous_nesting(JfrThreadLocal::make_non_reentrant(_thread)) {}");
836+
}
837+
if (!empty) {
838+
out.write("");
839+
out.write(" ~JfrNonReentrant() {");
840+
out.write(" if (_previous_nesting != -1) {");
841+
out.write(" JfrThreadLocal::make_reentrant(_thread, _previous_nesting);");
842+
out.write(" }");
843+
out.write(" }");
844+
}
845+
out.write("}; ");
846+
}
847+
816848
private static void printTypes(Printer out, Metadata metadata, boolean empty) {
817849
for (TypeElement t : metadata.getStructs()) {
818850
printType(out, t, empty);

‎src/hotspot/share/gc/z/zTracer.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ void ZTracer::initialize() {
124124
void ZTracer::send_stat_counter(const ZStatCounter& counter, uint64_t increment, uint64_t value) {
125125
NoSafepointVerifier nsv;
126126

127-
EventZStatisticsCounter e;
127+
JfrNonReentrant<EventZStatisticsCounter> e;
128128
if (e.should_commit()) {
129129
e.set_id(counter.id());
130130
e.set_increment(increment);
@@ -136,7 +136,7 @@ void ZTracer::send_stat_counter(const ZStatCounter& counter, uint64_t increment,
136136
void ZTracer::send_stat_sampler(const ZStatSampler& sampler, uint64_t value) {
137137
NoSafepointVerifier nsv;
138138

139-
EventZStatisticsSampler e;
139+
JfrNonReentrant<EventZStatisticsSampler> e;
140140
if (e.should_commit()) {
141141
e.set_id(sampler.id());
142142
e.set_value(value);

‎src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,10 @@ void JfrCheckpointManager::write_checkpoint(Thread* thread, traceid tid /* 0 */,
677677
JfrTypeManager::write_checkpoint(thread, tid, vthread);
678678
}
679679

680+
void JfrCheckpointManager::write_simplified_vthread_checkpoint(traceid vtid) {
681+
JfrTypeManager::write_simplified_vthread_checkpoint(vtid);
682+
}
683+
680684
class JfrNotifyClosure : public ThreadClosure {
681685
public:
682686
void do_thread(Thread* thread) {

‎src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ class JfrCheckpointManager : public JfrCHeapObj {
108108
public:
109109
static JfrBlobHandle create_thread_blob(JavaThread* jt, traceid tid = 0, oop vthread = nullptr);
110110
static void write_checkpoint(Thread* t, traceid tid = 0, oop vthread = nullptr);
111+
static void write_simplified_vthread_checkpoint(traceid vtid);
111112
size_t flush_type_set();
112113

113114
friend class Jfr;

‎src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,23 @@ void JfrThreadConstant::serialize(JfrCheckpointWriter& writer) {
312312
// VirtualThread threadgroup already serialized invariant.
313313
}
314314

315+
// This serializer is used when the vthread name cannot
316+
// be determined because we cannot access any oops.
317+
void JfrSimplifiedVirtualThreadConstant::serialize(JfrCheckpointWriter & writer) {
318+
writer.write_key(_vtid);
319+
// Write the null string categorically as the os name for virtual threads.
320+
writer.write((const char*)nullptr); // os name
321+
writer.write(0); // os id
322+
// vthread name cannot be determined for this simplified version.
323+
// This is because we cannot access any oops.
324+
writer.write_empty_string();
325+
writer.write(_vtid); // java tid
326+
// java thread group - VirtualThread threadgroup reserved id 1
327+
writer.write(1);
328+
writer.write<bool>(true); // isVirtual
329+
// VirtualThread threadgroup already serialized invariant.
330+
}
331+
315332
void BytecodeConstant::serialize(JfrCheckpointWriter& writer) {
316333
static const u4 nof_entries = Bytecodes::number_of_codes;
317334
writer.write_count(nof_entries);

‎src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp

+8
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ class JfrThreadConstant : public JfrSerializer {
117117
void serialize(JfrCheckpointWriter& writer);
118118
};
119119

120+
class JfrSimplifiedVirtualThreadConstant : public JfrSerializer {
121+
private:
122+
traceid _vtid;
123+
public:
124+
JfrSimplifiedVirtualThreadConstant(traceid vtid) : _vtid(vtid) {}
125+
void serialize(JfrCheckpointWriter & writer);
126+
};
127+
120128
class BytecodeConstant : public JfrSerializer {
121129
public:
122130
void serialize(JfrCheckpointWriter& writer);

‎src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ void JfrTypeManager::write_checkpoint(Thread* t, traceid tid /* 0 */, oop vthrea
132132
type_thread.serialize(writer);
133133
}
134134

135+
void JfrTypeManager::write_simplified_vthread_checkpoint(traceid vtid) {
136+
Thread* const current = Thread::current();
137+
assert(current != nullptr, "invariant");
138+
ResourceMark rm(current);
139+
JfrCheckpointWriter writer(current, true, THREADS, JFR_VIRTUAL_THREADLOCAL);
140+
// TYPE_THREAD and count is written later as part of vthread bulk serialization.
141+
writer.set_count(1); // Only a logical marker for the checkpoint header.
142+
JfrSimplifiedVirtualThreadConstant type_simple_vthread(vtid);
143+
type_simple_vthread.serialize(writer);
144+
}
145+
135146
class SerializerRegistrationGuard : public StackObj {
136147
private:
137148
static Semaphore _mutex_semaphore;

‎src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -41,6 +41,7 @@ class JfrTypeManager : public AllStatic {
4141
static void write_threads(JfrCheckpointWriter& writer);
4242
static JfrBlobHandle create_thread_blob(JavaThread* jt, traceid tid = 0, oop vthread = nullptr);
4343
static void write_checkpoint(Thread* t, traceid tid = 0, oop vthread = nullptr);
44+
static void write_simplified_vthread_checkpoint(traceid vtid);
4445
static void write_static_types(JfrCheckpointWriter& writer);
4546
};
4647

‎src/hotspot/share/jfr/support/jfrThreadLocal.cpp

+42-16
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ JfrThreadLocal::JfrThreadLocal() :
7070
_wallclock_time(os::javaTimeNanos()),
7171
_stackdepth(0),
7272
_entering_suspend_flag(0),
73+
_non_reentrant_nesting(0),
7374
_vthread_epoch(0),
7475
_vthread_excluded(false),
7576
_jvm_thread_excluded(false),
@@ -365,28 +366,32 @@ typedef JfrOopTraceId<ThreadIdAccess> AccessThreadTraceId;
365366
void JfrThreadLocal::set_vthread_epoch(const JavaThread* jt, traceid tid, u2 epoch) {
366367
assert(jt != nullptr, "invariant");
367368
assert(is_vthread(jt), "invariant");
368-
// To support event recursion, we update the native side first,
369-
// this provides the terminating case.
369+
assert(!is_non_reentrant(), "invariant");
370+
370371
Atomic::store(&jt->jfr_thread_local()->_vthread_epoch, epoch);
371-
/*
372-
* The java side, i.e. the vthread object, can now be updated.
373-
* Accessing the vthread object itself is a recursive case,
374-
* because it can trigger additional events, e.g.
375-
* loading the oop through load barriers.
376-
* Note there is a potential problem with this solution:
377-
* The recursive write hitting the terminating case will
378-
* use the thread id _before_ the checkpoint is committed.
379-
* Hence, the periodic thread can possibly flush that event
380-
* to a segment that does not include an associated checkpoint.
381-
* Considered rare and quite benign for now. The worst case is
382-
* that thread information for that event is not resolvable, i.e. null.
383-
*/
372+
384373
oop vthread = jt->vthread();
385374
assert(vthread != nullptr, "invariant");
375+
386376
AccessThreadTraceId::set_epoch(vthread, epoch);
387377
JfrCheckpointManager::write_checkpoint(const_cast<JavaThread*>(jt), tid, vthread);
388378
}
389379

380+
void JfrThreadLocal::set_vthread_epoch_checked(const JavaThread* jt, traceid tid, u2 epoch) {
381+
assert(jt != nullptr, "invariant");
382+
assert(is_vthread(jt), "invariant");
383+
384+
// If the event is marked as non reentrant, write only a simplified version of the vthread info.
385+
// Essentially all the same info except the vthread name, because we cannot touch the oop.
386+
// Since we cannot touch the oop, we also cannot update its vthread epoch.
387+
if (is_non_reentrant()) {
388+
JfrCheckpointManager::write_simplified_vthread_checkpoint(tid);
389+
return;
390+
}
391+
392+
set_vthread_epoch(jt, tid, epoch);
393+
}
394+
390395
traceid JfrThreadLocal::vthread_id(const Thread* t) {
391396
assert(t != nullptr, "invariant");
392397
return Atomic::load(&t->jfr_thread_local()->_vthread_id);
@@ -416,7 +421,7 @@ traceid JfrThreadLocal::thread_id(const Thread* t) {
416421
if (!tl->is_vthread_excluded()) {
417422
const u2 current_epoch = AccessThreadTraceId::current_epoch();
418423
if (vthread_epoch(jt) != current_epoch) {
419-
set_vthread_epoch(jt, tid, current_epoch);
424+
set_vthread_epoch_checked(jt, tid, current_epoch);
420425
}
421426
}
422427
return tid;
@@ -480,6 +485,26 @@ bool JfrThreadLocal::is_vthread(const JavaThread* jt) {
480485
return Atomic::load_acquire(&jt->jfr_thread_local()->_vthread) && jt->last_continuation() != nullptr;
481486
}
482487

488+
int32_t JfrThreadLocal::make_non_reentrant(Thread* t) {
489+
assert(t != nullptr, "invariant");
490+
if (!t->is_Java_thread() || !is_vthread(JavaThread::cast(t))) {
491+
return -1;
492+
}
493+
return t->jfr_thread_local()->_non_reentrant_nesting++;
494+
}
495+
496+
void JfrThreadLocal::make_reentrant(Thread* t, int32_t previous_nesting) {
497+
assert(t->is_Java_thread() && is_vthread(JavaThread::cast(t)), "invariant");
498+
assert(previous_nesting >= 0, "invariant");
499+
t->jfr_thread_local()->_non_reentrant_nesting = previous_nesting;
500+
}
501+
502+
bool JfrThreadLocal::is_non_reentrant() {
503+
Thread* const current_thread = Thread::current();
504+
assert(current_thread != nullptr, "invariant");
505+
return current_thread->jfr_thread_local()->_non_reentrant_nesting > 0;
506+
}
507+
483508
inline bool is_virtual(const JavaThread* jt, oop thread) {
484509
assert(jt != nullptr, "invariant");
485510
return thread != jt->threadObj();
@@ -493,6 +518,7 @@ void JfrThreadLocal::on_set_current_thread(JavaThread* jt, oop thread) {
493518
Atomic::release_store(&tl->_vthread, false);
494519
return;
495520
}
521+
assert(tl->_non_reentrant_nesting == 0, "invariant");
496522
Atomic::store(&tl->_vthread_id, AccessThreadTraceId::id(thread));
497523
const u2 epoch_raw = AccessThreadTraceId::epoch(thread);
498524
const bool excluded = epoch_raw & excluded_bit;

‎src/hotspot/share/jfr/support/jfrThreadLocal.hpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -64,6 +64,7 @@ class JfrThreadLocal {
6464
jlong _wallclock_time;
6565
mutable u4 _stackdepth;
6666
volatile jint _entering_suspend_flag;
67+
int32_t _non_reentrant_nesting;
6768
u2 _vthread_epoch;
6869
bool _vthread_excluded;
6970
bool _jvm_thread_excluded;
@@ -81,14 +82,16 @@ class JfrThreadLocal {
8182
static void set(bool* excluded_field, bool state);
8283
static traceid assign_thread_id(const Thread* t, JfrThreadLocal* tl);
8384
static traceid vthread_id(const Thread* t);
84-
static void set_vthread_epoch(const JavaThread* jt, traceid id, u2 epoch);
85+
static void set_vthread_epoch(const JavaThread* jt, traceid tid, u2 epoch);
86+
static void set_vthread_epoch_checked(const JavaThread* jt, traceid tid, u2 epoch);
8587
static traceid jvm_thread_id(const JfrThreadLocal* tl);
8688
bool is_vthread_excluded() const;
8789
static void exclude_vthread(const JavaThread* jt);
8890
static void include_vthread(const JavaThread* jt);
8991
static bool is_jvm_thread_excluded(const Thread* t);
9092
static void exclude_jvm_thread(const Thread* t);
9193
static void include_jvm_thread(const Thread* t);
94+
static bool is_non_reentrant();
9295

9396
public:
9497
JfrThreadLocal();
@@ -266,6 +269,9 @@ class JfrThreadLocal {
266269
return _dead;
267270
}
268271

272+
static int32_t make_non_reentrant(Thread* thread);
273+
static void make_reentrant(Thread* thread, int32_t previous_nesting);
274+
269275
bool is_excluded() const;
270276
bool is_included() const;
271277
static bool is_excluded(const Thread* thread);

0 commit comments

Comments
 (0)
Please sign in to comment.