Skip to content

Commit f850521

Browse files
committed
Add regression test for managed string storage issue
This test fails with libdatadog 16.0.1 and passes with DataDog/libdatadog#896
1 parent 37bf1a2 commit f850521

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

ext/datadog_profiling_native_extension/stack_recorder.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ static VALUE _native_is_object_recorded(DDTRACE_UNUSED VALUE _self, VALUE record
268268
static VALUE _native_heap_recorder_reset_last_update(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
269269
static VALUE _native_recorder_after_gc_step(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
270270
static VALUE _native_benchmark_intern(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE string, VALUE times, VALUE use_all);
271+
static VALUE _native_test_managed_string_storage_produces_valid_profiles(DDTRACE_UNUSED VALUE _self);
271272

272273
void stack_recorder_init(VALUE profiling_module) {
273274
VALUE stack_recorder_class = rb_define_class_under(profiling_module, "StackRecorder", rb_cObject);
@@ -303,6 +304,7 @@ void stack_recorder_init(VALUE profiling_module) {
303304
rb_define_singleton_method(testing_module, "_native_heap_recorder_reset_last_update", _native_heap_recorder_reset_last_update, 1);
304305
rb_define_singleton_method(testing_module, "_native_recorder_after_gc_step", _native_recorder_after_gc_step, 1);
305306
rb_define_singleton_method(testing_module, "_native_benchmark_intern", _native_benchmark_intern, 4);
307+
rb_define_singleton_method(testing_module, "_native_test_managed_string_storage_produces_valid_profiles", _native_test_managed_string_storage_produces_valid_profiles, 0);
306308

307309
ok_symbol = ID2SYM(rb_intern_const("ok"));
308310
error_symbol = ID2SYM(rb_intern_const("error"));
@@ -1051,3 +1053,92 @@ static VALUE _native_benchmark_intern(DDTRACE_UNUSED VALUE _self, VALUE recorder
10511053

10521054
return Qtrue;
10531055
}
1056+
1057+
// See comments in rspec test for details on what we're testing here.
1058+
static VALUE _native_test_managed_string_storage_produces_valid_profiles(DDTRACE_UNUSED VALUE _self) {
1059+
ddog_prof_ManagedStringStorageNewResult string_storage = ddog_prof_ManagedStringStorage_new();
1060+
1061+
if (string_storage.tag == DDOG_PROF_MANAGED_STRING_STORAGE_NEW_RESULT_ERR) {
1062+
rb_raise(rb_eRuntimeError, "Failed to initialize string storage: %"PRIsVALUE, get_error_details_and_drop(&string_storage.err));
1063+
}
1064+
1065+
ddog_prof_Slice_ValueType sample_types = {.ptr = all_value_types, .len = ALL_VALUE_TYPES_COUNT};
1066+
ddog_prof_Profile_NewResult profile = ddog_prof_Profile_with_string_storage(sample_types, NULL, NULL, string_storage.ok);
1067+
1068+
if (profile.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
1069+
rb_raise(rb_eRuntimeError, "Failed to initialize profile: %"PRIsVALUE, get_error_details_and_drop(&profile.err));
1070+
}
1071+
1072+
ddog_prof_ManagedStringId hello = intern_or_raise(string_storage.ok, DDOG_CHARSLICE_C("hello"));
1073+
ddog_prof_ManagedStringId world = intern_or_raise(string_storage.ok, DDOG_CHARSLICE_C("world"));
1074+
ddog_prof_ManagedStringId key = intern_or_raise(string_storage.ok, DDOG_CHARSLICE_C("key"));
1075+
1076+
int64_t metric_values[] = {1, 2, 3, 4, 5, 6, 7, 8};
1077+
ddog_prof_Label labels[] = {{.key_id = key, .str_id = key}};
1078+
1079+
ddog_prof_Location locations[] = {
1080+
(ddog_prof_Location) {
1081+
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C(""), .build_id_id = {}},
1082+
.function = {
1083+
.name = DDOG_CHARSLICE_C(""),
1084+
.name_id = hello,
1085+
.filename = DDOG_CHARSLICE_C(""),
1086+
.filename_id = world,
1087+
},
1088+
.line = 1,
1089+
}
1090+
};
1091+
1092+
ddog_prof_Profile_Result result = ddog_prof_Profile_add(
1093+
&profile.ok,
1094+
(ddog_prof_Sample) {
1095+
.locations = (ddog_prof_Slice_Location) { .ptr = locations, .len = 1},
1096+
.values = (ddog_Slice_I64) {.ptr = metric_values, .len = 8},
1097+
.labels = (ddog_prof_Slice_Label) { .ptr = labels, .len = 1 }
1098+
},
1099+
0
1100+
);
1101+
1102+
if (result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
1103+
rb_raise(rb_eArgError, "Failed to record sample: %"PRIsVALUE, get_error_details_and_drop(&result.err));
1104+
}
1105+
1106+
ddog_Timespec finish_timestamp = system_epoch_now_timespec();
1107+
ddog_prof_Profile_SerializeResult serialize_result = ddog_prof_Profile_serialize(&profile.ok, &finish_timestamp, NULL, NULL);
1108+
1109+
if (serialize_result.tag == DDOG_PROF_PROFILE_SERIALIZE_RESULT_ERR) {
1110+
rb_raise(rb_eRuntimeError, "Failed to serialize: %"PRIsVALUE, get_error_details_and_drop(&serialize_result.err));
1111+
}
1112+
1113+
ddog_prof_MaybeError advance_gen_result = ddog_prof_ManagedStringStorage_advance_gen(string_storage.ok);
1114+
1115+
if (advance_gen_result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
1116+
rb_raise(rb_eRuntimeError, "Failed to advance string storage gen: %"PRIsVALUE, get_error_details_and_drop(&advance_gen_result.some));
1117+
}
1118+
1119+
VALUE encoded_pprof_1 = ruby_string_from_vec_u8(serialize_result.ok.buffer);
1120+
1121+
result = ddog_prof_Profile_add(
1122+
&profile.ok,
1123+
(ddog_prof_Sample) {
1124+
.locations = (ddog_prof_Slice_Location) { .ptr = locations, .len = 1},
1125+
.values = (ddog_Slice_I64) {.ptr = metric_values, .len = 8},
1126+
.labels = (ddog_prof_Slice_Label) { .ptr = labels, .len = 1 }
1127+
},
1128+
0
1129+
);
1130+
1131+
if (result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
1132+
rb_raise(rb_eArgError, "Failed to record sample: %"PRIsVALUE, get_error_details_and_drop(&result.err));
1133+
}
1134+
1135+
serialize_result = ddog_prof_Profile_serialize(&profile.ok, &finish_timestamp, NULL, NULL);
1136+
1137+
if (serialize_result.tag == DDOG_PROF_PROFILE_SERIALIZE_RESULT_ERR) {
1138+
rb_raise(rb_eArgError, "Failed to serialize: %"PRIsVALUE, get_error_details_and_drop(&serialize_result.err));
1139+
}
1140+
1141+
VALUE encoded_pprof_2 = ruby_string_from_vec_u8(serialize_result.ok.buffer);
1142+
1143+
return rb_ary_new_from_args(2, encoded_pprof_1, encoded_pprof_2);
1144+
}

spec/datadog/profiling/stack_recorder_spec.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,4 +1076,25 @@ def sample_allocation(obj)
10761076
end
10771077
end
10781078
end
1079+
1080+
context "libdatadog managed string storage regression test" do
1081+
context "when reusing managed string ids across multiple profiles" do
1082+
it "produces correct profiles" do
1083+
profile1, profile2 = described_class::Testing._native_test_managed_string_storage_produces_valid_profiles
1084+
1085+
decoded_profile1 = decode_profile(profile1)
1086+
1087+
expect(decoded_profile1.string_table).to include("key", "hello", "world")
1088+
expect { samples_from_pprof(profile1) }.to_not raise_error
1089+
1090+
# Early versions of the managed string storage in datadog mistakenly omitted the strings from the string table,
1091+
# see https://github.com/DataDog/libdatadog/pull/896 for details.
1092+
1093+
decoded_profile2 = decode_profile(profile2)
1094+
1095+
expect(decoded_profile2.string_table).to include("key", "hello", "world")
1096+
expect { samples_from_pprof(profile2) }.to_not raise_error
1097+
end
1098+
end
1099+
end
10791100
end

0 commit comments

Comments
 (0)