Skip to content

Commit 3d908d7

Browse files
committed
Implement two-tier exception strategy for profiler errors
1 parent 27067c0 commit 3d908d7

File tree

12 files changed

+86
-23
lines changed

12 files changed

+86
-23
lines changed

ext/datadog_profiling_native_extension/encoded_profile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ VALUE from_ddog_prof_EncodedProfile(ddog_prof_EncodedProfile profile) {
4141
static ddog_ByteSlice get_bytes(ddog_prof_EncodedProfile *state) {
4242
ddog_prof_Result_ByteSlice raw_bytes = ddog_prof_EncodedProfile_bytes(state);
4343
if (raw_bytes.tag == DDOG_PROF_RESULT_BYTE_SLICE_ERR_BYTE_SLICE) {
44-
rb_raise(rb_eRuntimeError, "Failed to get bytes from profile: %"PRIsVALUE, get_error_details_and_drop(&raw_bytes.err));
44+
rb_raise(datadog_profiling_internal_error_class, "Failed to get bytes from profile: %"PRIsVALUE, get_error_details_and_drop(&raw_bytes.err));
4545
}
4646
return raw_bytes.ok;
4747
}

ext/datadog_profiling_native_extension/http_transport.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ static ddog_prof_ProfileExporter_Result create_exporter(VALUE exporter_configura
112112
}
113113

114114
static void validate_token(ddog_CancellationToken token, const char *file, int line) {
115-
if (token.inner == NULL) rb_raise(rb_eRuntimeError, "Unexpected: Validation token was empty at %s:%d", file, line);
115+
if (token.inner == NULL) rb_raise(datadog_profiling_internal_error_class, "Unexpected: Validation token was empty at %s:%d", file, line);
116116
}
117117

118118
static VALUE handle_exporter_failure(ddog_prof_ProfileExporter_Result exporter_result) {

ext/datadog_profiling_native_extension/libdatadog_helpers.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ ddog_prof_ManagedStringId intern_or_raise(ddog_prof_ManagedStringStorage string_
6666

6767
ddog_prof_ManagedStringStorageInternResult intern_result = ddog_prof_ManagedStringStorage_intern(string_storage, string);
6868
if (intern_result.tag == DDOG_PROF_MANAGED_STRING_STORAGE_INTERN_RESULT_ERR) {
69-
rb_raise(rb_eRuntimeError, "Failed to intern string: %"PRIsVALUE, get_error_details_and_drop(&intern_result.err));
69+
rb_raise(datadog_profiling_internal_error_class, "Failed to intern string: %"PRIsVALUE, get_error_details_and_drop(&intern_result.err));
7070
}
7171
return intern_result.ok;
7272
}
@@ -79,6 +79,6 @@ void intern_all_or_raise(
7979
) {
8080
ddog_prof_MaybeError result = ddog_prof_ManagedStringStorage_intern_all(string_storage, strings, output_ids, output_ids_size);
8181
if (result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
82-
rb_raise(rb_eRuntimeError, "Failed to intern_all: %"PRIsVALUE, get_error_details_and_drop(&result.some));
82+
rb_raise(datadog_profiling_internal_error_class, "Failed to intern_all: %"PRIsVALUE, get_error_details_and_drop(&result.some));
8383
}
8484
}

ext/datadog_profiling_native_extension/private_vm_api_access.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ void self_test_mn_enabled(void) {
738738
return;
739739
#else
740740
if (ddtrace_get_ractor()->threads.sched.enable_mn_threads == true) {
741-
rb_raise(rb_eRuntimeError, "Ruby VM is running with RUBY_MN_THREADS=1. This is not yet supported");
741+
rb_raise(datadog_profiling_error_class, "Ruby VM is running with RUBY_MN_THREADS=1. This is not yet supported");
742742
}
743743
#endif
744744
}
@@ -871,11 +871,11 @@ bool is_raised_flag_set(VALUE thread) { return thread_struct_from_object(thread)
871871
expected_current_fiber = current_fiber_for(rb_thread_current());
872872
}
873873

874-
if (expected_current_fiber != actual_current_fiber) rb_raise(rb_eRuntimeError, "current_fiber_for() self-test failed");
874+
if (expected_current_fiber != actual_current_fiber) rb_raise(datadog_profiling_error_class, "current_fiber_for() self-test failed");
875875
}
876876
#else
877877
NORETURN(VALUE current_fiber_for(DDTRACE_UNUSED VALUE thread));
878878

879-
VALUE current_fiber_for(DDTRACE_UNUSED VALUE thread) { rb_raise(rb_eRuntimeError, "Not implemented for Ruby < 3.1"); }
879+
VALUE current_fiber_for(DDTRACE_UNUSED VALUE thread) { rb_raise(datadog_profiling_error_class, "Not implemented for Ruby < 3.1"); }
880880
void self_test_current_fiber_for(void) { } // Nothing to do
881881
#endif

ext/datadog_profiling_native_extension/profiling.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
6060
datadog_profiling_error_class = rb_const_get(profiling_module, rb_intern("ProfilingError"));
6161
rb_global_variable(&datadog_profiling_error_class);
6262

63+
// Initialize the ProfilingInternalError exception class reference
64+
// This exception class should be defined in Ruby code (lib/datadog/profiling.rb)
65+
datadog_profiling_internal_error_class = rb_const_get(profiling_module, rb_intern("ProfilingInternalError"));
66+
rb_global_variable(&datadog_profiling_internal_error_class);
67+
6368
ruby_helpers_init();
6469
collectors_cpu_and_wall_time_worker_init(profiling_module);
6570
collectors_discrete_dynamic_sampler_init(profiling_module);

ext/datadog_profiling_native_extension/ruby_helpers.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ static ID to_s_id = Qnil;
1616
// Initialized in profiling.c during extension initialization
1717
VALUE datadog_profiling_error_class = Qnil;
1818

19+
// Global reference to Datadog::Profiling::ProfilingInternalError exception class
20+
// Initialized in profiling.c during extension initialization
21+
VALUE datadog_profiling_internal_error_class = Qnil;
22+
1923
void ruby_helpers_init(void) {
2024
rb_global_variable(&module_object_space);
2125

ext/datadog_profiling_native_extension/ruby_helpers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
// This is initialized in profiling.c during extension initialization
88
extern VALUE datadog_profiling_error_class;
99

10+
// Global reference to Datadog::Profiling::ProfilingInternalError exception class
11+
// This is initialized in profiling.c during extension initialization
12+
extern VALUE datadog_profiling_internal_error_class;
13+
1014
// Initialize internal data needed by some ruby helpers. Should be called during start, before any actual
1115
// usage of ruby helpers.
1216
void ruby_helpers_init(void);

ext/datadog_profiling_native_extension/stack_recorder.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ static VALUE _native_new(VALUE klass) {
349349
ddog_prof_ManagedStringStorageNewResult string_storage = ddog_prof_ManagedStringStorage_new();
350350

351351
if (string_storage.tag == DDOG_PROF_MANAGED_STRING_STORAGE_NEW_RESULT_ERR) {
352-
rb_raise(rb_eRuntimeError, "Failed to initialize string storage: %"PRIsVALUE, get_error_details_and_drop(&string_storage.err));
352+
rb_raise(datadog_profiling_internal_error_class, "Failed to initialize string storage: %"PRIsVALUE, get_error_details_and_drop(&string_storage.err));
353353
}
354354

355355
state->string_storage = string_storage.ok;
@@ -383,7 +383,7 @@ static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_Val
383383
ddog_prof_Profile_with_string_storage(sample_types, NULL /* period is optional */, state->string_storage);
384384

385385
if (slot_one_profile_result.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
386-
rb_raise(rb_eRuntimeError, "Failed to initialize slot one profile: %"PRIsVALUE, get_error_details_and_drop(&slot_one_profile_result.err));
386+
rb_raise(datadog_profiling_internal_error_class, "Failed to initialize slot one profile: %"PRIsVALUE, get_error_details_and_drop(&slot_one_profile_result.err));
387387
}
388388

389389
state->profile_slot_one = (profile_slot) { .profile = slot_one_profile_result.ok, .start_timestamp = start_timestamp };
@@ -393,7 +393,7 @@ static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_Val
393393

394394
if (slot_two_profile_result.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
395395
// Note: No need to take any special care of slot one, it'll get cleaned up by stack_recorder_typed_data_free
396-
rb_raise(rb_eRuntimeError, "Failed to initialize slot two profile: %"PRIsVALUE, get_error_details_and_drop(&slot_two_profile_result.err));
396+
rb_raise(datadog_profiling_internal_error_class, "Failed to initialize slot two profile: %"PRIsVALUE, get_error_details_and_drop(&slot_two_profile_result.err));
397397
}
398398

399399
state->profile_slot_two = (profile_slot) { .profile = slot_two_profile_result.ok, .start_timestamp = start_timestamp };
@@ -591,7 +591,7 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
591591

592592
ddog_prof_MaybeError result = args.advance_gen_result;
593593
if (result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
594-
rb_raise(rb_eRuntimeError, "Failed to advance string storage gen: %"PRIsVALUE, get_error_details_and_drop(&result.some));
594+
rb_raise(datadog_profiling_internal_error_class, "Failed to advance string storage gen: %"PRIsVALUE, get_error_details_and_drop(&result.some));
595595
}
596596

597597
VALUE start = ruby_time_from(args.slot->start_timestamp);
@@ -824,7 +824,7 @@ static locked_profile_slot sampler_lock_active_profile(stack_recorder_state *sta
824824
}
825825

826826
// We already tried both multiple times, and we did not succeed. This is not expected to happen. Let's stop sampling.
827-
rb_raise(rb_eRuntimeError, "Failed to grab either mutex in sampler_lock_active_profile");
827+
rb_raise(datadog_profiling_error_class, "Failed to grab either mutex in sampler_lock_active_profile");
828828
}
829829

830830
static void sampler_unlock_active_profile(locked_profile_slot active_slot) {
@@ -889,7 +889,7 @@ static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot) {
889889
return Qtrue;
890890
} else {
891891
ENFORCE_SUCCESS_GVL(error);
892-
rb_raise(rb_eRuntimeError, "Failed to raise exception in test_slot_mutex_state; this should never happen");
892+
rb_raise(datadog_profiling_error_class, "Failed to raise exception in test_slot_mutex_state; this should never happen");
893893
}
894894
}
895895

@@ -941,7 +941,7 @@ static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_ins
941941
static void reset_profile_slot(profile_slot *slot, ddog_Timespec start_timestamp) {
942942
ddog_prof_Profile_Result reset_result = ddog_prof_Profile_reset(&slot->profile);
943943
if (reset_result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
944-
rb_raise(rb_eRuntimeError, "Failed to reset profile: %"PRIsVALUE, get_error_details_and_drop(&reset_result.err));
944+
rb_raise(datadog_profiling_internal_error_class, "Failed to reset profile: %"PRIsVALUE, get_error_details_and_drop(&reset_result.err));
945945
}
946946
slot->start_timestamp = start_timestamp;
947947
slot->stats = (stats_slot) {};
@@ -1056,14 +1056,14 @@ static VALUE _native_test_managed_string_storage_produces_valid_profiles(DDTRACE
10561056
ddog_prof_ManagedStringStorageNewResult string_storage = ddog_prof_ManagedStringStorage_new();
10571057

10581058
if (string_storage.tag == DDOG_PROF_MANAGED_STRING_STORAGE_NEW_RESULT_ERR) {
1059-
rb_raise(rb_eRuntimeError, "Failed to initialize string storage: %"PRIsVALUE, get_error_details_and_drop(&string_storage.err));
1059+
rb_raise(datadog_profiling_internal_error_class, "Failed to initialize string storage: %"PRIsVALUE, get_error_details_and_drop(&string_storage.err));
10601060
}
10611061

10621062
ddog_prof_Slice_ValueType sample_types = {.ptr = all_value_types, .len = ALL_VALUE_TYPES_COUNT};
10631063
ddog_prof_Profile_NewResult profile = ddog_prof_Profile_with_string_storage(sample_types, NULL, string_storage.ok);
10641064

10651065
if (profile.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
1066-
rb_raise(rb_eRuntimeError, "Failed to initialize profile: %"PRIsVALUE, get_error_details_and_drop(&profile.err));
1066+
rb_raise(datadog_profiling_internal_error_class, "Failed to initialize profile: %"PRIsVALUE, get_error_details_and_drop(&profile.err));
10671067
}
10681068

10691069
ddog_prof_ManagedStringId hello = intern_or_raise(string_storage.ok, DDOG_CHARSLICE_C("hello"));
@@ -1105,13 +1105,13 @@ static VALUE _native_test_managed_string_storage_produces_valid_profiles(DDTRACE
11051105
ddog_prof_Profile_SerializeResult serialize_result = ddog_prof_Profile_serialize(&profile.ok, &start_timestamp, &finish_timestamp);
11061106

11071107
if (serialize_result.tag == DDOG_PROF_PROFILE_SERIALIZE_RESULT_ERR) {
1108-
rb_raise(rb_eRuntimeError, "Failed to serialize: %"PRIsVALUE, get_error_details_and_drop(&serialize_result.err));
1108+
rb_raise(datadog_profiling_internal_error_class, "Failed to serialize: %"PRIsVALUE, get_error_details_and_drop(&serialize_result.err));
11091109
}
11101110

11111111
ddog_prof_MaybeError advance_gen_result = ddog_prof_ManagedStringStorage_advance_gen(string_storage.ok);
11121112

11131113
if (advance_gen_result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
1114-
rb_raise(rb_eRuntimeError, "Failed to advance string storage gen: %"PRIsVALUE, get_error_details_and_drop(&advance_gen_result.some));
1114+
rb_raise(datadog_profiling_internal_error_class, "Failed to advance string storage gen: %"PRIsVALUE, get_error_details_and_drop(&advance_gen_result.some));
11151115
}
11161116

11171117
VALUE encoded_pprof_1 = from_ddog_prof_EncodedProfile(serialize_result.ok);

ext/datadog_profiling_native_extension/unsafe_api_calls_check.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ void unsafe_api_calls_check_init(void) {
2121
check_for_unsafe_api_calls_handle = rb_postponed_job_preregister(unused_flags, check_for_unsafe_api_calls, NULL);
2222

2323
if (check_for_unsafe_api_calls_handle == POSTPONED_JOB_HANDLE_INVALID) {
24-
rb_raise(rb_eRuntimeError, "Failed to register check_for_unsafe_api_calls_handle postponed job (got POSTPONED_JOB_HANDLE_INVALID)");
24+
rb_raise(datadog_profiling_error_class, "Failed to register check_for_unsafe_api_calls_handle postponed job (got POSTPONED_JOB_HANDLE_INVALID)");
2525
}
2626
#endif
2727
}

lib/datadog/profiling.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@
77
module Datadog
88
# Datadog Continuous Profiler implementation: https://docs.datadoghq.com/profiler/
99
module Profiling
10-
# Custom exception class for profiler errors.
11-
# This exception class is used by the profiler's C code to signal errors.
12-
# Telemetry will only include exception messages for instances of this class,
13-
# ensuring that only known-safe messages (created by Datadog code) are reported.
10+
# Custom exception class for profiler errors with constant messages.
11+
# This exception class is used by the profiler's C code to signal errors with constant,
12+
# known-safe messages. Telemetry will include exception messages for instances of this class.
1413
class ProfilingError < StandardError; end
1514

15+
# Custom exception class for profiler internal errors with dynamic content.
16+
# This exception class is used for errors that include dynamic information from libdatadog
17+
# or system state. Telemetry will NOT include exception messages from this class to avoid
18+
# fingerprinting issues, but the full details are preserved for local debugging.
19+
class ProfilingInternalError < StandardError; end
20+
1621
def self.supported?
1722
unsupported_reason.nil?
1823
end

0 commit comments

Comments
 (0)