Skip to content

Commit c5232cb

Browse files
authored
Merge pull request #4161 from DataDog/ivoanjo/prof-10967-fix-rb-obj-info-usage
[PROF-10967] Fix profiler not loading due to "rb_obj_info" symbol not found
2 parents 77b9d8f + 94104b8 commit c5232cb

File tree

7 files changed

+52
-13
lines changed

7 files changed

+52
-13
lines changed

.github/workflows/test-memory-leaks.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
- uses: actions/checkout@v4
88
- uses: ruby/setup-ruby@v1
99
with:
10-
ruby-version: 3.4.0-preview1 # TODO: Use stable version once 3.4 is out
10+
ruby-version: 3.4.0-preview2 # TODO: Use stable version once 3.4 is out
1111
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
1212
bundler: latest
1313
cache-version: v1 # bump this to invalidate cache

ext/datadog_profiling_native_extension/extconf.rb

+3
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ def skip_building_extension!(reason)
131131

132132
have_func "malloc_stats"
133133

134+
# On Ruby 2.5 and 3.3, this symbol was not visible. It is on 2.6 to 3.2, as well as 3.4+
135+
$defs << "-DNO_RB_OBJ_INFO" if RUBY_VERSION.start_with?("2.5", "3.3")
136+
134137
# On older Rubies, rb_postponed_job_preregister/rb_postponed_job_trigger did not exist
135138
$defs << "-DNO_POSTPONED_TRIGGER" if RUBY_VERSION < "3.3"
136139

ext/datadog_profiling_native_extension/private_vm_api_access.c

+2-8
Original file line numberDiff line numberDiff line change
@@ -587,16 +587,13 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, frame_info *st
587587
// Taken from upstream vm_insnhelper.c at commit 5f10bd634fb6ae8f74a4ea730176233b0ca96954 (March 2022, Ruby 3.2 trunk)
588588
// Copyright (C) 2007 Koichi Sasada
589589
// to support our custom rb_profile_frames (see above)
590-
// Modifications: None
590+
// Modifications:
591+
// * Removed debug checks (they were ifdef'd out anyway)
591592
static rb_callable_method_entry_t *
592593
check_method_entry(VALUE obj, int can_be_svar)
593594
{
594595
if (obj == Qfalse) return NULL;
595596

596-
#if VM_CHECK_MODE > 0
597-
if (!RB_TYPE_P(obj, T_IMEMO)) rb_bug("check_method_entry: unknown type: %s", rb_obj_info(obj));
598-
#endif
599-
600597
switch (imemo_type(obj)) {
601598
case imemo_ment:
602599
return (rb_callable_method_entry_t *)obj;
@@ -608,9 +605,6 @@ check_method_entry(VALUE obj, int can_be_svar)
608605
}
609606
// fallthrough
610607
default:
611-
#if VM_CHECK_MODE > 0
612-
rb_bug("check_method_entry: svar should not be there:");
613-
#endif
614608
return NULL;
615609
}
616610
}

ext/datadog_profiling_native_extension/profiling.c

+6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ static VALUE _native_trigger_holding_the_gvl_signal_handler_on(DDTRACE_UNUSED VA
3737
static VALUE _native_enforce_success(DDTRACE_UNUSED VALUE _self, VALUE syserr_errno, VALUE with_gvl);
3838
static void *trigger_enforce_success(void *trigger_args);
3939
static VALUE _native_malloc_stats(DDTRACE_UNUSED VALUE _self);
40+
static VALUE _native_safe_object_info(DDTRACE_UNUSED VALUE _self, VALUE obj);
4041

4142
void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
4243
VALUE datadog_module = rb_define_module("Datadog");
@@ -72,6 +73,7 @@ void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
7273
rb_define_singleton_method(testing_module, "_native_trigger_holding_the_gvl_signal_handler_on", _native_trigger_holding_the_gvl_signal_handler_on, 1);
7374
rb_define_singleton_method(testing_module, "_native_enforce_success", _native_enforce_success, 2);
7475
rb_define_singleton_method(testing_module, "_native_malloc_stats", _native_malloc_stats, 0);
76+
rb_define_singleton_method(testing_module, "_native_safe_object_info", _native_safe_object_info, 1);
7577
}
7678

