datadog 2.7.1 → 2.9.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 +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.
|