diff --git a/ext/ddtrace_profiling_native_extension/heap_recorder.c b/ext/ddtrace_profiling_native_extension/heap_recorder.c index f0008abbbf4..8578dda5715 100644 --- a/ext/ddtrace_profiling_native_extension/heap_recorder.c +++ b/ext/ddtrace_profiling_native_extension/heap_recorder.c @@ -333,6 +333,24 @@ void heap_recorder_testonly_assert_hash_matches(ddog_prof_Slice_Location locatio } } +int st_object_records_debug(DDTRACE_UNUSED st_data_t key, st_data_t value, DDTRACE_UNUSED st_data_t extra) { + object_record *record = (object_record*) value; + VALUE ref; + if (!ruby_ref_from_id(LONG2NUM(record->obj_id), &ref)) { + return ST_CONTINUE; + } + + heap_frame top_frame = record->heap_record->stack->frames[0]; + VALUE str = rb_sprintf("obj_id=%ld weight=%d location=%s:%d object=%+"PRIsVALUE, record->obj_id, record->object_data.weight, top_frame.filename, (int) top_frame.line, ref); + fprintf(stderr, "%s\n", RSTRING_PTR(str)); + return ST_CONTINUE; +} + +void heap_recorder_testonly_debug(heap_recorder *heap_recorder) { + fprintf(stderr, "object records:\n"); + st_foreach(heap_recorder->object_records, st_object_records_debug, (st_data_t) NULL); +} + // ========================== // Heap Recorder Internal API // ========================== diff --git a/ext/ddtrace_profiling_native_extension/heap_recorder.h b/ext/ddtrace_profiling_native_extension/heap_recorder.h index 0b5ae5020e1..96af08ea68b 100644 --- a/ext/ddtrace_profiling_native_extension/heap_recorder.h +++ b/ext/ddtrace_profiling_native_extension/heap_recorder.h @@ -106,3 +106,5 @@ bool heap_recorder_for_each_live_object( // Assert internal hashing logic is valid for the provided locations and its // corresponding internal representations in heap recorder. void heap_recorder_testonly_assert_hash_matches(ddog_prof_Slice_Location locations); + +void heap_recorder_testonly_debug(heap_recorder *heap_recorder); diff --git a/ext/ddtrace_profiling_native_extension/stack_recorder.c b/ext/ddtrace_profiling_native_extension/stack_recorder.c index 48c27b13a04..f3ddc9e7328 100644 --- a/ext/ddtrace_profiling_native_extension/stack_recorder.c +++ b/ext/ddtrace_profiling_native_extension/stack_recorder.c @@ -234,6 +234,7 @@ static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_ins static VALUE _native_check_heap_hashes(DDTRACE_UNUSED VALUE _self, VALUE locations); static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance); static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance); +static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance); void stack_recorder_init(VALUE profiling_module) { @@ -264,6 +265,8 @@ void stack_recorder_init(VALUE profiling_module) { _native_start_fake_slow_heap_serialization, 1); rb_define_singleton_method(testing_module, "_native_end_fake_slow_heap_serialization", _native_end_fake_slow_heap_serialization, 1); + rb_define_singleton_method(testing_module, "_native_debug_heap_recorder", + _native_debug_heap_recorder, 1); ok_symbol = ID2SYM(rb_intern_const("ok")); error_symbol = ID2SYM(rb_intern_const("error")); @@ -840,3 +843,14 @@ static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self return Qnil; } + +// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec. +// It SHOULD NOT be used for other purposes. +static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) { + struct stack_recorder_state *state; + TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state); + + heap_recorder_testonly_debug(state->heap_recorder); + + return Qnil; +} diff --git a/spec/datadog/profiling/stack_recorder_spec.rb b/spec/datadog/profiling/stack_recorder_spec.rb index b0aa7d9d406..bbefcba957e 100644 --- a/spec/datadog/profiling/stack_recorder_spec.rb +++ b/spec/datadog/profiling/stack_recorder_spec.rb @@ -403,6 +403,9 @@ def sample_allocation(obj) # We sample from 2 distinct locations expect(heap_samples.size).to eq(2) + # FIXME: Remove, this is just for debugging purposes + described_class::Testing._native_debug_heap_recorder(stack_recorder) + sum_heap_samples = 0 heap_samples.each { |s| sum_heap_samples += s.values[:'heap-live-samples'] } expect(sum_heap_samples).to eq([a_string, an_array, a_hash].size * sample_rate)