7779
static VALUE native_working_p(DDTRACE_UNUSED VALUE _self) {
@@ -265,3 +267,7 @@ static VALUE _native_malloc_stats(DDTRACE_UNUSED VALUE _self) {
265267
return Qfalse;
266268
#endif
267269
}
270+
271+
static VALUE _native_safe_object_info(DDTRACE_UNUSED VALUE _self, VALUE obj) {
272+
return rb_str_new_cstr(safe_object_info(obj));
273+
}

ext/datadog_profiling_native_extension/ruby_helpers.c

+14-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "ruby_helpers.h"
55
#include "private_vm_api_access.h"
6+
#include "extconf.h"
67

78
// The following global variables are initialized at startup to save expensive lookups later.
89
// They are not expected to be mutated outside of init.
@@ -219,17 +220,26 @@ static bool ruby_is_obj_with_class(VALUE obj) {
219220
return false;
220221
}
221222

222-
// These two functions are not present in the VM headers, but are public symbols that can be invoked.
223+
// This function is not present in the VM headers, but is a public symbol that can be invoked.
223224
int rb_objspace_internal_object_p(VALUE obj);
224-
const char *rb_obj_info(VALUE obj);
225+
226+
#ifdef NO_RB_OBJ_INFO
227+
const char* safe_object_info(DDTRACE_UNUSED VALUE obj) { return "(No rb_obj_info for current Ruby)"; }
228+
#else
229+
// This function is a public symbol, but not on all Rubies; `safe_object_info` below abstracts this, and
230+
// should be used instead.
231+
const char *rb_obj_info(VALUE obj);
232+
233+
const char* safe_object_info(VALUE obj) { return rb_obj_info(obj); }
234+
#endif
225235

226236
VALUE ruby_safe_inspect(VALUE obj) {
227237
if (!ruby_is_obj_with_class(obj)) return rb_str_new_cstr("(Not an object)");
228-
if (rb_objspace_internal_object_p(obj)) return rb_sprintf("(VM Internal, %s)", rb_obj_info(obj));
238+
if (rb_objspace_internal_object_p(obj)) return rb_sprintf("(VM Internal, %s)", safe_object_info(obj));
229239
// @ivoanjo: I saw crashes on Ruby 3.1.4 when trying to #inspect matchdata objects. I'm not entirely sure why this
230240
// is needed, but since we only use this method for debug purposes I put in this alternative and decided not to
231241
// dig deeper.
232-
if (rb_type(obj) == RUBY_T_MATCH) return rb_sprintf("(MatchData, %s)", rb_obj_info(obj));
242+
if (rb_type(obj) == RUBY_T_MATCH) return rb_sprintf("(MatchData, %s)", safe_object_info(obj));
233243
if (rb_respond_to(obj, inspect_id)) return rb_sprintf("%+"PRIsVALUE, obj);
234244
if (rb_respond_to(obj, to_s_id)) return rb_sprintf("%"PRIsVALUE, obj);
235245

ext/datadog_profiling_native_extension/ruby_helpers.h

+4
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,7 @@ size_t ruby_obj_memsize_of(VALUE obj);
9090
// return a string with the result of that call. Elsif the object responds to
9191
// 'to_s', return a string with the result of that call. Otherwise, return Qnil.
9292
VALUE ruby_safe_inspect(VALUE obj);
93+
94+
// You probably want ruby_safe_inspect instead; this is a lower-level dependency
95+
// of it, that's being exposed here just to facilitate testing.
96+
const char* safe_object_info(VALUE obj);

spec/datadog/profiling/native_extension_spec.rb

+22
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,26 @@
183183
end
184184
end
185185
end
186+
187+
describe "safe_object_info" do
188+
let(:object_to_inspect) { "Hey, I'm a string!" }
189+
190+
subject(:safe_object_info) { described_class::Testing._native_safe_object_info(object_to_inspect) }
191+
192+
context "on a Ruby with rb_obj_info" do
193+
before { skip "Behavior does not apply to current Ruby version" if RUBY_VERSION.start_with?("2.5", "3.3") }
194+
195+
it "returns a string with information about the object" do
196+
expect(safe_object_info).to include("T_STRING")
197+
end
198+
end
199+
200+
context "on a Ruby without rb_obj_info" do
201+
before { skip "Behavior does not apply to current Ruby version" unless RUBY_VERSION.start_with?("2.5", "3.3") }
202+
203+
it "returns a placeholder string and does not otherwise fail" do
204+
expect(safe_object_info).to eq "(No rb_obj_info for current Ruby)"
205+
end
206+
end
207+
end
186208
end

0 commit comments

Comments
 (0)