Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@
#include "clock_id.h"
#include "helpers.h"
#include "private_vm_api_access.h"
#include "ruby_helpers.h"
#include "time_helpers.h"

// Validate that our home-cooked pthread_id_for() matches pthread_self() for the current thread
void self_test_clock_id(void) {
rb_nativethread_id_t expected_pthread_id = pthread_self();
rb_nativethread_id_t actual_pthread_id = pthread_id_for(rb_thread_current());

if (expected_pthread_id != actual_pthread_id) rb_raise(rb_eRuntimeError, "pthread_id_for() self-test failed");
if (expected_pthread_id != actual_pthread_id) RAISE_PROFILING_TELEMETRY_SAFE("pthread_id_for() self-test failed");
}

// Safety: This function is assumed never to raise exceptions by callers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
) {
rb_raise(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
RAISE_PROFILING_TELEMETRY_SAFE("Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
}
#else
gc_finalize_deferred_workaround = objspace_ptr_for_gc_finalize_deferred_workaround();
Expand Down Expand Up @@ -472,10 +472,7 @@ static VALUE _native_sampling_loop(DDTRACE_UNUSED VALUE _self, VALUE instance) {
cpu_and_wall_time_worker_state *old_state = active_sampler_instance_state;
if (old_state != NULL) {
if (is_thread_alive(old_state->owner_thread)) {
rb_raise(
rb_eRuntimeError,
"Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread"
);
RAISE_PROFILING_TELEMETRY_SAFE("Could not start CpuAndWallTimeWorker: There's already another instance of CpuAndWallTimeWorker active in a different thread");
} else {
// The previously active thread seems to have died without cleaning up after itself.
// In this case, we can still go ahead and start the profiler BUT we make sure to disable any existing tracepoint
Expand Down Expand Up @@ -1284,7 +1281,7 @@ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {

static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error) {
// If we can't raise an immediate exception at the calling site, use the asynchronous flow through the main worker loop.
stop_state(state, rb_exc_new_cstr(rb_eRuntimeError, error));
stop_state(state, rb_exc_new_cstr(datadog_profiling_constant_error_class, error));
}

static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ static VALUE _native_new(VALUE klass) {

long now_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
if (now_ns == 0) {
rb_raise(rb_eRuntimeError, "failed to get clock time");
RAISE_PROFILING_TELEMETRY_SAFE("failed to get clock time");
}
discrete_dynamic_sampler_init(&state->sampler, "test sampler", now_ns);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <datadog/profiling.h>

#include "collectors_gc_profiling_helper.h"
#include "ruby_helpers.h"

// This helper is used by the Datadog::Profiling::Collectors::ThreadContext to profile garbage collection.
// It's tested through that class' interfaces.
Expand Down Expand Up @@ -119,7 +120,7 @@ uint8_t gc_profiling_set_metadata(ddog_prof_Label *labels, int labels_length) {
};

if (label_pos > max_label_count) {
rb_raise(rb_eRuntimeError, "BUG: gc_profiling_set_metadata unexpected label_pos (%d) > max_label_count (%d)", label_pos, max_label_count);
RAISE_PROFILING_TELEMETRY_UNSAFE("BUG: gc_profiling_set_metadata unexpected label_pos (%d) > max_label_count (%d)", label_pos, max_label_count);
}

return label_pos;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ static void *run_idle_sampling_loop(void *state_ptr) {
// Process pending action
if (next_action == ACTION_RUN) {
if (run_action_function == NULL) {
grab_gvl_and_raise(rb_eRuntimeError, "Unexpected NULL run_action_function in run_idle_sampling_loop");
grab_gvl_and_raise(datadog_profiling_constant_error_class, "Unexpected NULL run_action_function in run_idle_sampling_loop");
}

run_action_function();
Expand Down
5 changes: 3 additions & 2 deletions ext/datadog_profiling_native_extension/collectors_stack.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "datadog_ruby_common.h"
#include "private_vm_api_access.h"
#include "ruby_helpers.h"
#include "stack_recorder.h"
#include "collectors_stack.h"

Expand Down Expand Up @@ -284,11 +285,11 @@ void sample_thread(
// here, but >= 0 makes this easier to understand/debug.
bool only_wall_time = cpu_or_wall_sample && values.cpu_time_ns == 0 && values.wall_time_ns >= 0;

if (cpu_or_wall_sample && state_label == NULL) rb_raise(rb_eRuntimeError, "BUG: Unexpected missing state_label");
if (cpu_or_wall_sample && state_label == NULL) RAISE_PROFILING_TELEMETRY_SAFE("BUG: Unexpected missing state_label");

if (has_cpu_time) {
state_label->str = DDOG_CHARSLICE_C("had cpu");
if (labels.is_gvl_waiting_state) rb_raise(rb_eRuntimeError, "BUG: Unexpected combination of cpu-time with is_gvl_waiting");
if (labels.is_gvl_waiting_state) RAISE_PROFILING_TELEMETRY_SAFE("BUG: Unexpected combination of cpu-time with is_gvl_waiting");
}

int top_of_stack_position = captured_frames - 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "helpers.h"
#include "libdatadog_helpers.h"
#include "private_vm_api_access.h"
#include "ruby_helpers.h"
#include "stack_recorder.h"
#include "time_helpers.h"
#include "unsafe_api_calls_check.h"
Expand Down Expand Up @@ -831,7 +832,7 @@ VALUE thread_context_collector_sample_after_gc(VALUE self_instance) {
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);

if (state->gc_tracking.wall_time_at_previous_gc_ns == INVALID_TIME) {
rb_raise(rb_eRuntimeError, "BUG: Unexpected call to sample_after_gc without valid GC information available");
RAISE_PROFILING_TELEMETRY_SAFE("BUG: Unexpected call to sample_after_gc without valid GC information available");
}

int max_labels_needed_for_gc = 7; // Magic number gets validated inside gc_profiling_set_metadata
Expand Down Expand Up @@ -998,7 +999,7 @@ static void trigger_sample_for_thread(
// @ivoanjo: I wonder if C compilers are smart enough to statically prove this check never triggers unless someone
// changes the code erroneously and remove it entirely?
if (label_pos > max_label_count) {
rb_raise(rb_eRuntimeError, "BUG: Unexpected label_pos (%d) > max_label_count (%d)", label_pos, max_label_count);
RAISE_PROFILING_TELEMETRY_UNSAFE("BUG: Unexpected label_pos (%d) > max_label_count (%d)", label_pos, max_label_count);
}

ddog_prof_Slice_Label slice_labels = {.ptr = labels, .len = label_pos};
Expand Down Expand Up @@ -1295,7 +1296,7 @@ static long update_time_since_previous_sample(long *time_at_previous_sample_ns,
elapsed_time_ns = 0;
} else {
// We don't expect non-wall time to go backwards, so let's flag this as a bug
rb_raise(rb_eRuntimeError, "BUG: Unexpected negative elapsed_time_ns between samples");
RAISE_PROFILING_TELEMETRY_SAFE("BUG: Unexpected negative elapsed_time_ns between samples");
}
}

Expand Down Expand Up @@ -1961,7 +1962,7 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
thread_context_collector_state *state;
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);

if (!state->timeline_enabled) rb_raise(rb_eRuntimeError, "GVL profiling requires timeline to be enabled");
if (!state->timeline_enabled) RAISE_PROFILING_TELEMETRY_SAFE("GVL profiling requires timeline to be enabled");

intptr_t gvl_waiting_at = gvl_profiling_state_thread_object_get(current_thread);

Expand Down
3 changes: 2 additions & 1 deletion ext/datadog_profiling_native_extension/encoded_profile.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "encoded_profile.h"
#include "datadog_ruby_common.h"
#include "libdatadog_helpers.h"
#include "ruby_helpers.h"

// This class exists to wrap a ddog_prof_EncodedProfile into a Ruby object
// This file implements the native bits of the Datadog::Profiling::EncodedProfile class
Expand Down Expand Up @@ -41,7 +42,7 @@ VALUE from_ddog_prof_EncodedProfile(ddog_prof_EncodedProfile profile) {
static ddog_ByteSlice get_bytes(ddog_prof_EncodedProfile *state) {
ddog_prof_Result_ByteSlice raw_bytes = ddog_prof_EncodedProfile_bytes(state);
if (raw_bytes.tag == DDOG_PROF_RESULT_BYTE_SLICE_ERR_BYTE_SLICE) {
rb_raise(rb_eRuntimeError, "Failed to get bytes from profile: %"PRIsVALUE, get_error_details_and_drop(&raw_bytes.err));
RAISE_PROFILING_TELEMETRY_UNSAFE("Failed to get bytes from profile: %"PRIsVALUE, get_error_details_and_drop(&raw_bytes.err));
}
return raw_bytes.ok;
}
Expand Down
36 changes: 18 additions & 18 deletions ext/datadog_profiling_native_extension/heap_recorder.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj
}

if (heap_recorder->active_recording != NULL) {
rb_raise(rb_eRuntimeError, "Detected consecutive heap allocation recording starts without end.");
RAISE_PROFILING_TELEMETRY_SAFE("Detected consecutive heap allocation recording starts without end.");
}

if (++heap_recorder->num_recordings_skipped < heap_recorder->sample_rate ||
Expand All @@ -323,7 +323,7 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj

VALUE ruby_obj_id = rb_obj_id(new_obj);
if (!FIXNUM_P(ruby_obj_id)) {
rb_raise(rb_eRuntimeError, "Detected a bignum object id. These are not supported by heap profiling.");
RAISE_PROFILING_TELEMETRY_SAFE("Detected a bignum object id. These are not supported by heap profiling.");
}

heap_recorder->active_recording = object_record_new(
Expand Down Expand Up @@ -371,7 +371,7 @@ static VALUE end_heap_allocation_recording(VALUE protect_args) {

if (active_recording == NULL) {
// Recording ended without having been started?
rb_raise(rb_eRuntimeError, "Ended a heap recording that was not started");
RAISE_PROFILING_TELEMETRY_SAFE("Ended a heap recording that was not started");
}
// From now on, mark the global active recording as invalid so we can short-circuit at any point
// and not end up with a still active recording. the local active_recording still holds the
Expand Down Expand Up @@ -487,14 +487,14 @@ void heap_recorder_prepare_iteration(heap_recorder *heap_recorder) {

if (heap_recorder->object_records_snapshot != NULL) {
// we could trivially handle this but we raise to highlight and catch unexpected usages.
rb_raise(rb_eRuntimeError, "New heap recorder iteration prepared without the previous one having been finished.");
RAISE_PROFILING_TELEMETRY_SAFE("New heap recorder iteration prepared without the previous one having been finished.");
}

heap_recorder_update(heap_recorder, /* full_update: */ true);

heap_recorder->object_records_snapshot = st_copy(heap_recorder->object_records);
if (heap_recorder->object_records_snapshot == NULL) {
rb_raise(rb_eRuntimeError, "Failed to create heap snapshot.");
RAISE_PROFILING_TELEMETRY_SAFE("Failed to create heap snapshot.");
}
}

Expand All @@ -505,7 +505,7 @@ void heap_recorder_finish_iteration(heap_recorder *heap_recorder) {

if (heap_recorder->object_records_snapshot == NULL) {
// we could trivially handle this but we raise to highlight and catch unexpected usages.
rb_raise(rb_eRuntimeError, "Heap recorder iteration finished without having been prepared.");
RAISE_PROFILING_TELEMETRY_SAFE("Heap recorder iteration finished without having been prepared.");
}

st_free_table(heap_recorder->object_records_snapshot);
Expand Down Expand Up @@ -733,19 +733,19 @@ static void commit_recording(heap_recorder *heap_recorder, heap_record *heap_rec
// needed to fully build the object_record.
active_recording->heap_record = heap_record;
if (heap_record->num_tracked_objects == UINT32_MAX) {
rb_raise(rb_eRuntimeError, "Reached maximum number of tracked objects for heap record");
RAISE_PROFILING_TELEMETRY_SAFE("Reached maximum number of tracked objects for heap record");
}
heap_record->num_tracked_objects++;

int existing_error = st_update(heap_recorder->object_records, active_recording->obj_id, update_object_record_entry, (st_data_t) active_recording);
if (existing_error) {
object_record *existing_record = NULL;
st_lookup(heap_recorder->object_records, active_recording->obj_id, (st_data_t *) &existing_record);
if (existing_record == NULL) rb_raise(rb_eRuntimeError, "Unexpected NULL when reading existing record");
if (existing_record == NULL) RAISE_PROFILING_TELEMETRY_SAFE("Unexpected NULL when reading existing record");

VALUE existing_inspect = object_record_inspect(heap_recorder, existing_record);
VALUE new_inspect = object_record_inspect(heap_recorder, active_recording);
rb_raise(rb_eRuntimeError, "Object ids are supposed to be unique. We got 2 allocation recordings with "
RAISE_PROFILING_TELEMETRY_UNSAFE("Object ids are supposed to be unique. We got 2 allocation recordings with "
"the same id. previous={%"PRIsVALUE"} new={%"PRIsVALUE"}", existing_inspect, new_inspect);
}
}
Expand Down Expand Up @@ -781,7 +781,7 @@ static void cleanup_heap_record_if_unused(heap_recorder *heap_recorder, heap_rec
}

if (!st_delete(heap_recorder->heap_records, (st_data_t*) &heap_record, NULL)) {
rb_raise(rb_eRuntimeError, "Attempted to cleanup an untracked heap_record");
RAISE_PROFILING_TELEMETRY_SAFE("Attempted to cleanup an untracked heap_record");
};
heap_record_free(heap_recorder, heap_record);
}
Expand All @@ -791,14 +791,14 @@ static void on_committed_object_record_cleanup(heap_recorder *heap_recorder, obj
// (See PROF-10656 Datadog-internal for details). Just in case, I've sprinkled a bunch of NULL tests in this function for now.
// Once we figure out the issue we can get rid of them again.

if (heap_recorder == NULL) rb_raise(rb_eRuntimeError, "heap_recorder was NULL in on_committed_object_record_cleanup");
if (heap_recorder->heap_records == NULL) rb_raise(rb_eRuntimeError, "heap_recorder->heap_records was NULL in on_committed_object_record_cleanup");
if (record == NULL) rb_raise(rb_eRuntimeError, "record was NULL in on_committed_object_record_cleanup");
if (heap_recorder == NULL) RAISE_PROFILING_TELEMETRY_SAFE("heap_recorder was NULL in on_committed_object_record_cleanup");
if (heap_recorder->heap_records == NULL) RAISE_PROFILING_TELEMETRY_SAFE("heap_recorder->heap_records was NULL in on_committed_object_record_cleanup");
if (record == NULL) RAISE_PROFILING_TELEMETRY_SAFE("record was NULL in on_committed_object_record_cleanup");

// Starting with the associated heap record. There will now be one less tracked object pointing to it
heap_record *heap_record = record->heap_record;

if (heap_record == NULL) rb_raise(rb_eRuntimeError, "heap_record was NULL in on_committed_object_record_cleanup");
if (heap_record == NULL) RAISE_PROFILING_TELEMETRY_SAFE("heap_record was NULL in on_committed_object_record_cleanup");

heap_record->num_tracked_objects--;

Expand Down Expand Up @@ -862,7 +862,7 @@ heap_record* heap_record_new(heap_recorder *recorder, ddog_prof_Slice_Location l
uint16_t frames_len = locations.len;
if (frames_len > MAX_FRAMES_LIMIT) {
// This is not expected as MAX_FRAMES_LIMIT is shared with the stacktrace construction mechanism
rb_raise(rb_eRuntimeError, "Found stack with more than %d frames (%d)", MAX_FRAMES_LIMIT, frames_len);
RAISE_PROFILING_TELEMETRY_UNSAFE("Found stack with more than %d frames (%d)", MAX_FRAMES_LIMIT, frames_len);
}
heap_record *stack = calloc(1, sizeof(heap_record) + frames_len * sizeof(heap_frame)); // See "note on calloc vs ruby_xcalloc use" above
stack->num_tracked_objects = 0;
Expand Down Expand Up @@ -933,21 +933,21 @@ static void unintern_or_raise(heap_recorder *recorder, ddog_prof_ManagedStringId

ddog_prof_MaybeError result = ddog_prof_ManagedStringStorage_unintern(recorder->string_storage, id);
if (result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
rb_raise(rb_eRuntimeError, "Failed to unintern id: %"PRIsVALUE, get_error_details_and_drop(&result.some));
RAISE_PROFILING_TELEMETRY_UNSAFE("Failed to unintern id: %"PRIsVALUE, get_error_details_and_drop(&result.some));
}
}

static void unintern_all_or_raise(heap_recorder *recorder, ddog_prof_Slice_ManagedStringId ids) {
ddog_prof_MaybeError result = ddog_prof_ManagedStringStorage_unintern_all(recorder->string_storage, ids);
if (result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
rb_raise(rb_eRuntimeError, "Failed to unintern_all: %"PRIsVALUE, get_error_details_and_drop(&result.some));
RAISE_PROFILING_TELEMETRY_UNSAFE("Failed to unintern_all: %"PRIsVALUE, get_error_details_and_drop(&result.some));
}
}

static VALUE get_ruby_string_or_raise(heap_recorder *recorder, ddog_prof_ManagedStringId id) {
ddog_StringWrapperResult get_string_result = ddog_prof_ManagedStringStorage_get_string(recorder->string_storage, id);
if (get_string_result.tag == DDOG_STRING_WRAPPER_RESULT_ERR) {
rb_raise(rb_eRuntimeError, "Failed to get string: %"PRIsVALUE, get_error_details_and_drop(&get_string_result.err));
RAISE_PROFILING_TELEMETRY_UNSAFE("Failed to get string: %"PRIsVALUE, get_error_details_and_drop(&get_string_result.err));
}
VALUE ruby_string = ruby_string_from_vec_u8(get_string_result.ok.message);
ddog_StringWrapper_drop((ddog_StringWrapper *) &get_string_result.ok);
Expand Down
2 changes: 1 addition & 1 deletion ext/datadog_profiling_native_extension/http_transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ static ddog_prof_ProfileExporter_Result create_exporter(VALUE exporter_configura
}

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

static VALUE handle_exporter_failure(ddog_prof_ProfileExporter_Result exporter_result) {
Expand Down
5 changes: 3 additions & 2 deletions ext/datadog_profiling_native_extension/libdatadog_helpers.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "libdatadog_helpers.h"

#include <ruby.h>
#include "ruby_helpers.h"

const char *ruby_value_type_to_string(enum ruby_value_type type) {
return ruby_value_type_to_char_slice(type).ptr;
Expand Down Expand Up @@ -66,7 +67,7 @@ ddog_prof_ManagedStringId intern_or_raise(ddog_prof_ManagedStringStorage string_

ddog_prof_ManagedStringStorageInternResult intern_result = ddog_prof_ManagedStringStorage_intern(string_storage, string);
if (intern_result.tag == DDOG_PROF_MANAGED_STRING_STORAGE_INTERN_RESULT_ERR) {
rb_raise(rb_eRuntimeError, "Failed to intern string: %"PRIsVALUE, get_error_details_and_drop(&intern_result.err));
RAISE_PROFILING_TELEMETRY_UNSAFE("Failed to intern string: %"PRIsVALUE, get_error_details_and_drop(&intern_result.err));
}
return intern_result.ok;
}
Expand All @@ -79,6 +80,6 @@ void intern_all_or_raise(
) {
ddog_prof_MaybeError result = ddog_prof_ManagedStringStorage_intern_all(string_storage, strings, output_ids, output_ids_size);
if (result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
rb_raise(rb_eRuntimeError, "Failed to intern_all: %"PRIsVALUE, get_error_details_and_drop(&result.some));
RAISE_PROFILING_TELEMETRY_UNSAFE("Failed to intern_all: %"PRIsVALUE, get_error_details_and_drop(&result.some));
}
}
Loading
Loading