-
Notifications
You must be signed in to change notification settings - Fork 11
fix(profiler): pre-register vtable receiver classes via JVMTI (PROF-14618) #527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -575,6 +575,44 @@ void VM::loadAllMethodIDs(jvmtiEnv *jvmti, JNIEnv *jni) { | |
| } | ||
| } | ||
|
|
||
| void JNICALL VM::ClassPrepare(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, | ||
| jclass klass) { | ||
| loadMethodIDs(jvmti, jni, klass); | ||
|
|
||
| // Pre-populate _class_map for vtable_target signal-safe lookup. ClassPrepare | ||
| // runs on a JVM thread (never a signal handler), so we take a blocking | ||
| // shared lock via classMapSharedGuard() rather than the best-effort | ||
| // tryLockShared() in lookupClass(). This ensures newly prepared classes are | ||
| // never silently skipped while an exclusive dump/reset is in flight. Gate on | ||
| // vtable_target to avoid overhead when the feature is disabled. | ||
| Profiler* profiler = Profiler::instance(); | ||
| if (profiler == nullptr || !profiler->stackWalkFeatures().vtable_target) { | ||
| return; | ||
|
Comment on lines
+589
to
+590
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
On a fresh start that enables Useful? React with 👍 / 👎. |
||
| } | ||
| char* sig = nullptr; | ||
| jvmtiError err = jvmti->GetClassSignature(klass, &sig, nullptr); | ||
| if (err != JVMTI_ERROR_NONE) { | ||
| Log::warn("ClassPrepare: GetClassSignature failed (%d) — class skipped for vtable-target pre-registration", err); | ||
|
|
||
| return; | ||
| } | ||
| if (sig == nullptr) { | ||
| return; | ||
| } | ||
| // Only 'L'-type (reference) signatures can ever match vtable_target lookup | ||
| // keys; skip primitives and arrays. | ||
| if (sig[0] != 'L') { | ||
| jvmti->Deallocate(reinterpret_cast<unsigned char*>(sig)); | ||
| return; | ||
| } | ||
| const char* slice = nullptr; | ||
| size_t slice_len = 0; | ||
| if (ObjectSampler::normalizeClassSignature(sig, &slice, &slice_len)) { | ||
| SharedLockGuard guard = profiler->classMapSharedGuard(); | ||
| (void)profiler->classMap()->lookup(slice, slice_len); | ||
| } | ||
| jvmti->Deallocate(reinterpret_cast<unsigned char*>(sig)); | ||
| } | ||
|
|
||
| void JNICALL VM::VMInit(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) { | ||
| ready(jvmti, jni); | ||
| loadAllMethodIDs(jvmti, jni); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When profiling is resumed with
resume(reset == false) after a previous run has set_start_time, this new pre-registration remains inside the reset-only block and is skipped. Sincestop()disables the JVMTI events, any classes prepared while the profiler was idle never hitClassPrepare, so the resumed CPU-only/vtable-target recording will keep missing their receiver-class frames until a later dump happens to repopulate the map. Move the pre-registration out of the reset-only path (or otherwise run it on resume when vtable_target is enabled).Useful? React with 👍 / 👎.