@@ -206,6 +206,7 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector
206
206
static VALUE thread_list (struct thread_context_collector_state * state );
207
207
static VALUE _native_sample_allocation (DDTRACE_UNUSED VALUE self , VALUE collector_instance , VALUE sample_weight , VALUE new_object );
208
208
static VALUE _native_new_empty_thread (VALUE self );
209
+ ddog_CharSlice ruby_value_type_to_class_name (enum ruby_value_type type );
209
210
210
211
void collectors_thread_context_init (VALUE profiling_module ) {
211
212
VALUE collectors_module = rb_define_module_under (profiling_module , "Collectors" );
@@ -1122,13 +1123,25 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
1122
1123
type == RUBY_T_SYMBOL ||
1123
1124
type == RUBY_T_FIXNUM
1124
1125
) {
1125
- const char * name = rb_obj_classname (new_object );
1126
- size_t name_length = name != NULL ? strlen (name ) : 0 ;
1127
-
1128
- if (name_length > 0 ) {
1129
- class_name = (ddog_CharSlice ) {.ptr = name , .len = name_length };
1126
+ VALUE klass = rb_class_of (new_object );
1127
+
1128
+ // Ruby sometimes plays a bit fast and loose with some of its internal objects, e.g.
1129
+ // `rb_str_tmp_frozen_acquire` allocates a string with no class (klass=0).
1130
+ // Thus, we need to make sure there's actually a class before getting its name.
1131
+
1132
+ if (klass != 0 ) {
1133
+ const char * name = rb_obj_classname (new_object );
1134
+ size_t name_length = name != NULL ? strlen (name ) : 0 ;
1135
+
1136
+ if (name_length > 0 ) {
1137
+ class_name = (ddog_CharSlice ) {.ptr = name , .len = name_length };
1138
+ } else {
1139
+ // @ivoanjo: I'm not sure this can ever happen, but just-in-case
1140
+ class_name = ruby_value_type_to_class_name (type );
1141
+ }
1130
1142
} else {
1131
- class_name = DDOG_CHARSLICE_C ("(Anonymous)" );
1143
+ // Fallback for objects with no class
1144
+ class_name = ruby_value_type_to_class_name (type );
1132
1145
}
1133
1146
} else {
1134
1147
class_name = ruby_vm_type ; // For internal things and, we just use the VM type
@@ -1164,3 +1177,29 @@ static VALUE new_empty_thread_inner(DDTRACE_UNUSED void *arg) { return Qnil; }
1164
1177
static VALUE _native_new_empty_thread (DDTRACE_UNUSED VALUE self ) {
1165
1178
return rb_thread_create (new_empty_thread_inner , NULL );
1166
1179
}
1180
+
1181
+ ddog_CharSlice ruby_value_type_to_class_name (enum ruby_value_type type ) {
1182
+ switch (type ) {
1183
+ case (RUBY_T_OBJECT ): return DDOG_CHARSLICE_C ("Object" );
1184
+ case (RUBY_T_CLASS ): return DDOG_CHARSLICE_C ("Class" );
1185
+ case (RUBY_T_MODULE ): return DDOG_CHARSLICE_C ("Module" );
1186
+ case (RUBY_T_FLOAT ): return DDOG_CHARSLICE_C ("Float" );
1187
+ case (RUBY_T_STRING ): return DDOG_CHARSLICE_C ("String" );
1188
+ case (RUBY_T_REGEXP ): return DDOG_CHARSLICE_C ("Regexp" );
1189
+ case (RUBY_T_ARRAY ): return DDOG_CHARSLICE_C ("Array" );
1190
+ case (RUBY_T_HASH ): return DDOG_CHARSLICE_C ("Hash" );
1191
+ case (RUBY_T_STRUCT ): return DDOG_CHARSLICE_C ("Struct" );
1192
+ case (RUBY_T_BIGNUM ): return DDOG_CHARSLICE_C ("Integer" );
1193
+ case (RUBY_T_FILE ): return DDOG_CHARSLICE_C ("File" );
1194
+ case (RUBY_T_DATA ): return DDOG_CHARSLICE_C ("(VM Internal, T_DATA)" );
1195
+ case (RUBY_T_MATCH ): return DDOG_CHARSLICE_C ("MatchData" );
1196
+ case (RUBY_T_COMPLEX ): return DDOG_CHARSLICE_C ("Complex" );
1197
+ case (RUBY_T_RATIONAL ): return DDOG_CHARSLICE_C ("Rational" );
1198
+ case (RUBY_T_NIL ): return DDOG_CHARSLICE_C ("NilClass" );
1199
+ case (RUBY_T_TRUE ): return DDOG_CHARSLICE_C ("TrueClass" );
1200
+ case (RUBY_T_FALSE ): return DDOG_CHARSLICE_C ("FalseClass" );
1201
+ case (RUBY_T_SYMBOL ): return DDOG_CHARSLICE_C ("Symbol" );
1202
+ case (RUBY_T_FIXNUM ): return DDOG_CHARSLICE_C ("Integer" );
1203
+ default : return DDOG_CHARSLICE_C ("(VM Internal, Missing class)" );
1204
+ }
1205
+ }
0 commit comments