datadog 2.7.1 → 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 +69 -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 +259 -132
- data/ext/datadog_profiling_native_extension/extconf.rb +0 -8
- data/ext/datadog_profiling_native_extension/heap_recorder.c +11 -89
- 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 +4 -1
- 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 -88
- 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/ext/libdatadog_extconf_helpers.rb +1 -1
- 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/component.rb +1 -8
- data/lib/datadog/appsec/context.rb +54 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +73 -0
- data/lib/datadog/appsec/contrib/active_record/integration.rb +41 -0
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +53 -0
- 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/context.rb +2 -2
- data/lib/datadog/appsec/processor/rule_loader.rb +0 -3
- data/lib/datadog/appsec/remote.rb +1 -3
- data/lib/datadog/appsec/response.rb +7 -11
- data/lib/datadog/appsec.rb +6 -5
- 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 +20 -2
- data/lib/datadog/core/configuration/settings.rb +10 -0
- data/lib/datadog/core/configuration.rb +10 -2
- data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
- data/lib/datadog/core/crashtracking/component.rb +1 -3
- data/lib/datadog/core/remote/client/capabilities.rb +6 -0
- data/lib/datadog/core/remote/client.rb +65 -59
- data/lib/datadog/core/telemetry/component.rb +9 -3
- data/lib/datadog/core/telemetry/event.rb +87 -3
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- 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 +11 -7
- data/lib/datadog/di/component.rb +21 -11
- data/lib/datadog/di/configuration/settings.rb +11 -1
- data/lib/datadog/di/contrib/active_record.rb +1 -0
- 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 +111 -20
- data/lib/datadog/di/preload.rb +18 -0
- data/lib/datadog/di/probe.rb +11 -1
- data/lib/datadog/di/probe_builder.rb +1 -0
- data/lib/datadog/di/probe_manager.rb +8 -5
- data/lib/datadog/di/probe_notification_builder.rb +27 -7
- data/lib/datadog/di/probe_notifier_worker.rb +5 -6
- data/lib/datadog/di/remote.rb +124 -0
- data/lib/datadog/di/serializer.rb +14 -7
- data/lib/datadog/di/transport.rb +3 -5
- data/lib/datadog/di/utils.rb +7 -0
- data/lib/datadog/di.rb +23 -62
- data/lib/datadog/kit/appsec/events.rb +3 -3
- data/lib/datadog/kit/identity.rb +4 -4
- data/lib/datadog/profiling/component.rb +59 -69
- data/lib/datadog/profiling/http_transport.rb +1 -26
- data/lib/datadog/tracing/configuration/settings.rb +4 -8
- 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/cache/redis.rb +16 -4
- 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/elasticsearch/configuration/settings.rb +4 -0
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
- 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 +2 -2
- data/lib/datadog.rb +3 -0
- metadata +30 -17
- data/lib/datadog/appsec/processor/actions.rb +0 -49
- 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
@@ -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);
|
@@ -258,8 +258,6 @@ static VALUE _native_check_heap_hashes(DDTRACE_UNUSED VALUE _self, VALUE locatio
|
|
258
258
|
static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
259
259
|
static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
260
260
|
static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
261
|
-
static VALUE _native_gc_force_recycle(DDTRACE_UNUSED VALUE _self, VALUE obj);
|
262
|
-
static VALUE _native_has_seen_id_flag(DDTRACE_UNUSED VALUE _self, VALUE obj);
|
263
261
|
static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance);
|
264
262
|
static VALUE build_profile_stats(profile_slot *slot, long serialization_time_ns, long heap_iteration_prep_time_ns, long heap_profile_build_time_ns);
|
265
263
|
static VALUE _native_is_object_recorded(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE object_id);
|
@@ -297,10 +295,6 @@ void stack_recorder_init(VALUE profiling_module) {
|
|
297
295
|
_native_end_fake_slow_heap_serialization, 1);
|
298
296
|
rb_define_singleton_method(testing_module, "_native_debug_heap_recorder",
|
299
297
|
_native_debug_heap_recorder, 1);
|
300
|
-
rb_define_singleton_method(testing_module, "_native_gc_force_recycle",
|
301
|
-
_native_gc_force_recycle, 1);
|
302
|
-
rb_define_singleton_method(testing_module, "_native_has_seen_id_flag",
|
303
|
-
_native_has_seen_id_flag, 1);
|
304
298
|
rb_define_singleton_method(testing_module, "_native_is_object_recorded?", _native_is_object_recorded, 2);
|
305
299
|
rb_define_singleton_method(testing_module, "_native_heap_recorder_reset_last_update", _native_heap_recorder_reset_last_update, 1);
|
306
300
|
rb_define_singleton_method(testing_module, "_native_recorder_after_gc_step", _native_recorder_after_gc_step, 1);
|
@@ -322,7 +316,7 @@ static const rb_data_type_t stack_recorder_typed_data = {
|
|
322
316
|
};
|
323
317
|
|
324
318
|
static VALUE _native_new(VALUE klass) {
|
325
|
-
|
319
|
+
stack_recorder_state *state = ruby_xcalloc(1, sizeof(stack_recorder_state));
|
326
320
|
|
327
321
|
// Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
|
328
322
|
// being leaked.
|
@@ -360,7 +354,7 @@ static VALUE _native_new(VALUE klass) {
|
|
360
354
|
return stack_recorder;
|
361
355
|
}
|
362
356
|
|
363
|
-
static void initialize_slot_concurrency_control(
|
357
|
+
static void initialize_slot_concurrency_control(stack_recorder_state *state) {
|
364
358
|
state->mutex_slot_one = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
365
359
|
state->mutex_slot_two = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
366
360
|
|
@@ -370,7 +364,7 @@ static void initialize_slot_concurrency_control(struct stack_recorder_state *sta
|
|
370
364
|
state->active_slot = 1;
|
371
365
|
}
|
372
366
|
|
373
|
-
static void initialize_profiles(
|
367
|
+
static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types) {
|
374
368
|
ddog_prof_Profile_NewResult slot_one_profile_result =
|
375
369
|
ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
|
376
370
|
|
@@ -397,7 +391,7 @@ static void initialize_profiles(struct stack_recorder_state *state, ddog_prof_Sl
|
|
397
391
|
}
|
398
392
|
|
399
393
|
static void stack_recorder_typed_data_free(void *state_ptr) {
|
400
|
-
|
394
|
+
stack_recorder_state *state = (stack_recorder_state *) state_ptr;
|
401
395
|
|
402
396
|
pthread_mutex_destroy(&state->mutex_slot_one);
|
403
397
|
ddog_prof_Profile_drop(&state->profile_slot_one.profile);
|
@@ -432,8 +426,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
432
426
|
ENFORCE_BOOLEAN(timeline_enabled);
|
433
427
|
ENFORCE_BOOLEAN(heap_clean_after_gc_enabled);
|
434
428
|
|
435
|
-
|
436
|
-
TypedData_Get_Struct(recorder_instance,
|
429
|
+
stack_recorder_state *state;
|
430
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
437
431
|
|
438
432
|
state->heap_clean_after_gc_enabled = (heap_clean_after_gc_enabled == Qtrue);
|
439
433
|
|
@@ -523,8 +517,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
523
517
|
}
|
524
518
|
|
525
519
|
static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
526
|
-
|
527
|
-
TypedData_Get_Struct(recorder_instance,
|
520
|
+
stack_recorder_state *state;
|
521
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
528
522
|
|
529
523
|
ddog_Timespec finish_timestamp = system_epoch_now_timespec();
|
530
524
|
// Need to do this while still holding on to the Global VM Lock; see comments on method for why
|
@@ -538,7 +532,7 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
|
|
538
532
|
|
539
533
|
// We'll release the Global VM Lock while we're calling serialize, so that the Ruby VM can continue to work while this
|
540
534
|
// is pending
|
541
|
-
|
535
|
+
call_serialize_without_gvl_arguments args = {
|
542
536
|
.state = state,
|
543
537
|
.finish_timestamp = finish_timestamp,
|
544
538
|
.serialize_ran = false
|
@@ -603,8 +597,8 @@ static VALUE ruby_time_from(ddog_Timespec ddprof_time) {
|
|
603
597
|
}
|
604
598
|
|
605
599
|
void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, sample_values values, sample_labels labels) {
|
606
|
-
|
607
|
-
TypedData_Get_Struct(recorder_instance,
|
600
|
+
stack_recorder_state *state;
|
601
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
608
602
|
|
609
603
|
locked_profile_slot active_slot = sampler_lock_active_profile(state);
|
610
604
|
|
@@ -658,8 +652,8 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
|
|
658
652
|
}
|
659
653
|
|
660
654
|
void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight, ddog_CharSlice *alloc_class) {
|
661
|
-
|
662
|
-
TypedData_Get_Struct(recorder_instance,
|
655
|
+
stack_recorder_state *state;
|
656
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
663
657
|
// FIXME: Heap sampling currently has to be done in 2 parts because the construction of locations is happening
|
664
658
|
// very late in the allocation-sampling path (which is shared with the cpu sampling path). This can
|
665
659
|
// be fixed with some refactoring but for now this leads to a less impactful change.
|
@@ -667,8 +661,8 @@ void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample
|
|
667
661
|
}
|
668
662
|
|
669
663
|
void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_CharSlice endpoint) {
|
670
|
-
|
671
|
-
TypedData_Get_Struct(recorder_instance,
|
664
|
+
stack_recorder_state *state;
|
665
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
672
666
|
|
673
667
|
locked_profile_slot active_slot = sampler_lock_active_profile(state);
|
674
668
|
|
@@ -682,8 +676,8 @@ void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_
|
|
682
676
|
}
|
683
677
|
|
684
678
|
void recorder_after_gc_step(VALUE recorder_instance) {
|
685
|
-
|
686
|
-
TypedData_Get_Struct(recorder_instance,
|
679
|
+
stack_recorder_state *state;
|
680
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
687
681
|
|
688
682
|
if (state->heap_clean_after_gc_enabled) heap_recorder_update_young_objects(state->heap_recorder);
|
689
683
|
}
|
@@ -693,7 +687,7 @@ void recorder_after_gc_step(VALUE recorder_instance) {
|
|
693
687
|
// Heap recorder iteration context allows us access to stack recorder state and profile being serialized
|
694
688
|
// during iteration of heap recorder live objects.
|
695
689
|
typedef struct heap_recorder_iteration_context {
|
696
|
-
|
690
|
+
stack_recorder_state *state;
|
697
691
|
profile_slot *slot;
|
698
692
|
|
699
693
|
bool error;
|
@@ -755,7 +749,7 @@ static bool add_heap_sample_to_active_profile_without_gvl(heap_recorder_iteratio
|
|
755
749
|
return true;
|
756
750
|
}
|
757
751
|
|
758
|
-
static void build_heap_profile_without_gvl(
|
752
|
+
static void build_heap_profile_without_gvl(stack_recorder_state *state, profile_slot *slot) {
|
759
753
|
heap_recorder_iteration_context iteration_context = {
|
760
754
|
.state = state,
|
761
755
|
.slot = slot,
|
@@ -776,7 +770,7 @@ static void build_heap_profile_without_gvl(struct stack_recorder_state *state, p
|
|
776
770
|
}
|
777
771
|
|
778
772
|
static void *call_serialize_without_gvl(void *call_args) {
|
779
|
-
|
773
|
+
call_serialize_without_gvl_arguments *args = (call_serialize_without_gvl_arguments *) call_args;
|
780
774
|
|
781
775
|
long serialize_no_gvl_start_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
782
776
|
|
@@ -802,7 +796,7 @@ VALUE enforce_recorder_instance(VALUE object) {
|
|
802
796
|
return object;
|
803
797
|
}
|
804
798
|
|
805
|
-
static locked_profile_slot sampler_lock_active_profile(
|
799
|
+
static locked_profile_slot sampler_lock_active_profile(stack_recorder_state *state) {
|
806
800
|
int error;
|
807
801
|
|
808
802
|
for (int attempts = 0; attempts < 2; attempts++) {
|
@@ -829,7 +823,7 @@ static void sampler_unlock_active_profile(locked_profile_slot active_slot) {
|
|
829
823
|
ENFORCE_SUCCESS_GVL(pthread_mutex_unlock(active_slot.mutex));
|
830
824
|
}
|
831
825
|
|
832
|
-
static profile_slot* serializer_flip_active_and_inactive_slots(
|
826
|
+
static profile_slot* serializer_flip_active_and_inactive_slots(stack_recorder_state *state) {
|
833
827
|
int previously_active_slot = state->active_slot;
|
834
828
|
|
835
829
|
if (previously_active_slot != 1 && previously_active_slot != 2) {
|
@@ -855,8 +849,8 @@ static profile_slot* serializer_flip_active_and_inactive_slots(struct stack_reco
|
|
855
849
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
856
850
|
// It SHOULD NOT be used for other purposes.
|
857
851
|
static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
858
|
-
|
859
|
-
TypedData_Get_Struct(recorder_instance,
|
852
|
+
stack_recorder_state *state;
|
853
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
860
854
|
|
861
855
|
return INT2NUM(state->active_slot);
|
862
856
|
}
|
@@ -870,8 +864,8 @@ static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE
|
|
870
864
|
static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) { return test_slot_mutex_state(recorder_instance, 2); }
|
871
865
|
|
872
866
|
static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot) {
|
873
|
-
|
874
|
-
TypedData_Get_Struct(recorder_instance,
|
867
|
+
stack_recorder_state *state;
|
868
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
875
869
|
|
876
870
|
pthread_mutex_t *slot_mutex = (slot == 1) ? &state->mutex_slot_one : &state->mutex_slot_two;
|
877
871
|
|
@@ -901,8 +895,8 @@ static ddog_Timespec system_epoch_now_timespec(void) {
|
|
901
895
|
// Assumption: This method gets called BEFORE restarting profiling -- e.g. there are no components attempting to
|
902
896
|
// trigger samples at the same time.
|
903
897
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance) {
|
904
|
-
|
905
|
-
TypedData_Get_Struct(recorder_instance,
|
898
|
+
stack_recorder_state *state;
|
899
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
906
900
|
|
907
901
|
// In case the fork happened halfway through `serializer_flip_active_and_inactive_slots` execution and the
|
908
902
|
// resulting state is inconsistent, we make sure to reset it back to the initial state.
|
@@ -918,7 +912,7 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_
|
|
918
912
|
|
919
913
|
// Assumption 1: This method is called with the GVL being held, because `ddog_prof_Profile_reset` mutates the profile and must
|
920
914
|
// not be interrupted part-way through by a VM fork.
|
921
|
-
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) {
|
922
916
|
// Before making this profile active, we reset it so that it uses the correct start_time for its start
|
923
917
|
profile_slot *next_profile_slot = (state->active_slot == 1) ? &state->profile_slot_two : &state->profile_slot_one;
|
924
918
|
reset_profile_slot(next_profile_slot, &start_time);
|
@@ -978,8 +972,8 @@ static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /*
|
|
978
972
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
979
973
|
// It SHOULD NOT be used for other purposes.
|
980
974
|
static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
981
|
-
|
982
|
-
TypedData_Get_Struct(recorder_instance,
|
975
|
+
stack_recorder_state *state;
|
976
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
983
977
|
|
984
978
|
heap_recorder_prepare_iteration(state->heap_recorder);
|
985
979
|
|
@@ -989,8 +983,8 @@ static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _se
|
|
989
983
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
990
984
|
// It SHOULD NOT be used for other purposes.
|
991
985
|
static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
992
|
-
|
993
|
-
TypedData_Get_Struct(recorder_instance,
|
986
|
+
stack_recorder_state *state;
|
987
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
994
988
|
|
995
989
|
heap_recorder_finish_iteration(state->heap_recorder);
|
996
990
|
|
@@ -1000,43 +994,15 @@ static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self
|
|
1000
994
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
1001
995
|
// It SHOULD NOT be used for other purposes.
|
1002
996
|
static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
1003
|
-
|
1004
|
-
TypedData_Get_Struct(recorder_instance,
|
997
|
+
stack_recorder_state *state;
|
998
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
1005
999
|
|
1006
1000
|
return heap_recorder_testonly_debug(state->heap_recorder);
|
1007
1001
|
}
|
1008
1002
|
|
1009
|
-
#pragma GCC diagnostic push
|
1010
|
-
// rb_gc_force_recycle was deprecated in latest versions of Ruby and is a noop.
|
1011
|
-
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
1012
|
-
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
1013
|
-
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
1014
|
-
// It SHOULD NOT be used for other purposes.
|
1015
|
-
static VALUE _native_gc_force_recycle(DDTRACE_UNUSED VALUE _self, VALUE obj) {
|
1016
|
-
#ifdef HAVE_WORKING_RB_GC_FORCE_RECYCLE
|
1017
|
-
rb_gc_force_recycle(obj);
|
1018
|
-
#endif
|
1019
|
-
return Qnil;
|
1020
|
-
}
|
1021
|
-
#pragma GCC diagnostic pop
|
1022
|
-
|
1023
|
-
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
1024
|
-
// It SHOULD NOT be used for other purposes.
|
1025
|
-
static VALUE _native_has_seen_id_flag(DDTRACE_UNUSED VALUE _self, VALUE obj) {
|
1026
|
-
#ifndef NO_SEEN_OBJ_ID_FLAG
|
1027
|
-
if (RB_FL_TEST(obj, RUBY_FL_SEEN_OBJ_ID)) {
|
1028
|
-
return Qtrue;
|
1029
|
-
} else {
|
1030
|
-
return Qfalse;
|
1031
|
-
}
|
1032
|
-
#else
|
1033
|
-
return Qfalse;
|
1034
|
-
#endif
|
1035
|
-
}
|
1036
|
-
|
1037
1003
|
static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE recorder_instance) {
|
1038
|
-
|
1039
|
-
TypedData_Get_Struct(recorder_instance,
|
1004
|
+
stack_recorder_state *state;
|
1005
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
1040
1006
|
|
1041
1007
|
uint64_t total_serializations = state->stats_lifetime.serialization_successes + state->stats_lifetime.serialization_failures;
|
1042
1008
|
|
@@ -1074,15 +1040,15 @@ static VALUE build_profile_stats(profile_slot *slot, long serialization_time_ns,
|
|
1074
1040
|
static VALUE _native_is_object_recorded(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE obj_id) {
|
1075
1041
|
ENFORCE_TYPE(obj_id, T_FIXNUM);
|
1076
1042
|
|
1077
|
-
|
1078
|
-
TypedData_Get_Struct(recorder_instance,
|
1043
|
+
stack_recorder_state *state;
|
1044
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
1079
1045
|
|
1080
1046
|
return heap_recorder_testonly_is_object_recorded(state->heap_recorder, obj_id);
|
1081
1047
|
}
|
1082
1048
|
|
1083
1049
|
static VALUE _native_heap_recorder_reset_last_update(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
1084
|
-
|
1085
|
-
TypedData_Get_Struct(recorder_instance,
|
1050
|
+
stack_recorder_state *state;
|
1051
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
1086
1052
|
|
1087
1053
|
heap_recorder_testonly_reset_last_update(state->heap_recorder);
|
1088
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 = {
|
@@ -8,7 +8,7 @@ module Datadog
|
|
8
8
|
module LibdatadogExtconfHelpers
|
9
9
|
# Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
|
10
10
|
# may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
|
11
|
-
LIBDATADOG_VERSION = '~> 14.1.
|
11
|
+
LIBDATADOG_VERSION = '~> 14.3.1.1.0'
|
12
12
|
|
13
13
|
# Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
|
14
14
|
# libdatadog are moved after the extension gets compiled.
|