@@ -19,6 +19,7 @@ struct ruby_procs_t {
1919// option is to adjust this number downwards.
2020// NOTE: the maximum size stack is FRAMES_PER_WALK_RUBY_STACK * calls to tail_call().
2121#define FRAMES_PER_WALK_RUBY_STACK 32
22+
2223// When resolving a CME, we need to traverse environment pointers until we
2324// find IMEMO_MENT. Since we can't do a while loop, we have to bound this
2425// the max encountered in experimentation on a production rails app is 6.
@@ -29,7 +30,7 @@ struct ruby_procs_t {
2930// This increases insn for the kernel verifier: all code in the ep check "loop"
3031// is M*N for instruction checks, so be extra sensitive about additions there.
3132// If we get ERR_RUBY_READ_CME_MAX_EP regularly, we may need to raise it.
32- #define MAX_EP_CHECKS 10
33+ #define MAX_EP_CHECKS 10
3334
3435// Constants related to reading a method entry
3536// https://github.com/ruby/ruby/blob/523857bfcb0f0cdfd1ed7faa09b9c59a0266e7e2/method.h#L118
@@ -271,8 +272,9 @@ static EBPF_INLINE ErrorCode read_ruby_frame(
271272 // frames will almost certainly be incorrect for Ruby versions < 2.6.
272273 frame_type = RUBY_FRAME_TYPE_CME_CFUNC ;
273274 } else if (record -> rubyUnwindState .jit_detected ) {
274- // If we detected a jit frame and are now in a cfunc, push the c frame
275- // as we can no longer unwind native anymore
275+ // JIT is active but frame pointers are not available, so we cannot unwind
276+ // through JIT frames to get back to native code. Push the cfunc inline
277+ // instead of handing off to the native unwinder.
276278 frame_type = RUBY_FRAME_TYPE_CME_CFUNC ;
277279 } else {
278280 // We save this cfp on in the "Record" entry, and when we start the unwinder
@@ -450,20 +452,19 @@ static EBPF_INLINE ErrorCode walk_ruby_stack(
450452 record -> rubyUnwindState .cfunc_saved_frame = 0 ;
451453 }
452454
453- if (
454- rubyinfo -> jit_start > 0 && record -> state .pc >= rubyinfo -> jit_start &&
455- record -> state .pc < rubyinfo -> jit_end ) {
455+ // Detect if the CPU PC is in the JIT region.
456+ bool in_jit = rubyinfo -> jit_start > 0 && record -> state .pc >= rubyinfo -> jit_start &&
457+ record -> state .pc < rubyinfo -> jit_end ;
458+
459+ if (in_jit ) {
456460 record -> rubyUnwindState .jit_detected = true;
457461
458- // If the first frame is a jit PC, the leaf ruby frame should be the jit "owner"
459- // the cpu PC is also pushed as the address,
460- // as in theory this can be used to symbolize the JIT frame later
461- if (trace -> num_frames == 0 ) {
462- ErrorCode error =
463- push_ruby (& record -> state , trace , RUBY_FRAME_TYPE_JIT , (u64 )record -> state .pc , 0 , 0 );
464- if (error ) {
465- return error ;
466- }
462+ // Push a JIT frame with the raw machine PC. This can be used to
463+ // symbolize the JIT frame via perf map later.
464+ ErrorCode jit_error =
465+ push_ruby (& record -> state , trace , RUBY_FRAME_TYPE_JIT , (u64 )record -> state .pc , 0 , 0 );
466+ if (jit_error ) {
467+ return jit_error ;
467468 }
468469 }
469470
@@ -474,8 +475,8 @@ static EBPF_INLINE ErrorCode walk_ruby_stack(
474475
475476 if (last_stack_frame <= stack_ptr ) {
476477 // We have processed all frames in the Ruby VM and can stop here.
477- // if this process has been JIT'd , the PC is invalid and we cannot resume native unwinding so
478- // we are done
478+ // If JIT was detected , the PC is in the JIT region and native
479+ // unwinding would fail, so we stop.
479480 * next_unwinder = record -> rubyUnwindState .jit_detected ? PROG_UNWIND_STOP : PROG_UNWIND_NATIVE ;
480481 goto save_state ;
481482 } else {
0 commit comments