3232 #endif
3333#endif
3434
35- #include <datadog/crashtracker.h>
3635#include "runtime_stacks.h"
36+ #include <datadog/crashtracker.h>
3737#include "datadog_ruby_common.h"
38+ #include "private_vm_api_access.h"
3839#include <sys/mman.h>
3940#include <unistd.h>
4041#include <errno.h>
@@ -310,58 +311,7 @@ static void ruby_runtime_stack_callback(
310311 const char * function_name = "<C method>" ;
311312 const char * file_name = "<C extension>" ;
312313
313- #ifdef RUBY_MJIT_HEADER
314- // Only attempt method entry resolution on Ruby versions with MJIT header
315- // where rb_vm_frame_method_entry is guaranteed to be available
316- const rb_callable_method_entry_t * me = rb_vm_frame_method_entry (cfp );
317- if (me && is_pointer_readable (me , sizeof (rb_callable_method_entry_t ))) {
318- if (me -> def && is_pointer_readable (me -> def , sizeof (* me -> def ))) {
319- if (me -> def -> original_id ) {
320- const char * method_name = rb_id2name (me -> def -> original_id );
321- if (method_name && is_pointer_readable (method_name , strlen (method_name ))) {
322- size_t method_name_len = strlen (method_name );
323- if (method_name_len > 0 && method_name_len < 256 ) {
324- function_name = method_name ;
325- }
326- }
327- }
328-
329- if (me -> def -> type == VM_METHOD_TYPE_CFUNC && me -> owner ) {
330- // Try to get the full class/module path
331- VALUE owner_name = Qnil ;
332- VALUE actual_owner = me -> owner ;
333-
334- // If this is a singleton class (like Fiddle's singleton class for module methods),
335- // try to get the attached object which should be the actual module or else we will
336- // just get `Module` which is not that useful to us
337- if (RB_TYPE_P (me -> owner , T_CLASS ) && FL_TEST (me -> owner , FL_SINGLETON )) {
338- VALUE attached = rb_ivar_get (me -> owner , rb_intern ("__attached__" ));
339- if (attached != Qnil ) {
340- actual_owner = attached ;
341- }
342- }
343-
344- // Get the class/module path
345- if (RB_TYPE_P (actual_owner , T_CLASS ) || RB_TYPE_P (actual_owner , T_MODULE )) {
346- owner_name = rb_class_path (actual_owner );
347- }
348-
349- // Fallback to rb_class_name if rb_class_path fails
350- if (owner_name == Qnil ) {
351- owner_name = rb_class_name (actual_owner );
352- }
353-
354- if (owner_name != Qnil ) {
355- const char * owner_str = safe_string_ptr (owner_name );
356- static char file_buffer [256 ];
357- snprintf (file_buffer , sizeof (file_buffer ), "<%s (C extension)>" , owner_str );
358- file_name = file_buffer ;
359- }
360- }
361- }
362- }
363- #else
364- // For Ruby versions without MJIT header, use our own rb_vm_frame_method_entry implementation
314+ // Resolve Ruby C frames via rb_vm_frame_method_entry (Ruby or our fallback depending on version)
365315 const rb_callable_method_entry_t * me = rb_vm_frame_method_entry (cfp );
366316 if (me && is_pointer_readable (me , sizeof (rb_callable_method_entry_t ))) {
367317 if (me -> def && is_pointer_readable (me -> def , sizeof (* me -> def ))) {
@@ -408,7 +358,6 @@ static void ruby_runtime_stack_callback(
408358 }
409359 }
410360 }
411- #endif
412361
413362 ddog_crasht_RuntimeStackFrame frame = {
414363 .type_name = char_slice_from_cstr (NULL ),
@@ -424,58 +373,6 @@ static void ruby_runtime_stack_callback(
424373 }
425374}
426375
427- // Support code for Ruby versions without MJIT header (copied from private_vm_api_access.c)
428- #ifndef RUBY_MJIT_HEADER
429-
430- #define MJIT_STATIC // No-op on older Rubies
431-
432- #ifndef FALSE
433- # define FALSE false
434- #elif FALSE
435- # error FALSE must be false
436- #endif
437-
438- #ifndef TRUE
439- # define TRUE true
440- #elif ! TRUE
441- # error TRUE must be true
442- #endif
443-
444- static rb_callable_method_entry_t *
445- check_method_entry (VALUE obj , int can_be_svar )
446- {
447- if (obj == Qfalse ) return NULL ;
448-
449- switch (imemo_type (obj )) {
450- case imemo_ment :
451- return (rb_callable_method_entry_t * )obj ;
452- case imemo_cref :
453- return NULL ;
454- case imemo_svar :
455- if (can_be_svar ) {
456- return check_method_entry (((struct vm_svar * )obj )-> cref_or_me , FALSE);
457- }
458- // fallthrough
459- default :
460- return NULL ;
461- }
462- }
463-
464- MJIT_STATIC const rb_callable_method_entry_t *
465- rb_vm_frame_method_entry (const rb_control_frame_t * cfp )
466- {
467- const VALUE * ep = cfp -> ep ;
468- rb_callable_method_entry_t * me ;
469-
470- while (!VM_ENV_LOCAL_P (ep )) {
471- if ((me = check_method_entry (ep [VM_ENV_DATA_INDEX_ME_CREF ], FALSE)) != NULL ) return me ;
472- ep = VM_ENV_PREV_EP (ep );
473- }
474-
475- return check_method_entry (ep [VM_ENV_DATA_INDEX_ME_CREF ], TRUE);
476- }
477- #endif // RUBY_MJIT_HEADER
478-
479376static VALUE _native_register_runtime_stack_callback (DDTRACE_UNUSED VALUE _self ) {
480377 enum ddog_crasht_CallbackResult result = ddog_crasht_register_runtime_frame_callback (
481378 ruby_runtime_stack_callback
@@ -495,10 +392,20 @@ static VALUE _native_is_runtime_callback_registered(DDTRACE_UNUSED VALUE _self)
495392 return ddog_crasht_is_runtime_callback_registered () ? Qtrue : Qfalse ;
496393}
497394
498- void DDTRACE_EXPORT Init_datadog_runtime_stacks (void ) {
499- VALUE datadog_module = rb_define_module ("Datadog" );
395+ void runtime_stacks_init (VALUE datadog_module ) {
500396 VALUE runtime_stacks_class = rb_define_class_under (datadog_module , "RuntimeStacks" , rb_cObject );
501397
502- rb_define_singleton_method (runtime_stacks_class , "_native_register_runtime_stack_callback" , _native_register_runtime_stack_callback , 0 );
503- rb_define_singleton_method (runtime_stacks_class , "_native_is_runtime_callback_registered" , _native_is_runtime_callback_registered , 0 );
398+ rb_define_singleton_method (
399+ runtime_stacks_class ,
400+ "_native_register_runtime_stack_callback" ,
401+ _native_register_runtime_stack_callback ,
402+ 0
403+ );
404+ rb_define_singleton_method (
405+ runtime_stacks_class ,
406+ "_native_is_runtime_callback_registered" ,
407+ _native_is_runtime_callback_registered ,
408+ 0
409+ );
504410}
411+
0 commit comments