Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4c6c7d1
Add fjmethodid=lazy: resolve raw Method* frames via a class liveness …
rkennke Oct 15, 2026
8917466
Tag raw Method* frames with a KlassRegistry epoch to reject reused Kl…
rkennke Oct 15, 2026
9c3ad33
Locate Klass::_trace_id by probing array klasses
rkennke Oct 15, 2026
133d109
Tag raw Method* frames with the holder's JFR trace ID when calibrated
rkennke Oct 15, 2026
1772384
Mark each raw frame's tag kind and validate the trace-ID offset again…
rkennke Oct 15, 2026
feded39
Harden lazy resolution against metadata reuse and redefinition
rkennke Oct 15, 2026
6ccbfa4
Validate the trace-ID offset against jdk.ClassDefine instead of execu…
rkennke Oct 15, 2026
50089d8
Cache raw frame resolutions per dump
rkennke Oct 15, 2026
76292f4
Let JVMTI validate redefinition arguments before the hooks inspect them
rkennke Oct 15, 2026
f4833eb
Stress-test lazy resolution against live class redefinition
rkennke Oct 15, 2026
85dd1ad
Drop registry-epoch tags; lazy mode requires JFR trace IDs
rkennke Oct 15, 2026
45c3dd9
Catch a malformed trace ID in LazyTraceIdJfrValidationTest's log parser
rkennke Oct 15, 2026
f428e7b
Add working plan for combining #864 and #867
rkennke Oct 16, 2026
80940ec
Resolve raw frames from VM metadata instead of allocating jmethodIDs
rkennke Oct 16, 2026
1a44b70
Update the combined plan: PR structure, phase 1 status, Linux hand-over
rkennke Oct 16, 2026
fd9dfc5
Run LazyRedefinitionStressTest on JDK 8
rkennke Oct 16, 2026
35b44ee
Count lazy frames whose Method* can no longer be read
rkennke Oct 19, 2026
ca557cb
Retry trace-ID calibration on fresh array dimensions
rkennke Oct 19, 2026
9c8acd3
Fail on unparsable child-JVM output instead of skipping
rkennke Oct 19, 2026
b13bc80
Name hidden classes as JVMTI does when resolving raw frames
rkennke Oct 19, 2026
f81edc2
Remove the working plan document
rkennke Oct 19, 2026
666dd56
Report KlassRegistry's table size and purges as counters
rkennke Oct 19, 2026
1eb2f01
Pin classes per dump instead of registering every class
rkennke Oct 19, 2026
4e73a94
Harden per-dump pinning
rkennke Oct 19, 2026
4460372
Recheck untracked redefinitions after reading a raw frame's metadata
rkennke Oct 20, 2026
c93e314
Clear the OutOfMemoryError of a failed redefinition-mark allocation
rkennke Oct 20, 2026
364df55
Harden lazy resolution against review findings
rkennke Oct 20, 2026
a212fd3
Tighten lazy-mode Java tests
rkennke Oct 20, 2026
ed6408d
Count lazy frames refused because their class is too large to scan
rkennke Oct 20, 2026
a154d17
Probe fresh classes for the trace ID; set pending exceptions aside
rkennke Oct 20, 2026
99a74ec
Merge branch 'main' into investigate/lazy-jmethodid-vm-resolution
jbachorik Oct 21, 2026
337b79b
Close the pre-lazy redefinition window; log lazy-mode fallbacks
rkennke Oct 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ddprof-lib/src/main/cpp/arguments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,13 @@ Error Arguments::parse(const char *args) {
if (value != nullptr) {
if (strcmp(value, "false") == 0) {
_force_jmethodID = false;
_lazy_jmethodID = false;
} else if (strcmp(value, "true") == 0) {
_force_jmethodID = true;
_lazy_jmethodID = false;
} else if (strcmp(value, "lazy") == 0) {
_force_jmethodID = false;
_lazy_jmethodID = true;
} else {
msg = "Invalid jmethodID creation value";
}
Expand Down
4 changes: 3 additions & 1 deletion ddprof-lib/src/main/cpp/arguments.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ class Arguments {
bool _nativesocket;
long _nativesocket_interval; // initial sampling period in nanoseconds; 0 = engine default
bool _force_jmethodID; // Load all jmethodIDs, true by default
bool _lazy_jmethodID; // fjmethodid=lazy: preload no jmethodIDs; resolve raw Method* frames from VM metadata at dump time

Arguments(bool persistent = false)
: _buf(NULL),
Expand Down Expand Up @@ -324,7 +325,8 @@ class Arguments {
_jvmtistacks(false),
_nativesocket(false),
_nativesocket_interval(0),
_force_jmethodID(true) {}
_force_jmethodID(true),
_lazy_jmethodID(false) {}

~Arguments();

Expand Down
64 changes: 63 additions & 1 deletion ddprof-lib/src/main/cpp/counters.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@
X(CALLTRACE_STORAGE_EXPANSION_SKIPPED, "calltrace_storage_expansion_skipped") \
X(LINE_NUMBER_TABLES, "line_number_tables") \
X(LINE_NUMBER_TABLE_UNREADABLE, "line_number_table_unreadable") \
/* Raw frames' line number tables dropped because malloc() failed. */ \
X(LINE_NUMBER_TABLE_ALLOC_FAILED, "line_number_table_alloc_failed") \
X(REMOTE_SYMBOLICATION_FRAMES, "remote_symbolication_frames") \
X(REMOTE_SYMBOLICATION_LIBS_WITH_BUILD_ID, "remote_symbolication_libs_with_build_id") \
X(REMOTE_SYMBOLICATION_BUILD_ID_CACHE_HITS, "remote_symbolication_build_id_cache_hits") \
Expand Down Expand Up @@ -170,10 +172,70 @@
X(METHOD_RESOLVE_FAULT_RECOVERED, "method_resolve_fault_recovered") \
/* Symbol length/body rejected during the same resolution: unreadable body, \
* empty (recycled slot), or over MAX_SYMBOL_LEN. A name that merely exceeds \
* the fixed inline buffers in hotspotSupport.cpp's ResolvedNames still \
* the fixed inline buffers in ResolvedMethod (hotspotSupport.h) still \
* resolves via its malloc fallback and does not land here. The frame \
* serializes as "unknown". */ \
X(METHOD_RESOLVE_SYMBOL_UNREADABLE, "method_resolve_symbol_unreadable") \
/* fjmethodid=lazy resolution outcomes. UNLOADED: the frame's class is not \
* among the classes the dump pins (unloaded since the sample, or the \
* holder read from a freed Method* is garbage). NOT_CURRENT: the class \
* is alive but the Method* is not among its current methods (freed or \
* redefined). Both \
* serialize as "unknown". RESOLVED: raw frames whose names were read from \
* VM metadata, once per method and tag per dump. */ \
X(METHOD_RESOLVE_LAZY_UNLOADED, "method_resolve_lazy_unloaded") \
X(METHOD_RESOLVE_LAZY_NOT_CURRENT, "method_resolve_lazy_not_current") \
/* CLASS_TOO_LARGE: like NOT_CURRENT, but the Method* was not at its \
* idnum's index and the class has too many methods to scan \
* (VMKlass::MAX_METHODS_SCANNED), so it may well be current; serializes \
* as "unknown". */ \
X(METHOD_RESOLVE_LAZY_CLASS_TOO_LARGE, "method_resolve_lazy_class_too_large") \
X(METHOD_RESOLVE_LAZY_RESOLVED, "method_resolve_lazy_resolved") \
/* STALE: the class now at the frame's holder address is a different load \
* (its trace ID differs); serializes as "unknown". \
* UNTAGGABLE: a Method* using address bits the raw-frame tag needs, or \
* an unreadable holder trace ID; the frame is stored as not walkable. */ \
X(METHOD_RESOLVE_LAZY_STALE, "method_resolve_lazy_stale") \
/* REDEFINED: raw frame of a class that has been redefined (its Method*s \
* may have been freed and reused); serializes as "unknown". */ \
X(METHOD_RESOLVE_LAZY_REDEFINED, "method_resolve_lazy_redefined") \
/* UNREADABLE: the frame's Method* no longer leads to a holder Klass* and \
* ConstMethod (the metadata was freed when its class was unloaded, or the \
* read faulted), so no class could even be looked up; serializes as \
* "unknown". Under class churn this is most of the frames lost. */ \
X(METHOD_RESOLVE_LAZY_UNREADABLE, "method_resolve_lazy_unreadable") \
/* Classes marked by the redefinition hooks (once per class per \
* redefine/retransform call). */ \
X(KLASS_REGISTRY_REDEFINITIONS_MARKED, "klass_registry_redefinitions_marked") \
/* Gauge, set by each dump that resolves lazy raw frames: loaded classes \
* whose trace ID no longer fits in a raw frame's 25-bit tag. Nonzero means trace-ID tags of two classes \
* can now collide: a frame sampled from an unloaded class could then, if \
* a newer class also took over its Method* memory, resolve to that \
* class's method. */ \
X(KLASS_TRACE_ID_BEYOND_TAG_RANGE, "klass_trace_id_beyond_tag_range") \
Comment thread
jbachorik marked this conversation as resolved.
/* Gauges, set by each dump that resolves lazy raw frames: the classes it \
* pinned (array classes included), and the nanoseconds it took to collect \
* their Klass*s. */ \
X(LAZY_DUMP_CLASSES, "lazy_dump_classes") \
X(LAZY_DUMP_PIN_NS, "lazy_dump_pin_ns") \
/* Raw Method* frame occurrences answered from Lookup's per-dump cache \
* instead of resolving them again. */ \
X(RAW_FRAME_RESOLVE_CACHE_HITS, "raw_frame_resolve_cache_hits") \
/* Redefinitions whose mark could not be recorded (no memory for it); \
* every lazy frame is refused for the rest of the process once this is \
* nonzero. */ \
X(KLASS_REGISTRY_ADD_FAILED, "klass_registry_add_failed") \
/* Gauges: KlassRegistry's table slots (24 B each; NMT does not see the \
* table) and occupied slots, live or dead. It holds only redefined \
* classes. PURGED: dead entries dropped when the table was rebuilt. */ \
X(KLASS_REGISTRY_CAPACITY, "klass_registry_capacity") \
X(KLASS_REGISTRY_ENTRIES, "klass_registry_entries") \
X(KLASS_REGISTRY_PURGED, "klass_registry_purged") \
X(WALKVM_RAW_FRAME_UNTAGGABLE, "walkvm_raw_frame_untaggable") \
/* Gauge, set at each fjmethodid=lazy start: the Klass::_trace_id offset \
* raw frames are tagged with, or -1 if it was not found (lazy mode then \
* falls back to fjmethodid=false). 0 if lazy mode was never requested. */ \
X(KLASS_TRACE_ID_OFFSET, "klass_trace_id_offset") \
/* Strict subset of SAMPLES_DROPPED_THREAD_LOCAL, not an independent count: \
* ThreadLocalDataPool::claim() increments this on capacity exhaustion, and \
* every acquireCurrent() caller that gets nullptr back -- for this or any \
Expand Down
Loading
Loading