datadog 2.8.0 → 2.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -1
- data/ext/datadog_profiling_native_extension/clock_id.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +64 -54
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +16 -16
- data/ext/datadog_profiling_native_extension/collectors_stack.c +7 -7
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +219 -122
- data/ext/datadog_profiling_native_extension/heap_recorder.h +1 -1
- data/ext/datadog_profiling_native_extension/http_transport.c +4 -4
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +3 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -1
- data/ext/datadog_profiling_native_extension/profiling.c +10 -8
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +8 -8
- data/ext/datadog_profiling_native_extension/stack_recorder.c +54 -54
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -1
- data/ext/datadog_profiling_native_extension/time_helpers.h +1 -1
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +47 -0
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +31 -0
- data/ext/libdatadog_api/crashtracker.c +3 -0
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +355 -157
- data/lib/datadog/appsec/assets/waf_rules/strict.json +62 -32
- data/lib/datadog/appsec/context.rb +54 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +7 -7
- data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +6 -6
- data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +4 -4
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +19 -28
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +5 -5
- data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +64 -96
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +10 -10
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +5 -5
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +6 -6
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -49
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +21 -32
- data/lib/datadog/appsec/contrib/rails/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +6 -6
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +41 -63
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +2 -2
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +5 -5
- data/lib/datadog/appsec/event.rb +6 -6
- data/lib/datadog/appsec/ext.rb +3 -1
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +22 -32
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +5 -5
- data/lib/datadog/appsec/processor/rule_loader.rb +0 -3
- data/lib/datadog/appsec.rb +3 -3
- data/lib/datadog/auto_instrument.rb +3 -0
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +39 -11
- data/lib/datadog/core/configuration/components.rb +4 -2
- data/lib/datadog/core/configuration.rb +1 -1
- data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
- data/lib/datadog/core/crashtracking/component.rb +1 -3
- data/lib/datadog/core/telemetry/event.rb +87 -3
- data/lib/datadog/core/telemetry/logging.rb +2 -2
- data/lib/datadog/core/telemetry/metric.rb +22 -0
- data/lib/datadog/core/telemetry/worker.rb +33 -0
- data/lib/datadog/di/base.rb +115 -0
- data/lib/datadog/di/code_tracker.rb +7 -4
- data/lib/datadog/di/component.rb +17 -11
- data/lib/datadog/di/configuration/settings.rb +11 -1
- data/lib/datadog/di/contrib/railtie.rb +15 -0
- data/lib/datadog/di/contrib.rb +26 -0
- data/lib/datadog/di/error.rb +5 -0
- data/lib/datadog/di/instrumenter.rb +39 -18
- data/lib/datadog/di/{init.rb → preload.rb} +2 -4
- data/lib/datadog/di/probe_manager.rb +4 -4
- data/lib/datadog/di/probe_notification_builder.rb +16 -2
- data/lib/datadog/di/probe_notifier_worker.rb +5 -6
- data/lib/datadog/di/remote.rb +4 -4
- data/lib/datadog/di/transport.rb +2 -4
- data/lib/datadog/di.rb +5 -108
- data/lib/datadog/kit/appsec/events.rb +3 -3
- data/lib/datadog/kit/identity.rb +4 -4
- data/lib/datadog/profiling/component.rb +55 -53
- data/lib/datadog/profiling/http_transport.rb +1 -26
- data/lib/datadog/tracing/contrib/action_cable/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/action_mailer/integration.rb +6 -2
- data/lib/datadog/tracing/contrib/action_pack/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/action_view/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/active_job/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/active_record/integration.rb +6 -2
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +3 -1
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +3 -1
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -0
- data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/auto_instrument.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/httprb/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/mongodb/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/opensearch/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/presto/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/rack/integration.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/framework.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -0
- data/lib/datadog/tracing/span.rb +12 -4
- data/lib/datadog/tracing/span_event.rb +123 -3
- data/lib/datadog/tracing/span_operation.rb +6 -0
- data/lib/datadog/tracing/transport/serializable_trace.rb +24 -6
- data/lib/datadog/version.rb +1 -1
- metadata +19 -10
- data/lib/datadog/appsec/reactive/operation.rb +0 -68
- data/lib/datadog/appsec/scope.rb +0 -58
- data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
@@ -17,7 +17,7 @@
|
|
17
17
|
typedef struct heap_recorder heap_recorder;
|
18
18
|
|
19
19
|
// Extra data associated with each live object being tracked.
|
20
|
-
typedef struct
|
20
|
+
typedef struct {
|
21
21
|
// The weight of this object from a sampling perspective.
|
22
22
|
//
|
23
23
|
// A notion of weight is preserved for each tracked object to allow for an approximate
|
@@ -13,13 +13,13 @@ static VALUE error_symbol = Qnil; // :error in Ruby
|
|
13
13
|
|
14
14
|
static VALUE library_version_string = Qnil;
|
15
15
|
|
16
|
-
struct
|
16
|
+
typedef struct {
|
17
17
|
ddog_prof_Exporter *exporter;
|
18
18
|
ddog_prof_Exporter_Request_BuildResult *build_result;
|
19
19
|
ddog_CancellationToken *cancel_token;
|
20
20
|
ddog_prof_Exporter_SendResult result;
|
21
21
|
bool send_ran;
|
22
|
-
};
|
22
|
+
} call_exporter_without_gvl_arguments;
|
23
23
|
|
24
24
|
static inline ddog_ByteSlice byte_slice_from_ruby_string(VALUE string);
|
25
25
|
static VALUE _native_validate_exporter(VALUE self, VALUE exporter_configuration);
|
@@ -165,7 +165,7 @@ static VALUE perform_export(
|
|
165
165
|
|
166
166
|
// We'll release the Global VM Lock while we're calling send, so that the Ruby VM can continue to work while this
|
167
167
|
// is pending
|
168
|
-
|
168
|
+
call_exporter_without_gvl_arguments args =
|
169
169
|
{.exporter = exporter, .build_result = &build_result, .cancel_token = cancel_token, .send_ran = false};
|
170
170
|
|
171
171
|
// We use rb_thread_call_without_gvl2 instead of rb_thread_call_without_gvl as the gvl2 variant never raises any
|
@@ -300,7 +300,7 @@ static VALUE _native_do_export(
|
|
300
300
|
}
|
301
301
|
|
302
302
|
static void *call_exporter_without_gvl(void *call_args) {
|
303
|
-
|
303
|
+
call_exporter_without_gvl_arguments *args = (call_exporter_without_gvl_arguments*) call_args;
|
304
304
|
|
305
305
|
args->result = ddog_prof_Exporter_send(args->exporter, &args->build_result->ok, args->cancel_token);
|
306
306
|
args->send_ran = true;
|
@@ -800,3 +800,6 @@ static inline int ddtrace_imemo_type(VALUE imemo) {
|
|
800
800
|
return current_thread;
|
801
801
|
}
|
802
802
|
#endif
|
803
|
+
|
804
|
+
// Is the VM smack in the middle of raising an exception?
|
805
|
+
bool is_raised_flag_set(VALUE thread) { return thread_struct_from_object(thread)->ec->raised_flag > 0; }
|
@@ -18,7 +18,7 @@ typedef struct {
|
|
18
18
|
rb_nativethread_id_t owner;
|
19
19
|
} current_gvl_owner;
|
20
20
|
|
21
|
-
typedef struct
|
21
|
+
typedef struct {
|
22
22
|
union {
|
23
23
|
struct {
|
24
24
|
VALUE iseq;
|
@@ -68,3 +68,5 @@ const char *imemo_kind(VALUE imemo);
|
|
68
68
|
|
69
69
|
#define ENFORCE_THREAD(value) \
|
70
70
|
{ if (RB_UNLIKELY(!rb_typeddata_is_kind_of(value, RTYPEDDATA_TYPE(rb_thread_current())))) raise_unexpected_type(value, ADD_QUOTES(value), "Thread", __FILE__, __LINE__, __func__); }
|
71
|
+
|
72
|
+
bool is_raised_flag_set(VALUE thread);
|
@@ -11,6 +11,7 @@
|
|
11
11
|
#include "ruby_helpers.h"
|
12
12
|
#include "setup_signal_handler.h"
|
13
13
|
#include "time_helpers.h"
|
14
|
+
#include "unsafe_api_calls_check.h"
|
14
15
|
|
15
16
|
// Each class/module here is implemented in their separate file
|
16
17
|
void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module);
|
@@ -56,6 +57,7 @@ void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
|
|
56
57
|
collectors_thread_context_init(profiling_module);
|
57
58
|
http_transport_init(profiling_module);
|
58
59
|
stack_recorder_init(profiling_module);
|
60
|
+
unsafe_api_calls_check_init();
|
59
61
|
|
60
62
|
// Hosts methods used for testing the native code using RSpec
|
61
63
|
VALUE testing_module = rb_define_module_under(native_extension_module, "Testing");
|
@@ -83,16 +85,16 @@ static VALUE native_working_p(DDTRACE_UNUSED VALUE _self) {
|
|
83
85
|
return Qtrue;
|
84
86
|
}
|
85
87
|
|
86
|
-
struct
|
88
|
+
typedef struct {
|
87
89
|
VALUE exception_class;
|
88
90
|
char *test_message;
|
89
91
|
int test_message_arg;
|
90
|
-
};
|
92
|
+
} trigger_grab_gvl_and_raise_arguments;
|
91
93
|
|
92
94
|
static VALUE _native_grab_gvl_and_raise(DDTRACE_UNUSED VALUE _self, VALUE exception_class, VALUE test_message, VALUE test_message_arg, VALUE release_gvl) {
|
93
95
|
ENFORCE_TYPE(test_message, T_STRING);
|
94
96
|
|
95
|
-
|
97
|
+
trigger_grab_gvl_and_raise_arguments args;
|
96
98
|
|
97
99
|
args.exception_class = exception_class;
|
98
100
|
args.test_message = StringValueCStr(test_message);
|
@@ -108,7 +110,7 @@ static VALUE _native_grab_gvl_and_raise(DDTRACE_UNUSED VALUE _self, VALUE except
|
|
108
110
|
}
|
109
111
|
|
110
112
|
static void *trigger_grab_gvl_and_raise(void *trigger_args) {
|
111
|
-
|
113
|
+
trigger_grab_gvl_and_raise_arguments *args = (trigger_grab_gvl_and_raise_arguments *) trigger_args;
|
112
114
|
|
113
115
|
if (args->test_message_arg >= 0) {
|
114
116
|
grab_gvl_and_raise(args->exception_class, "%s%d", args->test_message, args->test_message_arg);
|
@@ -119,16 +121,16 @@ static void *trigger_grab_gvl_and_raise(void *trigger_args) {
|
|
119
121
|
return NULL;
|
120
122
|
}
|
121
123
|
|
122
|
-
struct
|
124
|
+
typedef struct {
|
123
125
|
int syserr_errno;
|
124
126
|
char *test_message;
|
125
127
|
int test_message_arg;
|
126
|
-
};
|
128
|
+
} trigger_grab_gvl_and_raise_syserr_arguments;
|
127
129
|
|
128
130
|
static VALUE _native_grab_gvl_and_raise_syserr(DDTRACE_UNUSED VALUE _self, VALUE syserr_errno, VALUE test_message, VALUE test_message_arg, VALUE release_gvl) {
|
129
131
|
ENFORCE_TYPE(test_message, T_STRING);
|
130
132
|
|
131
|
-
|
133
|
+
trigger_grab_gvl_and_raise_syserr_arguments args;
|
132
134
|
|
133
135
|
args.syserr_errno = NUM2INT(syserr_errno);
|
134
136
|
args.test_message = StringValueCStr(test_message);
|
@@ -144,7 +146,7 @@ static VALUE _native_grab_gvl_and_raise_syserr(DDTRACE_UNUSED VALUE _self, VALUE
|
|
144
146
|
}
|
145
147
|
|
146
148
|
static void *trigger_grab_gvl_and_raise_syserr(void *trigger_args) {
|
147
|
-
|
149
|
+
trigger_grab_gvl_and_raise_syserr_arguments *args = (trigger_grab_gvl_and_raise_syserr_arguments *) trigger_args;
|
148
150
|
|
149
151
|
if (args->test_message_arg >= 0) {
|
150
152
|
grab_gvl_and_raise_syserr(args->syserr_errno, "%s%d", args->test_message, args->test_message_arg);
|
@@ -23,18 +23,18 @@ void ruby_helpers_init(void) {
|
|
23
23
|
|
24
24
|
#define MAX_RAISE_MESSAGE_SIZE 256
|
25
25
|
|
26
|
-
struct
|
26
|
+
typedef struct {
|
27
27
|
VALUE exception_class;
|
28
28
|
char exception_message[MAX_RAISE_MESSAGE_SIZE];
|
29
|
-
};
|
29
|
+
} raise_args;
|
30
30
|
|
31
31
|
static void *trigger_raise(void *raise_arguments) {
|
32
|
-
|
32
|
+
raise_args *args = (raise_args *) raise_arguments;
|
33
33
|
rb_raise(args->exception_class, "%s", args->exception_message);
|
34
34
|
}
|
35
35
|
|
36
36
|
void grab_gvl_and_raise(VALUE exception_class, const char *format_string, ...) {
|
37
|
-
|
37
|
+
raise_args args;
|
38
38
|
|
39
39
|
args.exception_class = exception_class;
|
40
40
|
|
@@ -55,18 +55,18 @@ void grab_gvl_and_raise(VALUE exception_class, const char *format_string, ...) {
|
|
55
55
|
rb_bug("[ddtrace] Unexpected: Reached the end of grab_gvl_and_raise while raising '%s'\n", args.exception_message);
|
56
56
|
}
|
57
57
|
|
58
|
-
struct
|
58
|
+
typedef struct {
|
59
59
|
int syserr_errno;
|
60
60
|
char exception_message[MAX_RAISE_MESSAGE_SIZE];
|
61
|
-
};
|
61
|
+
} syserr_raise_args;
|
62
62
|
|
63
63
|
static void *trigger_syserr_raise(void *syserr_raise_arguments) {
|
64
|
-
|
64
|
+
syserr_raise_args *args = (syserr_raise_args *) syserr_raise_arguments;
|
65
65
|
rb_syserr_fail(args->syserr_errno, args->exception_message);
|
66
66
|
}
|
67
67
|
|
68
68
|
void grab_gvl_and_raise_syserr(int syserr_errno, const char *format_string, ...) {
|
69
|
-
|
69
|
+
syserr_raise_args args;
|
70
70
|
|
71
71
|
args.syserr_errno = syserr_errno;
|
72
72
|
|
@@ -173,18 +173,18 @@ static const uint8_t all_value_types_positions[] =
|
|
173
173
|
|
174
174
|
// Struct for storing stats related to a profile in a particular slot.
|
175
175
|
// These stats will share the same lifetime as the data in that profile slot.
|
176
|
-
typedef struct
|
176
|
+
typedef struct {
|
177
177
|
// How many individual samples were recorded into this slot (un-weighted)
|
178
178
|
uint64_t recorded_samples;
|
179
179
|
} stats_slot;
|
180
180
|
|
181
|
-
typedef struct
|
181
|
+
typedef struct {
|
182
182
|
ddog_prof_Profile profile;
|
183
183
|
stats_slot stats;
|
184
184
|
} profile_slot;
|
185
185
|
|
186
186
|
// Contains native state for each instance
|
187
|
-
struct
|
187
|
+
typedef struct {
|
188
188
|
// Heap recorder instance
|
189
189
|
heap_recorder *heap_recorder;
|
190
190
|
bool heap_clean_after_gc_enabled;
|
@@ -210,17 +210,17 @@ struct stack_recorder_state {
|
|
210
210
|
long serialization_time_ns_max;
|
211
211
|
uint64_t serialization_time_ns_total;
|
212
212
|
} stats_lifetime;
|
213
|
-
};
|
213
|
+
} stack_recorder_state;
|
214
214
|
|
215
215
|
// Used to group mutex and the corresponding profile slot for easy unlocking after work is done.
|
216
|
-
typedef struct
|
216
|
+
typedef struct {
|
217
217
|
pthread_mutex_t *mutex;
|
218
218
|
profile_slot *data;
|
219
219
|
} locked_profile_slot;
|
220
220
|
|
221
|
-
struct
|
221
|
+
typedef struct {
|
222
222
|
// Set by caller
|
223
|
-
|
223
|
+
stack_recorder_state *state;
|
224
224
|
ddog_Timespec finish_timestamp;
|
225
225
|
|
226
226
|
// Set by callee
|
@@ -231,26 +231,26 @@ struct call_serialize_without_gvl_arguments {
|
|
231
231
|
|
232
232
|
// Set by both
|
233
233
|
bool serialize_ran;
|
234
|
-
};
|
234
|
+
} call_serialize_without_gvl_arguments;
|
235
235
|
|
236
236
|
static VALUE _native_new(VALUE klass);
|
237
|
-
static void initialize_slot_concurrency_control(
|
238
|
-
static void initialize_profiles(
|
237
|
+
static void initialize_slot_concurrency_control(stack_recorder_state *state);
|
238
|
+
static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types);
|
239
239
|
static void stack_recorder_typed_data_free(void *data);
|
240
240
|
static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
|
241
241
|
static VALUE _native_serialize(VALUE self, VALUE recorder_instance);
|
242
242
|
static VALUE ruby_time_from(ddog_Timespec ddprof_time);
|
243
243
|
static void *call_serialize_without_gvl(void *call_args);
|
244
|
-
static locked_profile_slot sampler_lock_active_profile(
|
244
|
+
static locked_profile_slot sampler_lock_active_profile(stack_recorder_state *state);
|
245
245
|
static void sampler_unlock_active_profile(locked_profile_slot active_slot);
|
246
|
-
static profile_slot* serializer_flip_active_and_inactive_slots(
|
246
|
+
static profile_slot* serializer_flip_active_and_inactive_slots(stack_recorder_state *state);
|
247
247
|
static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
248
248
|
static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
249
249
|
static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
250
250
|
static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot);
|
251
251
|
static ddog_Timespec system_epoch_now_timespec(void);
|
252
252
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance);
|
253
|
-
static void serializer_set_start_timestamp_for_next_profile(
|
253
|
+
static void serializer_set_start_timestamp_for_next_profile(stack_recorder_state *state, ddog_Timespec start_time);
|
254
254
|
static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint);
|
255
255
|
static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /* Can be null */);
|
256
256
|
static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE new_obj, VALUE weight, VALUE alloc_class);
|
@@ -316,7 +316,7 @@ static const rb_data_type_t stack_recorder_typed_data = {
|
|
316
316
|
};
|
317
317
|
|
318
318
|
static VALUE _native_new(VALUE klass) {
|
319
|
-
|
319
|
+
stack_recorder_state *state = ruby_xcalloc(1, sizeof(stack_recorder_state));
|
320
320
|
|
321
321
|
// Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
|
322
322
|
// being leaked.
|
@@ -354,7 +354,7 @@ static VALUE _native_new(VALUE klass) {
|
|
354
354
|
return stack_recorder;
|
355
355
|
}
|
356
356
|
|
357
|
-
static void initialize_slot_concurrency_control(
|
357
|
+
static void initialize_slot_concurrency_control(stack_recorder_state *state) {
|
358
358
|
state->mutex_slot_one = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
359
359
|
state->mutex_slot_two = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
360
360
|
|
@@ -364,7 +364,7 @@ static void initialize_slot_concurrency_control(struct stack_recorder_state *sta
|
|
364
364
|
state->active_slot = 1;
|
365
365
|
}
|
366
366
|
|
367
|
-
static void initialize_profiles(
|
367
|
+
static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types) {
|
368
368
|
ddog_prof_Profile_NewResult slot_one_profile_result =
|
369
369
|
ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
|
370
370
|
|
@@ -391,7 +391,7 @@ static void initialize_profiles(struct stack_recorder_state *state, ddog_prof_Sl
|
|
391
391
|
}
|
392
392
|
|
393
393
|
static void stack_recorder_typed_data_free(void *state_ptr) {
|
394
|
-
|
394
|
+
stack_recorder_state *state = (stack_recorder_state *) state_ptr;
|
395
395
|
|
396
396
|
pthread_mutex_destroy(&state->mutex_slot_one);
|
397
397
|
ddog_prof_Profile_drop(&state->profile_slot_one.profile);
|
@@ -426,8 +426,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
426
426
|
ENFORCE_BOOLEAN(timeline_enabled);
|
427
427
|
ENFORCE_BOOLEAN(heap_clean_after_gc_enabled);
|
428
428
|
|
429
|
-
|
430
|
-
TypedData_Get_Struct(recorder_instance,
|
429
|
+
stack_recorder_state *state;
|
430
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
431
431
|
|
432
432
|
state->heap_clean_after_gc_enabled = (heap_clean_after_gc_enabled == Qtrue);
|
433
433
|
|
@@ -517,8 +517,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
517
517
|
}
|
518
518
|
|
519
519
|
static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
520
|
-
|
521
|
-
TypedData_Get_Struct(recorder_instance,
|
520
|
+
stack_recorder_state *state;
|
521
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
522
522
|
|
523
523
|
ddog_Timespec finish_timestamp = system_epoch_now_timespec();
|
524
524
|
// Need to do this while still holding on to the Global VM Lock; see comments on method for why
|
@@ -532,7 +532,7 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
|
|
532
532
|
|
533
533
|
// We'll release the Global VM Lock while we're calling serialize, so that the Ruby VM can continue to work while this
|
534
534
|
// is pending
|
535
|
-
|
535
|
+
call_serialize_without_gvl_arguments args = {
|
536
536
|
.state = state,
|
537
537
|
.finish_timestamp = finish_timestamp,
|
538
538
|
.serialize_ran = false
|
@@ -597,8 +597,8 @@ static VALUE ruby_time_from(ddog_Timespec ddprof_time) {
|
|
597
597
|
}
|
598
598
|
|
599
599
|
void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, sample_values values, sample_labels labels) {
|
600
|
-
|
601
|
-
TypedData_Get_Struct(recorder_instance,
|
600
|
+
stack_recorder_state *state;
|
601
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
602
602
|
|
603
603
|
locked_profile_slot active_slot = sampler_lock_active_profile(state);
|
604
604
|
|
@@ -652,8 +652,8 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
|
|
652
652
|
}
|
653
653
|
|
654
654
|
void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight, ddog_CharSlice *alloc_class) {
|
655
|
-
|
656
|
-
TypedData_Get_Struct(recorder_instance,
|
655
|
+
stack_recorder_state *state;
|
656
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
657
657
|
// FIXME: Heap sampling currently has to be done in 2 parts because the construction of locations is happening
|
658
658
|
// very late in the allocation-sampling path (which is shared with the cpu sampling path). This can
|
659
659
|
// be fixed with some refactoring but for now this leads to a less impactful change.
|
@@ -661,8 +661,8 @@ void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample
|
|
661
661
|
}
|
662
662
|
|
663
663
|
void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_CharSlice endpoint) {
|
664
|
-
|
665
|
-
TypedData_Get_Struct(recorder_instance,
|
664
|
+
stack_recorder_state *state;
|
665
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
666
666
|
|
667
667
|
locked_profile_slot active_slot = sampler_lock_active_profile(state);
|
668
668
|
|
@@ -676,8 +676,8 @@ void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_
|
|
676
676
|
}
|
677
677
|
|
678
678
|
void recorder_after_gc_step(VALUE recorder_instance) {
|
679
|
-
|
680
|
-
TypedData_Get_Struct(recorder_instance,
|
679
|
+
stack_recorder_state *state;
|
680
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
681
681
|
|
682
682
|
if (state->heap_clean_after_gc_enabled) heap_recorder_update_young_objects(state->heap_recorder);
|
683
683
|
}
|
@@ -687,7 +687,7 @@ void recorder_after_gc_step(VALUE recorder_instance) {
|
|
687
687
|
// Heap recorder iteration context allows us access to stack recorder state and profile being serialized
|
688
688
|
// during iteration of heap recorder live objects.
|
689
689
|
typedef struct heap_recorder_iteration_context {
|
690
|
-
|
690
|
+
stack_recorder_state *state;
|
691
691
|
profile_slot *slot;
|
692
692
|
|
693
693
|
bool error;
|
@@ -749,7 +749,7 @@ static bool add_heap_sample_to_active_profile_without_gvl(heap_recorder_iteratio
|
|
749
749
|
return true;
|
750
750
|
}
|
751
751
|
|
752
|
-
static void build_heap_profile_without_gvl(
|
752
|
+
static void build_heap_profile_without_gvl(stack_recorder_state *state, profile_slot *slot) {
|
753
753
|
heap_recorder_iteration_context iteration_context = {
|
754
754
|
.state = state,
|
755
755
|
.slot = slot,
|
@@ -770,7 +770,7 @@ static void build_heap_profile_without_gvl(struct stack_recorder_state *state, p
|
|
770
770
|
}
|
771
771
|
|
772
772
|
static void *call_serialize_without_gvl(void *call_args) {
|
773
|
-
|
773
|
+
call_serialize_without_gvl_arguments *args = (call_serialize_without_gvl_arguments *) call_args;
|
774
774
|
|
775
775
|
long serialize_no_gvl_start_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
776
776
|
|
@@ -796,7 +796,7 @@ VALUE enforce_recorder_instance(VALUE object) {
|
|
796
796
|
return object;
|
797
797
|
}
|
798
798
|
|
799
|
-
static locked_profile_slot sampler_lock_active_profile(
|
799
|
+
static locked_profile_slot sampler_lock_active_profile(stack_recorder_state *state) {
|
800
800
|
int error;
|
801
801
|
|
802
802
|
for (int attempts = 0; attempts < 2; attempts++) {
|
@@ -823,7 +823,7 @@ static void sampler_unlock_active_profile(locked_profile_slot active_slot) {
|
|
823
823
|
ENFORCE_SUCCESS_GVL(pthread_mutex_unlock(active_slot.mutex));
|
824
824
|
}
|
825
825
|
|
826
|
-
static profile_slot* serializer_flip_active_and_inactive_slots(
|
826
|
+
static profile_slot* serializer_flip_active_and_inactive_slots(stack_recorder_state *state) {
|
827
827
|
int previously_active_slot = state->active_slot;
|
828
828
|
|
829
829
|
if (previously_active_slot != 1 && previously_active_slot != 2) {
|
@@ -849,8 +849,8 @@ static profile_slot* serializer_flip_active_and_inactive_slots(struct stack_reco
|
|
849
849
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
850
850
|
// It SHOULD NOT be used for other purposes.
|
851
851
|
static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
852
|
-
|
853
|
-
TypedData_Get_Struct(recorder_instance,
|
852
|
+
stack_recorder_state *state;
|
853
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
854
854
|
|
855
855
|
return INT2NUM(state->active_slot);
|
856
856
|
}
|
@@ -864,8 +864,8 @@ static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE
|
|
864
864
|
static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) { return test_slot_mutex_state(recorder_instance, 2); }
|
865
865
|
|
866
866
|
static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot) {
|
867
|
-
|
868
|
-
TypedData_Get_Struct(recorder_instance,
|
867
|
+
stack_recorder_state *state;
|
868
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
869
869
|
|
870
870
|
pthread_mutex_t *slot_mutex = (slot == 1) ? &state->mutex_slot_one : &state->mutex_slot_two;
|
871
871
|
|
@@ -895,8 +895,8 @@ static ddog_Timespec system_epoch_now_timespec(void) {
|
|
895
895
|
// Assumption: This method gets called BEFORE restarting profiling -- e.g. there are no components attempting to
|
896
896
|
// trigger samples at the same time.
|
897
897
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance) {
|
898
|
-
|
899
|
-
TypedData_Get_Struct(recorder_instance,
|
898
|
+
stack_recorder_state *state;
|
899
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
900
900
|
|
901
901
|
// In case the fork happened halfway through `serializer_flip_active_and_inactive_slots` execution and the
|
902
902
|
// resulting state is inconsistent, we make sure to reset it back to the initial state.
|
@@ -912,7 +912,7 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_
|
|
912
912
|
|
913
913
|
// Assumption 1: This method is called with the GVL being held, because `ddog_prof_Profile_reset` mutates the profile and must
|
914
914
|
// not be interrupted part-way through by a VM fork.
|
915
|
-
static void serializer_set_start_timestamp_for_next_profile(
|
915
|
+
static void serializer_set_start_timestamp_for_next_profile(stack_recorder_state *state, ddog_Timespec start_time) {
|
916
916
|
// Before making this profile active, we reset it so that it uses the correct start_time for its start
|
917
917
|
profile_slot *next_profile_slot = (state->active_slot == 1) ? &state->profile_slot_two : &state->profile_slot_one;
|
918
918
|
reset_profile_slot(next_profile_slot, &start_time);
|
@@ -972,8 +972,8 @@ static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /*
|
|
972
972
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
973
973
|
// It SHOULD NOT be used for other purposes.
|
974
974
|
static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
975
|
-
|
976
|
-
TypedData_Get_Struct(recorder_instance,
|
975
|
+
stack_recorder_state *state;
|
976
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
977
977
|
|
978
978
|
heap_recorder_prepare_iteration(state->heap_recorder);
|
979
979
|
|
@@ -983,8 +983,8 @@ static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _se
|
|
983
983
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
984
984
|
// It SHOULD NOT be used for other purposes.
|
985
985
|
static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
986
|
-
|
987
|
-
TypedData_Get_Struct(recorder_instance,
|
986
|
+
stack_recorder_state *state;
|
987
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
988
988
|
|
989
989
|
heap_recorder_finish_iteration(state->heap_recorder);
|
990
990
|
|
@@ -994,15 +994,15 @@ static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self
|
|
994
994
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
995
995
|
// It SHOULD NOT be used for other purposes.
|
996
996
|
static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
997
|
-
|
998
|
-
TypedData_Get_Struct(recorder_instance,
|
997
|
+
stack_recorder_state *state;
|
998
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
999
999
|
|
1000
1000
|
return heap_recorder_testonly_debug(state->heap_recorder);
|
1001
1001
|
}
|
1002
1002
|
|
1003
1003
|
static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE recorder_instance) {
|
1004
|
-
|
1005
|
-
TypedData_Get_Struct(recorder_instance,
|
1004
|
+
stack_recorder_state *state;
|
1005
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
1006
1006
|
|
1007
1007
|
uint64_t total_serializations = state->stats_lifetime.serialization_successes + state->stats_lifetime.serialization_failures;
|
1008
1008
|
|
@@ -1040,15 +1040,15 @@ static VALUE build_profile_stats(profile_slot *slot, long serialization_time_ns,
|
|
1040
1040
|
static VALUE _native_is_object_recorded(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE obj_id) {
|
1041
1041
|
ENFORCE_TYPE(obj_id, T_FIXNUM);
|
1042
1042
|
|
1043
|
-
|
1044
|
-
TypedData_Get_Struct(recorder_instance,
|
1043
|
+
stack_recorder_state *state;
|
1044
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
1045
1045
|
|
1046
1046
|
return heap_recorder_testonly_is_object_recorded(state->heap_recorder, obj_id);
|
1047
1047
|
}
|
1048
1048
|
|
1049
1049
|
static VALUE _native_heap_recorder_reset_last_update(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
1050
|
-
|
1051
|
-
TypedData_Get_Struct(recorder_instance,
|
1050
|
+
stack_recorder_state *state;
|
1051
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
1052
1052
|
|
1053
1053
|
heap_recorder_testonly_reset_last_update(state->heap_recorder);
|
1054
1054
|
|
@@ -13,7 +13,7 @@ typedef struct {
|
|
13
13
|
int64_t timeline_wall_time_ns;
|
14
14
|
} sample_values;
|
15
15
|
|
16
|
-
typedef struct
|
16
|
+
typedef struct {
|
17
17
|
ddog_prof_Slice_Label labels;
|
18
18
|
|
19
19
|
// This is used to allow the `Collectors::Stack` to modify the existing label, if any. This MUST be NULL or point
|
@@ -39,7 +39,7 @@ static inline long system_epoch_time_now_ns(raise_on_failure_setting raise_on_fa
|
|
39
39
|
// https://docs.redhat.com/en/documentation/red_hat_enterprise_linux_for_real_time/7/html/reference_guide/sect-posix_clocks#Using_clock_getres_to_compare_clock_resolution
|
40
40
|
// We introduce here a separate type for it, so as to make it harder to misuse/more explicit when these timestamps are used
|
41
41
|
|
42
|
-
typedef struct
|
42
|
+
typedef struct {
|
43
43
|
long timestamp_ns;
|
44
44
|
} coarse_instant;
|
45
45
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <ruby/debug.h>
|
3
|
+
#include <stdbool.h>
|
4
|
+
|
5
|
+
#include "datadog_ruby_common.h"
|
6
|
+
#include "unsafe_api_calls_check.h"
|
7
|
+
#include "extconf.h"
|
8
|
+
|
9
|
+
static bool inside_unsafe_context = false;
|
10
|
+
|
11
|
+
#ifndef NO_POSTPONED_TRIGGER
|
12
|
+
static rb_postponed_job_handle_t check_for_unsafe_api_calls_handle;
|
13
|
+
#endif
|
14
|
+
|
15
|
+
static void check_for_unsafe_api_calls(DDTRACE_UNUSED void *_unused);
|
16
|
+
|
17
|
+
void unsafe_api_calls_check_init(void) {
|
18
|
+
#ifndef NO_POSTPONED_TRIGGER
|
19
|
+
int unused_flags = 0;
|
20
|
+
|
21
|
+
check_for_unsafe_api_calls_handle = rb_postponed_job_preregister(unused_flags, check_for_unsafe_api_calls, NULL);
|
22
|
+
|
23
|
+
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)");
|
25
|
+
}
|
26
|
+
#endif
|
27
|
+
}
|
28
|
+
|
29
|
+
void debug_enter_unsafe_context(void) {
|
30
|
+
inside_unsafe_context = true;
|
31
|
+
|
32
|
+
#ifndef NO_POSTPONED_TRIGGER
|
33
|
+
rb_postponed_job_trigger(check_for_unsafe_api_calls_handle);
|
34
|
+
#else
|
35
|
+
rb_postponed_job_register(0, check_for_unsafe_api_calls, NULL);
|
36
|
+
#endif
|
37
|
+
}
|
38
|
+
|
39
|
+
void debug_leave_unsafe_context(void) {
|
40
|
+
inside_unsafe_context = false;
|
41
|
+
}
|
42
|
+
|
43
|
+
static void check_for_unsafe_api_calls(DDTRACE_UNUSED void *_unused) {
|
44
|
+
if (inside_unsafe_context) rb_bug(
|
45
|
+
"Datadog Ruby profiler detected callback nested inside sample. Please report this at https://github.com/datadog/dd-trace-rb/blob/master/CONTRIBUTING.md#found-a-bug"
|
46
|
+
);
|
47
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
// This checker is used to detect accidental thread scheduling switching points happening during profiling sampling.
|
4
|
+
//
|
5
|
+
// Specifically, when the profiler is sampling, we're never supposed to call into Ruby code (e.g. methods
|
6
|
+
// implemented using Ruby code) or allocate Ruby objects.
|
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
|
+
// This includes raising exceptions, unless we're trying to stop the profiler, and even then we must be careful.
|
10
|
+
//
|
11
|
+
// The above is especially true in situations such as GC profiling or allocation/heap profiling, as in those situations
|
12
|
+
// we can even crash the Ruby VM if we switch away at the wrong time.
|
13
|
+
//
|
14
|
+
// The below APIs can be used to detect these situations. They work by relying on the following observation:
|
15
|
+
// in most (all?) thread switch points, Ruby will check for interrupts and run the postponed jobs.
|
16
|
+
//
|
17
|
+
// Thus, if we set a flag while we're sampling (inside_unsafe_context), trigger the postponed job, and then only unset
|
18
|
+
// the flag after sampling, he correct thing to happen is that the postponed job should never see the flag.
|
19
|
+
//
|
20
|
+
// If, however, we have a bug and there's a thread switch point, our postponed job will see the flag and immediately
|
21
|
+
// stop the Ruby VM before further damage happens (and hopefully giving us a stack trace clearly pointing to the culprit).
|
22
|
+
|
23
|
+
void unsafe_api_calls_check_init(void);
|
24
|
+
|
25
|
+
// IMPORTANT: This method **MUST** only be called from test code, as it causes an immediate hard-crash on the Ruby VM
|
26
|
+
// when it detects a potential issue, and that's not something we want for production apps.
|
27
|
+
//
|
28
|
+
// In the future we may introduce some kind of setting (off by default) to also allow this to be safely be used
|
29
|
+
// in production code if needed.
|
30
|
+
void debug_enter_unsafe_context(void);
|
31
|
+
void debug_leave_unsafe_context(void);
|
@@ -54,6 +54,9 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
54
54
|
// Tags and endpoint are heap-allocated, so after here we can't raise exceptions otherwise we'll leak this memory
|
55
55
|
// Start of exception-free zone to prevent leaks {{
|
56
56
|
ddog_Endpoint *endpoint = ddog_endpoint_from_url(char_slice_from_ruby_string(agent_base_url));
|
57
|
+
if (endpoint == NULL) {
|
58
|
+
rb_raise(rb_eRuntimeError, "Failed to create endpoint from agent_base_url: %"PRIsVALUE, agent_base_url);
|
59
|
+
}
|
57
60
|
ddog_Vec_Tag tags = convert_tags(tags_as_array);
|
58
61
|
|
59
62
|
ddog_crasht_Config config = {
|