datadog 2.26.0 → 2.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -1
- data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +2 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +7 -6
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +2 -2
- data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +3 -2
- data/ext/datadog_profiling_native_extension/collectors_stack.c +6 -5
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +16 -12
- data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +2 -2
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +48 -1
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +41 -0
- data/ext/datadog_profiling_native_extension/encoded_profile.c +2 -1
- data/ext/datadog_profiling_native_extension/heap_recorder.c +24 -24
- data/ext/datadog_profiling_native_extension/http_transport.c +10 -5
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +3 -22
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +0 -5
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +9 -8
- data/ext/datadog_profiling_native_extension/profiling.c +20 -15
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +55 -44
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +17 -5
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +8 -2
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +3 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +16 -16
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +2 -1
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +5 -2
- data/ext/libdatadog_api/crashtracker.c +5 -8
- data/ext/libdatadog_api/datadog_ruby_common.c +48 -1
- data/ext/libdatadog_api/datadog_ruby_common.h +41 -0
- data/ext/libdatadog_api/ddsketch.c +4 -8
- data/ext/libdatadog_api/feature_flags.c +5 -5
- data/ext/libdatadog_api/helpers.h +27 -0
- data/ext/libdatadog_api/init.c +4 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +8 -1
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +9 -2
- data/lib/datadog/appsec/component.rb +1 -1
- data/lib/datadog/appsec/context.rb +3 -3
- data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +47 -12
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +32 -15
- data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rest_client/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +50 -14
- data/lib/datadog/appsec/ext.rb +2 -0
- data/lib/datadog/appsec/metrics/collector.rb +8 -3
- data/lib/datadog/appsec/metrics/exporter.rb +7 -0
- data/lib/datadog/appsec/metrics/telemetry.rb +7 -2
- data/lib/datadog/appsec/metrics.rb +5 -5
- data/lib/datadog/appsec/remote.rb +4 -4
- data/lib/datadog/appsec.rb +7 -1
- data/lib/datadog/core/configuration/settings.rb +17 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +1 -0
- data/lib/datadog/core/telemetry/logger.rb +2 -0
- data/lib/datadog/core/telemetry/logging.rb +20 -2
- data/lib/datadog/profiling/component.rb +13 -0
- data/lib/datadog/profiling/exporter.rb +4 -0
- data/lib/datadog/profiling/ext/exec_monkey_patch.rb +32 -0
- data/lib/datadog/profiling/flush.rb +3 -0
- data/lib/datadog/profiling/profiler.rb +3 -5
- data/lib/datadog/profiling/scheduler.rb +8 -7
- data/lib/datadog/profiling/tag_builder.rb +1 -0
- data/lib/datadog/version.rb +1 -1
- metadata +6 -4
|
@@ -71,7 +71,7 @@ static void install_sigprof_signal_handler_internal(
|
|
|
71
71
|
);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
raise_error(
|
|
75
75
|
rb_eRuntimeError,
|
|
76
76
|
"Could not install profiling signal handler (%s): There's a pre-existing SIGPROF signal handler",
|
|
77
77
|
handler_pretty_name
|
|
@@ -95,8 +95,14 @@ static inline void toggle_sigprof_signal_handler_for_current_thread(int action)
|
|
|
95
95
|
sigset_t signals_to_toggle;
|
|
96
96
|
sigemptyset(&signals_to_toggle);
|
|
97
97
|
sigaddset(&signals_to_toggle, SIGPROF);
|
|
98
|
+
|
|
98
99
|
int error = pthread_sigmask(action, &signals_to_toggle, NULL);
|
|
99
|
-
if (error)
|
|
100
|
+
if (error) {
|
|
101
|
+
const char *message = (action == SIG_BLOCK) ?
|
|
102
|
+
"Unexpected failure in pthread_sigmask: action SIG_BLOCK" :
|
|
103
|
+
"Unexpected failure in pthread_sigmask: action SIG_UNBLOCK";
|
|
104
|
+
private_raise_syserr(error, message, message);
|
|
105
|
+
}
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
void block_sigprof_signal_handler_from_running_in_current_thread(void) {
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
#include <signal.h>
|
|
4
4
|
#include "datadog_ruby_common.h"
|
|
5
5
|
|
|
6
|
+
|
|
7
|
+
|
|
6
8
|
void empty_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
|
|
9
|
+
|
|
7
10
|
void install_sigprof_signal_handler(void (*signal_handler_function)(int, siginfo_t *, void *), const char *handler_pretty_name);
|
|
8
11
|
void replace_sigprof_signal_handler_with_empty_handler(void (*expected_existing_handler)(int, siginfo_t *, void *));
|
|
9
12
|
void remove_sigprof_signal_handler(void);
|
|
@@ -349,7 +349,7 @@ static VALUE _native_new(VALUE klass) {
|
|
|
349
349
|
ddog_prof_ManagedStringStorageNewResult string_storage = ddog_prof_ManagedStringStorage_new();
|
|
350
350
|
|
|
351
351
|
if (string_storage.tag == DDOG_PROF_MANAGED_STRING_STORAGE_NEW_RESULT_ERR) {
|
|
352
|
-
|
|
352
|
+
raise_error(rb_eRuntimeError, "Failed to initialize string storage: %"PRIsVALUE, get_error_details_and_drop(&string_storage.err));
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
state->string_storage = string_storage.ok;
|
|
@@ -383,7 +383,7 @@ static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_Val
|
|
|
383
383
|
ddog_prof_Profile_with_string_storage(sample_types, NULL /* period is optional */, state->string_storage);
|
|
384
384
|
|
|
385
385
|
if (slot_one_profile_result.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
|
|
386
|
-
|
|
386
|
+
raise_error(rb_eRuntimeError, "Failed to initialize slot one profile: %"PRIsVALUE, get_error_details_and_drop(&slot_one_profile_result.err));
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
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
|
|
|
393
393
|
|
|
394
394
|
if (slot_two_profile_result.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
|
|
395
395
|
// Note: No need to take any special care of slot one, it'll get cleaned up by stack_recorder_typed_data_free
|
|
396
|
-
|
|
396
|
+
raise_error(rb_eRuntimeError, "Failed to initialize slot two profile: %"PRIsVALUE, get_error_details_and_drop(&slot_two_profile_result.err));
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
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
|
|
|
591
591
|
|
|
592
592
|
ddog_prof_MaybeError result = args.advance_gen_result;
|
|
593
593
|
if (result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
|
|
594
|
-
|
|
594
|
+
raise_error(rb_eRuntimeError, "Failed to advance string storage gen: %"PRIsVALUE, get_error_details_and_drop(&result.some));
|
|
595
595
|
}
|
|
596
596
|
|
|
597
597
|
VALUE start = ruby_time_from(args.slot->start_timestamp);
|
|
@@ -658,7 +658,7 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
|
|
|
658
658
|
sampler_unlock_active_profile(active_slot);
|
|
659
659
|
|
|
660
660
|
if (result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
|
|
661
|
-
|
|
661
|
+
raise_error(rb_eArgError, "Failed to record sample: %"PRIsVALUE, get_error_details_and_drop(&result.err));
|
|
662
662
|
}
|
|
663
663
|
}
|
|
664
664
|
|
|
@@ -682,7 +682,7 @@ void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_
|
|
|
682
682
|
sampler_unlock_active_profile(active_slot);
|
|
683
683
|
|
|
684
684
|
if (result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
|
|
685
|
-
|
|
685
|
+
raise_error(rb_eArgError, "Failed to record endpoint: %"PRIsVALUE, get_error_details_and_drop(&result.err));
|
|
686
686
|
}
|
|
687
687
|
}
|
|
688
688
|
|
|
@@ -824,7 +824,7 @@ static locked_profile_slot sampler_lock_active_profile(stack_recorder_state *sta
|
|
|
824
824
|
}
|
|
825
825
|
|
|
826
826
|
// We already tried both multiple times, and we did not succeed. This is not expected to happen. Let's stop sampling.
|
|
827
|
-
|
|
827
|
+
raise_error(rb_eRuntimeError, "Failed to grab either mutex in sampler_lock_active_profile");
|
|
828
828
|
}
|
|
829
829
|
|
|
830
830
|
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) {
|
|
|
889
889
|
return Qtrue;
|
|
890
890
|
} else {
|
|
891
891
|
ENFORCE_SUCCESS_GVL(error);
|
|
892
|
-
|
|
892
|
+
raise_error(rb_eRuntimeError, "Failed to raise exception in test_slot_mutex_state; this should never happen");
|
|
893
893
|
}
|
|
894
894
|
}
|
|
895
895
|
|
|
@@ -941,7 +941,7 @@ static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_ins
|
|
|
941
941
|
static void reset_profile_slot(profile_slot *slot, ddog_Timespec start_timestamp) {
|
|
942
942
|
ddog_prof_Profile_Result reset_result = ddog_prof_Profile_reset(&slot->profile);
|
|
943
943
|
if (reset_result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
|
|
944
|
-
|
|
944
|
+
raise_error(rb_eRuntimeError, "Failed to reset profile: %"PRIsVALUE, get_error_details_and_drop(&reset_result.err));
|
|
945
945
|
}
|
|
946
946
|
slot->start_timestamp = start_timestamp;
|
|
947
947
|
slot->stats = (stats_slot) {};
|
|
@@ -1056,14 +1056,14 @@ static VALUE _native_test_managed_string_storage_produces_valid_profiles(DDTRACE
|
|
|
1056
1056
|
ddog_prof_ManagedStringStorageNewResult string_storage = ddog_prof_ManagedStringStorage_new();
|
|
1057
1057
|
|
|
1058
1058
|
if (string_storage.tag == DDOG_PROF_MANAGED_STRING_STORAGE_NEW_RESULT_ERR) {
|
|
1059
|
-
|
|
1059
|
+
raise_error(rb_eRuntimeError, "Failed to initialize string storage: %"PRIsVALUE, get_error_details_and_drop(&string_storage.err));
|
|
1060
1060
|
}
|
|
1061
1061
|
|
|
1062
1062
|
ddog_prof_Slice_ValueType sample_types = {.ptr = all_value_types, .len = ALL_VALUE_TYPES_COUNT};
|
|
1063
1063
|
ddog_prof_Profile_NewResult profile = ddog_prof_Profile_with_string_storage(sample_types, NULL, string_storage.ok);
|
|
1064
1064
|
|
|
1065
1065
|
if (profile.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
|
|
1066
|
-
|
|
1066
|
+
raise_error(rb_eRuntimeError, "Failed to initialize profile: %"PRIsVALUE, get_error_details_and_drop(&profile.err));
|
|
1067
1067
|
}
|
|
1068
1068
|
|
|
1069
1069
|
ddog_prof_ManagedStringId hello = intern_or_raise(string_storage.ok, DDOG_CHARSLICE_C("hello"));
|
|
@@ -1097,7 +1097,7 @@ static VALUE _native_test_managed_string_storage_produces_valid_profiles(DDTRACE
|
|
|
1097
1097
|
);
|
|
1098
1098
|
|
|
1099
1099
|
if (result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
|
|
1100
|
-
|
|
1100
|
+
raise_error(rb_eArgError, "Failed to record sample: %"PRIsVALUE, get_error_details_and_drop(&result.err));
|
|
1101
1101
|
}
|
|
1102
1102
|
|
|
1103
1103
|
ddog_Timespec finish_timestamp = system_epoch_now_timespec();
|
|
@@ -1105,13 +1105,13 @@ static VALUE _native_test_managed_string_storage_produces_valid_profiles(DDTRACE
|
|
|
1105
1105
|
ddog_prof_Profile_SerializeResult serialize_result = ddog_prof_Profile_serialize(&profile.ok, &start_timestamp, &finish_timestamp);
|
|
1106
1106
|
|
|
1107
1107
|
if (serialize_result.tag == DDOG_PROF_PROFILE_SERIALIZE_RESULT_ERR) {
|
|
1108
|
-
|
|
1108
|
+
raise_error(rb_eRuntimeError, "Failed to serialize: %"PRIsVALUE, get_error_details_and_drop(&serialize_result.err));
|
|
1109
1109
|
}
|
|
1110
1110
|
|
|
1111
1111
|
ddog_prof_MaybeError advance_gen_result = ddog_prof_ManagedStringStorage_advance_gen(string_storage.ok);
|
|
1112
1112
|
|
|
1113
1113
|
if (advance_gen_result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
|
|
1114
|
-
|
|
1114
|
+
raise_error(rb_eRuntimeError, "Failed to advance string storage gen: %"PRIsVALUE, get_error_details_and_drop(&advance_gen_result.some));
|
|
1115
1115
|
}
|
|
1116
1116
|
|
|
1117
1117
|
VALUE encoded_pprof_1 = from_ddog_prof_EncodedProfile(serialize_result.ok);
|
|
@@ -1127,13 +1127,13 @@ static VALUE _native_test_managed_string_storage_produces_valid_profiles(DDTRACE
|
|
|
1127
1127
|
);
|
|
1128
1128
|
|
|
1129
1129
|
if (result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
|
|
1130
|
-
|
|
1130
|
+
raise_error(rb_eArgError, "Failed to record sample: %"PRIsVALUE, get_error_details_and_drop(&result.err));
|
|
1131
1131
|
}
|
|
1132
1132
|
|
|
1133
1133
|
serialize_result = ddog_prof_Profile_serialize(&profile.ok, &start_timestamp, &finish_timestamp);
|
|
1134
1134
|
|
|
1135
1135
|
if (serialize_result.tag == DDOG_PROF_PROFILE_SERIALIZE_RESULT_ERR) {
|
|
1136
|
-
|
|
1136
|
+
raise_error(rb_eArgError, "Failed to serialize: %"PRIsVALUE, get_error_details_and_drop(&serialize_result.err));
|
|
1137
1137
|
}
|
|
1138
1138
|
|
|
1139
1139
|
VALUE encoded_pprof_2 = from_ddog_prof_EncodedProfile(serialize_result.ok);
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <stdbool.h>
|
|
4
4
|
|
|
5
5
|
#include "datadog_ruby_common.h"
|
|
6
|
+
#include "ruby_helpers.h"
|
|
6
7
|
#include "unsafe_api_calls_check.h"
|
|
7
8
|
#include "extconf.h"
|
|
8
9
|
|
|
@@ -21,7 +22,7 @@ void unsafe_api_calls_check_init(void) {
|
|
|
21
22
|
check_for_unsafe_api_calls_handle = rb_postponed_job_preregister(unused_flags, check_for_unsafe_api_calls, NULL);
|
|
22
23
|
|
|
23
24
|
if (check_for_unsafe_api_calls_handle == POSTPONED_JOB_HANDLE_INVALID) {
|
|
24
|
-
|
|
25
|
+
raise_error(rb_eRuntimeError, "Failed to register check_for_unsafe_api_calls_handle postponed job (got POSTPONED_JOB_HANDLE_INVALID)");
|
|
25
26
|
}
|
|
26
27
|
#endif
|
|
27
28
|
}
|
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
// Specifically, when the profiler is sampling, we're never supposed to call into Ruby code (e.g. methods
|
|
6
6
|
// implemented using Ruby code) or allocate Ruby objects.
|
|
7
7
|
// That's because those events introduce thread switch points, and really we don't the VM switching between threads
|
|
8
|
-
// in the middle of the profiler sampling.
|
|
9
|
-
//
|
|
8
|
+
// in the middle of the profiler sampling. This includes raising exceptions.
|
|
9
|
+
//
|
|
10
|
+
// Raising exceptions as the very last operation, to stop the profiler is ok, but comes a caveat: raising exceptions
|
|
11
|
+
// will fail the unsafe check. When testing exception paths, you must disable unsafe checking for that test execution.
|
|
12
|
+
// See `allow_exception` usage for how to we disable it for testing today.
|
|
10
13
|
//
|
|
11
14
|
// The above is especially true in situations such as GC profiling or allocation/heap profiling, as in those situations
|
|
12
15
|
// we can even crash the Ruby VM if we switch away at the wrong time.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#include <datadog/crashtracker.h>
|
|
3
3
|
|
|
4
4
|
#include "datadog_ruby_common.h"
|
|
5
|
+
#include "helpers.h"
|
|
5
6
|
|
|
6
7
|
static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
|
|
7
8
|
static VALUE _native_stop(DDTRACE_UNUSED VALUE _self);
|
|
@@ -41,7 +42,7 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
|
41
42
|
ENFORCE_TYPE(action, T_SYMBOL);
|
|
42
43
|
ENFORCE_TYPE(upload_timeout_seconds, T_FIXNUM);
|
|
43
44
|
|
|
44
|
-
if (action != start_action && action != update_on_fork_action)
|
|
45
|
+
if (action != start_action && action != update_on_fork_action) raise_error(rb_eArgError, "Unexpected action: %+"PRIsVALUE, action);
|
|
45
46
|
|
|
46
47
|
VALUE version = datadog_gem_version();
|
|
47
48
|
|
|
@@ -49,7 +50,7 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
|
49
50
|
// Start of exception-free zone to prevent leaks {{
|
|
50
51
|
ddog_Endpoint *endpoint = ddog_endpoint_from_url(char_slice_from_ruby_string(agent_base_url));
|
|
51
52
|
if (endpoint == NULL) {
|
|
52
|
-
|
|
53
|
+
raise_error(rb_eRuntimeError, "Failed to create endpoint from agent_base_url: %"PRIsVALUE, agent_base_url);
|
|
53
54
|
}
|
|
54
55
|
ddog_Vec_Tag tags = convert_tags(tags_as_array);
|
|
55
56
|
|
|
@@ -107,9 +108,7 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
|
107
108
|
ddog_endpoint_drop(endpoint);
|
|
108
109
|
// }} End of exception-free zone to prevent leaks
|
|
109
110
|
|
|
110
|
-
|
|
111
|
-
rb_raise(rb_eRuntimeError, "Failed to start/update the crash tracker: %"PRIsVALUE, get_error_details_and_drop(&result.err));
|
|
112
|
-
}
|
|
111
|
+
CHECK_VOID_RESULT("Failed to start/update the crash tracker", result);
|
|
113
112
|
|
|
114
113
|
return Qtrue;
|
|
115
114
|
}
|
|
@@ -117,9 +116,7 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
|
117
116
|
static VALUE _native_stop(DDTRACE_UNUSED VALUE _self) {
|
|
118
117
|
ddog_VoidResult result = ddog_crasht_disable();
|
|
119
118
|
|
|
120
|
-
|
|
121
|
-
rb_raise(rb_eRuntimeError, "Failed to stop the crash tracker: %"PRIsVALUE, get_error_details_and_drop(&result.err));
|
|
122
|
-
}
|
|
119
|
+
CHECK_VOID_RESULT("Failed to stop the crash tracker", result);
|
|
123
120
|
|
|
124
121
|
return Qtrue;
|
|
125
122
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#include "datadog_ruby_common.h"
|
|
2
|
+
#include <stdarg.h>
|
|
2
3
|
|
|
3
4
|
// IMPORTANT: Currently this file is copy-pasted between extensions. Make sure to update all versions when doing any change!
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
static ID telemetry_message_id;
|
|
7
|
+
|
|
8
|
+
void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char *function_name) {
|
|
6
9
|
rb_exc_raise(
|
|
7
10
|
rb_exc_new_str(
|
|
8
11
|
rb_eTypeError,
|
|
@@ -18,6 +21,26 @@ void raise_unexpected_type(VALUE value, const char *value_name, const char *type
|
|
|
18
21
|
);
|
|
19
22
|
}
|
|
20
23
|
|
|
24
|
+
// Raises an exception with separate telemetry-safe and detailed messages.
|
|
25
|
+
// NOTE: Raising an exception always invokes Ruby code so it requires the GVL and is not compatible with "debug_enter_unsafe_context".
|
|
26
|
+
// @see debug_enter_unsafe_context
|
|
27
|
+
void private_raise_exception(VALUE exception, const char *static_message) {
|
|
28
|
+
rb_ivar_set(exception, telemetry_message_id, rb_str_new_cstr(static_message));
|
|
29
|
+
rb_exc_raise(exception);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Helper for raising pre-formatted exceptions
|
|
33
|
+
void private_raise_error_formatted(VALUE exception_class, const char *detailed_message, const char *static_message) {
|
|
34
|
+
VALUE exception = rb_exc_new_cstr(exception_class, detailed_message);
|
|
35
|
+
private_raise_exception(exception, static_message);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Use `raise_error` the macro instead, as it provides additional argument checks.
|
|
39
|
+
void private_raise_error(VALUE exception_class, const char *fmt, ...) {
|
|
40
|
+
FORMAT_VA_ERROR_MESSAGE(detailed_message, fmt);
|
|
41
|
+
private_raise_error_formatted(exception_class, detailed_message, fmt);
|
|
42
|
+
}
|
|
43
|
+
|
|
21
44
|
VALUE datadog_gem_version(void) {
|
|
22
45
|
VALUE ddtrace_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
|
|
23
46
|
ENFORCE_TYPE(ddtrace_module, T_MODULE);
|
|
@@ -78,3 +101,27 @@ ddog_Vec_Tag convert_tags(VALUE tags_as_array) {
|
|
|
78
101
|
|
|
79
102
|
return tags;
|
|
80
103
|
}
|
|
104
|
+
|
|
105
|
+
size_t read_ddogerr_string_and_drop(ddog_Error *error, char *string, size_t capacity) {
|
|
106
|
+
if (capacity == 0 || string == NULL) {
|
|
107
|
+
// short-circuit, we can't write anything
|
|
108
|
+
ddog_Error_drop(error);
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
ddog_CharSlice error_msg_slice = ddog_Error_message(error);
|
|
113
|
+
size_t error_msg_size = error_msg_slice.len;
|
|
114
|
+
// Account for extra null char for proper cstring
|
|
115
|
+
if (error_msg_size >= capacity) {
|
|
116
|
+
// Error message too big, lets truncate it to capacity - 1 to allow for extra null at end
|
|
117
|
+
error_msg_size = capacity - 1;
|
|
118
|
+
}
|
|
119
|
+
strncpy(string, error_msg_slice.ptr, error_msg_size);
|
|
120
|
+
string[error_msg_size] = '\0';
|
|
121
|
+
ddog_Error_drop(error);
|
|
122
|
+
return error_msg_size;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
void datadog_ruby_common_init(void) {
|
|
126
|
+
telemetry_message_id = rb_intern("@telemetry_message");
|
|
127
|
+
}
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
#include <ruby.h>
|
|
6
6
|
#include <datadog/common.h>
|
|
7
7
|
|
|
8
|
+
// Must be called once during initialization
|
|
9
|
+
void datadog_ruby_common_init(void);
|
|
10
|
+
|
|
8
11
|
// Used to mark symbols to be exported to the outside of the extension.
|
|
9
12
|
// Consider very carefully before tagging a function with this.
|
|
10
13
|
#define DDTRACE_EXPORT __attribute__ ((visibility ("default")))
|
|
@@ -32,6 +35,39 @@
|
|
|
32
35
|
|
|
33
36
|
NORETURN(void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name));
|
|
34
37
|
|
|
38
|
+
// Raises an exception of the specified class with the formatted string as its message.
|
|
39
|
+
// This macro ensures that the literal string is sent for telemetry, while the formatted
|
|
40
|
+
// message is the default `Exception#message`.
|
|
41
|
+
// *Ruby exceptions not raised through this function will not be reported via telemetry.*
|
|
42
|
+
#define raise_error(exception_class, fmt, ...) \
|
|
43
|
+
private_raise_error(exception_class, "" fmt, ##__VA_ARGS__)
|
|
44
|
+
|
|
45
|
+
NORETURN(
|
|
46
|
+
void private_raise_error(VALUE exception_class, const char *fmt, ...)
|
|
47
|
+
__attribute__ ((format (printf, 2, 3)));
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Internal helper for raising pre-formatted exceptions
|
|
51
|
+
NORETURN(
|
|
52
|
+
void private_raise_error_formatted(VALUE exception_class, const char *detailed_message, const char *static_message)
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Raises an exception with separate telemetry-safe and detailed messages.
|
|
56
|
+
// NOTE: Raising an exception always invokes Ruby code so it requires the GVL and is not compatible with "debug_enter_unsafe_context".
|
|
57
|
+
// @see debug_enter_unsafe_context
|
|
58
|
+
NORETURN(
|
|
59
|
+
void private_raise_exception(VALUE exception, const char *static_message)
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
#define MAX_RAISE_MESSAGE_SIZE 256
|
|
63
|
+
|
|
64
|
+
#define FORMAT_VA_ERROR_MESSAGE(buf, fmt) \
|
|
65
|
+
char buf[MAX_RAISE_MESSAGE_SIZE]; \
|
|
66
|
+
va_list buf##_args; \
|
|
67
|
+
va_start(buf##_args, fmt); \
|
|
68
|
+
vsnprintf(buf, MAX_RAISE_MESSAGE_SIZE, fmt, buf##_args); \
|
|
69
|
+
va_end(buf##_args);
|
|
70
|
+
|
|
35
71
|
// Helper to retrieve Datadog::VERSION::STRING
|
|
36
72
|
VALUE datadog_gem_version(void);
|
|
37
73
|
|
|
@@ -61,3 +97,8 @@ static inline VALUE get_error_details_and_drop(ddog_Error *error) {
|
|
|
61
97
|
ddog_Error_drop(error);
|
|
62
98
|
return result;
|
|
63
99
|
}
|
|
100
|
+
|
|
101
|
+
// Utility function to be able to extract an error cstring from a ddog_Error.
|
|
102
|
+
// Returns the amount of characters written to string (which are necessarily
|
|
103
|
+
// bounded by capacity - 1 since the string will be null-terminated).
|
|
104
|
+
size_t read_ddogerr_string_and_drop(ddog_Error *error, char *string, size_t capacity);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#include <datadog/ddsketch.h>
|
|
3
3
|
|
|
4
4
|
#include "datadog_ruby_common.h"
|
|
5
|
+
#include "helpers.h"
|
|
5
6
|
|
|
6
7
|
static VALUE _native_new(VALUE klass);
|
|
7
8
|
static void ddsketch_free(void *ptr);
|
|
@@ -9,7 +10,6 @@ static VALUE native_add(VALUE self, VALUE point);
|
|
|
9
10
|
static VALUE native_add_with_count(VALUE self, VALUE point, VALUE count);
|
|
10
11
|
static VALUE native_count(VALUE self);
|
|
11
12
|
static VALUE native_encode(VALUE self);
|
|
12
|
-
NORETURN(static void raise_ddsketch_error(const char *message, ddog_VoidResult result));
|
|
13
13
|
|
|
14
14
|
void ddsketch_init(VALUE core_module) {
|
|
15
15
|
VALUE ddsketch_class = rb_define_class_under(core_module, "DDSketch", rb_cObject);
|
|
@@ -48,17 +48,13 @@ static void ddsketch_free(void *ptr) {
|
|
|
48
48
|
ruby_xfree(ptr);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
static void raise_ddsketch_error(const char *message, ddog_VoidResult result) {
|
|
52
|
-
rb_raise(rb_eRuntimeError, "%s: %"PRIsVALUE, message, get_error_details_and_drop(&result.err));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
51
|
static VALUE native_add(VALUE self, VALUE point) {
|
|
56
52
|
ddsketch_Handle_DDSketch *state;
|
|
57
53
|
TypedData_Get_Struct(self, ddsketch_Handle_DDSketch, &ddsketch_typed_data, state);
|
|
58
54
|
|
|
59
55
|
ddog_VoidResult result = ddog_ddsketch_add(state, NUM2DBL(point));
|
|
60
56
|
|
|
61
|
-
|
|
57
|
+
CHECK_VOID_RESULT("DDSketch add failed", result);
|
|
62
58
|
|
|
63
59
|
return self;
|
|
64
60
|
}
|
|
@@ -69,7 +65,7 @@ static VALUE native_add_with_count(VALUE self, VALUE point, VALUE count) {
|
|
|
69
65
|
|
|
70
66
|
ddog_VoidResult result = ddog_ddsketch_add_with_count(state, NUM2DBL(point), NUM2DBL(count));
|
|
71
67
|
|
|
72
|
-
|
|
68
|
+
CHECK_VOID_RESULT("DDSketch add_with_count failed", result);
|
|
73
69
|
|
|
74
70
|
return self;
|
|
75
71
|
}
|
|
@@ -81,7 +77,7 @@ static VALUE native_count(VALUE self) {
|
|
|
81
77
|
double count_out;
|
|
82
78
|
ddog_VoidResult result = ddog_ddsketch_count(state, &count_out);
|
|
83
79
|
|
|
84
|
-
|
|
80
|
+
CHECK_VOID_RESULT("DDSketch count failed", result);
|
|
85
81
|
|
|
86
82
|
return DBL2NUM(count_out);
|
|
87
83
|
}
|
|
@@ -130,7 +130,7 @@ void feature_flags_init(VALUE core_module) {
|
|
|
130
130
|
static VALUE configuration_new(VALUE klass, VALUE json_str) {
|
|
131
131
|
struct ddog_ffe_Result_HandleConfiguration result = ddog_ffe_configuration_new(borrow_str(json_str));
|
|
132
132
|
if (result.tag == DDOG_FFE_RESULT_HANDLE_CONFIGURATION_ERR_HANDLE_CONFIGURATION) {
|
|
133
|
-
|
|
133
|
+
raise_error(feature_flags_error_class, "Failed to create configuration from JSON: %"PRIsVALUE, get_error_details_and_drop(&result.err));
|
|
134
134
|
}
|
|
135
135
|
return TypedData_Wrap_Struct(klass, &configuration_data_type, result.ok);
|
|
136
136
|
}
|
|
@@ -159,7 +159,7 @@ static ddog_ffe_ExpectedFlagType expected_type_from_value(VALUE expected_type) {
|
|
|
159
159
|
} else if (id == id_float) {
|
|
160
160
|
return DDOG_FFE_EXPECTED_FLAG_TYPE_FLOAT;
|
|
161
161
|
} else {
|
|
162
|
-
|
|
162
|
+
raise_error(feature_flags_error_class, "Internal: Unexpected flag type: %"PRIsVALUE, expected_type);
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
|
|
@@ -199,7 +199,7 @@ static int evaluation_context_foreach_callback(VALUE key, VALUE value, VALUE arg
|
|
|
199
199
|
if (builder->attr_count >= builder->attr_capacity) {
|
|
200
200
|
// This should never happen because evaluation_context_from_hash()
|
|
201
201
|
// pre-allocates attr_capacity equal to iterated Hash size.
|
|
202
|
-
|
|
202
|
+
raise_error(feature_flags_error_class, "Internal: Attribute count exceeded capacity");
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
ddog_ffe_AttributePair *attr = &builder->attrs[builder->attr_count];
|
|
@@ -354,7 +354,7 @@ static VALUE resolution_details_get_raw_value(VALUE self) {
|
|
|
354
354
|
return Qnil;
|
|
355
355
|
default:
|
|
356
356
|
// This should never happen as we checked for all possible tag values.
|
|
357
|
-
|
|
357
|
+
raise_error(feature_flags_error_class, "Internal: Unexpected ResolutionDetails value tag");
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
360
|
|
|
@@ -387,7 +387,7 @@ static VALUE resolution_details_get_flag_type(VALUE self) {
|
|
|
387
387
|
return Qnil;
|
|
388
388
|
default:
|
|
389
389
|
// This should never happen as we checked for all possible tag values.
|
|
390
|
-
|
|
390
|
+
raise_error(feature_flags_error_class, "Internal: Unexpected ResolutionDetails value tag");
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
393
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "datadog_ruby_common.h"
|
|
4
|
+
|
|
5
|
+
// Raises a Ruby error if the `ddog_VoidResult` indicates an error.
|
|
6
|
+
// The error message in `result.err` is appended to the provided `message`.
|
|
7
|
+
//
|
|
8
|
+
// @param[in] message (const char *) The error message
|
|
9
|
+
// @param[in] result (ddog_VoidResult) A result to check
|
|
10
|
+
#define CHECK_VOID_RESULT(message, result) \
|
|
11
|
+
do { \
|
|
12
|
+
if (result.tag == DDOG_VOID_RESULT_ERR) { \
|
|
13
|
+
raise_lib_error(message, result); \
|
|
14
|
+
} \
|
|
15
|
+
} while (0)
|
|
16
|
+
|
|
17
|
+
// Raises a Ruby error for the error result.
|
|
18
|
+
// The error message in `result.err` is appended to the provided `message`.
|
|
19
|
+
//
|
|
20
|
+
// @param[in] message (const char *) The error message
|
|
21
|
+
// @param[in] result (struct { ddog_Error res; ... }) Any type of result
|
|
22
|
+
#define raise_lib_error(message, result) \
|
|
23
|
+
do { \
|
|
24
|
+
char error_msg[MAX_RAISE_MESSAGE_SIZE]; \
|
|
25
|
+
read_ddogerr_string_and_drop(&result.err, error_msg, MAX_RAISE_MESSAGE_SIZE); \
|
|
26
|
+
raise_error(rb_eRuntimeError, message ": %s", error_msg); \
|
|
27
|
+
} while (0)
|
data/ext/libdatadog_api/init.c
CHANGED
|
@@ -10,6 +10,10 @@ void ddsketch_init(VALUE core_module);
|
|
|
10
10
|
|
|
11
11
|
void DDTRACE_EXPORT Init_libdatadog_api(void) {
|
|
12
12
|
VALUE datadog_module = rb_define_module("Datadog");
|
|
13
|
+
|
|
14
|
+
// MUST be called before all other initialization
|
|
15
|
+
datadog_ruby_common_init();
|
|
16
|
+
|
|
13
17
|
VALUE core_module = rb_define_module_under(datadog_module, "Core");
|
|
14
18
|
|
|
15
19
|
crashtracker_init(core_module);
|
|
@@ -19,7 +19,14 @@ module Datadog
|
|
|
19
19
|
Enumerator.new do |yielder|
|
|
20
20
|
@routes.each do |route|
|
|
21
21
|
if route.dispatcher?
|
|
22
|
-
|
|
22
|
+
if route.verb.include?('|')
|
|
23
|
+
# report separate route for each method for multi-method routes
|
|
24
|
+
route.verb.split('|').each do |method|
|
|
25
|
+
yielder.yield RailsRouteSerializer.serialize(route, method_override: method)
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
yielder.yield RailsRouteSerializer.serialize(route)
|
|
29
|
+
end
|
|
23
30
|
elsif mounted_grape_app?(route.app.rack_app)
|
|
24
31
|
route.app.rack_app.routes.each do |grape_route|
|
|
25
32
|
yielder.yield GrapeRouteSerializer.serialize(grape_route, path_prefix: route.path.spec.to_s)
|
|
@@ -10,8 +10,15 @@ module Datadog
|
|
|
10
10
|
|
|
11
11
|
module_function
|
|
12
12
|
|
|
13
|
-
def serialize(route)
|
|
14
|
-
method =
|
|
13
|
+
def serialize(route, method_override: nil)
|
|
14
|
+
method = if method_override
|
|
15
|
+
method_override
|
|
16
|
+
elsif route.verb.empty?
|
|
17
|
+
"*"
|
|
18
|
+
else
|
|
19
|
+
route.verb
|
|
20
|
+
end
|
|
21
|
+
|
|
15
22
|
path = route.path.spec.to_s.delete_suffix(FORMAT_SUFFIX)
|
|
16
23
|
|
|
17
24
|
{
|
|
@@ -48,11 +48,11 @@ module Datadog
|
|
|
48
48
|
result
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
def run_rasp(type, persistent_data, ephemeral_data, timeout = WAF::LibDDWAF::DDWAF_RUN_TIMEOUT)
|
|
51
|
+
def run_rasp(type, persistent_data, ephemeral_data, timeout = WAF::LibDDWAF::DDWAF_RUN_TIMEOUT, phase: nil)
|
|
52
52
|
result = @waf_runner.run(persistent_data, ephemeral_data, timeout)
|
|
53
53
|
|
|
54
|
-
Metrics::Telemetry.report_rasp(type, result)
|
|
55
|
-
@metrics.record_rasp(result)
|
|
54
|
+
Metrics::Telemetry.report_rasp(type, result, phase: phase)
|
|
55
|
+
@metrics.record_rasp(result, type: type, phase: phase)
|
|
56
56
|
|
|
57
57
|
result
|
|
58
58
|
end
|