@@ -1286,6 +1286,56 @@ func profileFrameFullLabel(classPath, label, baseLabel, methodName libpf.String,
12861286// On x86_64, frame pointers are only emitted when --yjit-perf or --yjit-perf=fp is used.
12871287// When --yjit-perf is active, YJIT also creates /tmp/perf-PID.map, which we use as the
12881288// detection signal on x86_64.
1289+ // findJITRegion detects the YJIT JIT code region from process memory mappings.
1290+ // YJIT reserves a large contiguous address range (typically 48-128 MiB) via mmap
1291+ // with PROT_NONE and then mprotects individual 16k codepages to r-x as needed.
1292+ // On systems with CONFIG_ANON_VMA_NAME, Ruby labels the region via prctl(PR_SET_VMA)
1293+ // giving it a path like "[anon:Ruby:rb_yjit_reserve_addr_space]".
1294+ // On systems without that config, we fall back to a heuristic: the first anonymous
1295+ // executable mapping (by address) is assumed to be the JIT region since YJIT
1296+ // initializes before any gems could create anonymous executable mappings.
1297+ // Returns (start, end, found).
1298+ func findJITRegion (mappings []process.RawMapping ) (uint64 , uint64 , bool ) {
1299+ var jitStart , jitEnd uint64
1300+ labelFound := false
1301+ var heuristicStart , heuristicEnd uint64
1302+ heuristicFound := false
1303+
1304+ for idx := range mappings {
1305+ m := & mappings [idx ]
1306+
1307+ // Check for prctl-labeled JIT region. These mappings may be ---p (PROT_NONE)
1308+ // or r-xp depending on whether YJIT has activated codepages in this region.
1309+ if strings .Contains (m .Path , "jit_reserve_addr_space" ) {
1310+ if ! labelFound || m .Vaddr < jitStart {
1311+ jitStart = m .Vaddr
1312+ }
1313+ if ! labelFound || m .Vaddr + m .Length > jitEnd {
1314+ jitEnd = m .Vaddr + m .Length
1315+ }
1316+ labelFound = true
1317+ continue
1318+ }
1319+
1320+ // Heuristic fallback: first anonymous executable mapping by address.
1321+ // Mappings from /proc/pid/maps are sorted by address, so the first
1322+ // match is the lowest address.
1323+ if ! heuristicFound && m .IsExecutable () && m .IsAnonymous () {
1324+ heuristicStart = m .Vaddr
1325+ heuristicEnd = m .Vaddr + m .Length
1326+ heuristicFound = true
1327+ }
1328+ }
1329+
1330+ if labelFound {
1331+ return jitStart , jitEnd , true
1332+ }
1333+ if heuristicFound {
1334+ return heuristicStart , heuristicEnd , true
1335+ }
1336+ return 0 , 0 , false
1337+ }
1338+
12891339func hasJitFramePointers (pr process.Process ) bool {
12901340 machine := pr .GetMachineData ().Machine
12911341 if machine == elf .EM_AARCH64 {
@@ -1310,33 +1360,7 @@ func (r *rubyInstance) SynchronizeMappings(ebpf interpreter.EbpfHandler,
13101360
13111361 log .Debugf ("Synchronizing ruby mappings" )
13121362
1313- // First pass: detect JIT bounds from ALL mappings (including non-executable).
1314- // Ruby reserves a large address range for JIT code via mmap and labels it with
1315- // prctl(PR_SET_VMA), giving it a path like "[anon:Ruby:rb_jit_reserve_addr_space]".
1316- // The reserved region is typically ---p (non-executable). Ruby then mprotects individual
1317- // pages to r-xp as JIT code is compiled. We need the full reserved region bounds for
1318- // jit_start/jit_end so the eBPF program can recognize any PC within the JIT range,
1319- // even as new pages are made executable.
1320- var jitStart , jitEnd uint64
1321- jitFound := false
1322- for idx := range mappings {
1323- m := & mappings [idx ]
1324- if strings .Contains (m .Path , "jit_reserve_addr_space" ) {
1325- if ! jitFound || m .Vaddr < jitStart {
1326- jitStart = m .Vaddr
1327- }
1328- if ! jitFound || m .Vaddr + m .Length > jitEnd {
1329- jitEnd = m .Vaddr + m .Length
1330- }
1331- jitFound = true
1332- }
1333- }
1334-
1335- // Second pass: register LPM prefixes for executable anonymous/JIT mappings.
1336- // This only covers r-xp pages so the eBPF unwinder is invoked for JIT code that
1337- // has actually been committed. If no labeled JIT region was found above, fall back
1338- // to heuristic detection from executable anonymous mappings.
1339- var heuristicJitMapping * process.RawMapping
1363+ // Register LPM prefixes for executable anonymous mappings.
13401364 for idx := range mappings {
13411365 m := & mappings [idx ]
13421366 if ! m .IsExecutable () || ! m .IsAnonymous () {
0 commit comments