datadog 2.8.0 → 2.10.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 +62 -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 +66 -56
- 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_stack.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +221 -127
- data/ext/datadog_profiling_native_extension/heap_recorder.c +50 -92
- data/ext/datadog_profiling_native_extension/heap_recorder.h +2 -2
- 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 +63 -76
- data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -2
- 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/actions_handler.rb +27 -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/component.rb +14 -8
- data/lib/datadog/appsec/configuration/settings.rb +9 -0
- data/lib/datadog/appsec/context.rb +74 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +12 -8
- 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/appsec_trace.rb +1 -7
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +20 -30
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +6 -6
- data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +67 -96
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +11 -11
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +6 -6
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +7 -7
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -60
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +23 -33
- data/lib/datadog/appsec/contrib/rails/patcher.rb +4 -14
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +7 -7
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +45 -65
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +5 -28
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +6 -6
- data/lib/datadog/appsec/event.rb +6 -6
- data/lib/datadog/appsec/ext.rb +8 -1
- data/lib/datadog/appsec/metrics/collector.rb +38 -0
- data/lib/datadog/appsec/metrics/exporter.rb +35 -0
- data/lib/datadog/appsec/metrics/telemetry.rb +23 -0
- data/lib/datadog/appsec/metrics.rb +13 -0
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +23 -32
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +6 -6
- data/lib/datadog/appsec/processor/rule_loader.rb +0 -3
- data/lib/datadog/appsec/processor.rb +4 -3
- data/lib/datadog/appsec/response.rb +18 -80
- data/lib/datadog/appsec/security_engine/result.rb +67 -0
- data/lib/datadog/appsec/security_engine/runner.rb +88 -0
- data/lib/datadog/appsec/security_engine.rb +9 -0
- data/lib/datadog/appsec.rb +17 -8
- 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 +19 -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 +22 -2
- data/lib/datadog/di/probe_notifier_worker.rb +5 -6
- data/lib/datadog/di/redactor.rb +0 -1
- data/lib/datadog/di/remote.rb +30 -9
- 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/extensions.rb +15 -3
- data/lib/datadog/tracing/contrib/http/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 +40 -17
- data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -14
- data/lib/datadog/appsec/processor/context.rb +0 -107
- 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);
|
@@ -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.
|
@@ -332,29 +332,22 @@ static VALUE _native_new(VALUE klass) {
|
|
332
332
|
.serialization_time_ns_min = INT64_MAX,
|
333
333
|
};
|
334
334
|
|
335
|
-
// Note: At this point, slot_one_profile
|
335
|
+
// Note: At this point, slot_one_profile/slot_two_profile contain null pointers. Libdatadog validates pointers
|
336
336
|
// before using them so it's ok for us to go ahead and create the StackRecorder object.
|
337
337
|
|
338
|
-
// Note: As of this writing, no new Ruby objects get created and stored in the state. If that ever changes, remember
|
339
|
-
// to keep them on the stack and mark them with RB_GC_GUARD -- otherwise it's possible for a GC to run and
|
340
|
-
// since the instance representing the state does not yet exist, such objects will not get marked.
|
341
|
-
|
342
338
|
VALUE stack_recorder = TypedData_Wrap_Struct(klass, &stack_recorder_typed_data, state);
|
343
339
|
|
344
|
-
// NOTE: We initialize this because we want a new recorder to be operational even
|
340
|
+
// NOTE: We initialize this because we want a new recorder to be operational even before #initialize runs and our
|
345
341
|
// default is everything enabled. However, if during recording initialization it turns out we don't want
|
346
|
-
// heap samples, we will free and reset heap_recorder to NULL
|
347
|
-
// to heap profiling (all calls to heap_recorder_* with a NULL heap recorder are noops).
|
342
|
+
// heap samples, we will free and reset heap_recorder back to NULL.
|
348
343
|
state->heap_recorder = heap_recorder_new();
|
349
344
|
|
350
|
-
// Note: Don't raise exceptions after this point, since it'll lead to libdatadog memory leaking!
|
351
|
-
|
352
345
|
initialize_profiles(state, sample_types);
|
353
346
|
|
354
347
|
return stack_recorder;
|
355
348
|
}
|
356
349
|
|
357
|
-
static void initialize_slot_concurrency_control(
|
350
|
+
static void initialize_slot_concurrency_control(stack_recorder_state *state) {
|
358
351
|
state->mutex_slot_one = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
359
352
|
state->mutex_slot_two = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
360
353
|
|
@@ -364,7 +357,7 @@ static void initialize_slot_concurrency_control(struct stack_recorder_state *sta
|
|
364
357
|
state->active_slot = 1;
|
365
358
|
}
|
366
359
|
|
367
|
-
static void initialize_profiles(
|
360
|
+
static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types) {
|
368
361
|
ddog_prof_Profile_NewResult slot_one_profile_result =
|
369
362
|
ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
|
370
363
|
|
@@ -372,26 +365,21 @@ static void initialize_profiles(struct stack_recorder_state *state, ddog_prof_Sl
|
|
372
365
|
rb_raise(rb_eRuntimeError, "Failed to initialize slot one profile: %"PRIsVALUE, get_error_details_and_drop(&slot_one_profile_result.err));
|
373
366
|
}
|
374
367
|
|
368
|
+
state->profile_slot_one = (profile_slot) { .profile = slot_one_profile_result.ok };
|
369
|
+
|
375
370
|
ddog_prof_Profile_NewResult slot_two_profile_result =
|
376
371
|
ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
|
377
372
|
|
378
373
|
if (slot_two_profile_result.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
|
379
|
-
//
|
380
|
-
ddog_prof_Profile_drop(&slot_one_profile_result.ok);
|
381
|
-
// And now we can raise...
|
374
|
+
// Note: No need to take any special care of slot one, it'll get cleaned up by stack_recorder_typed_data_free
|
382
375
|
rb_raise(rb_eRuntimeError, "Failed to initialize slot two profile: %"PRIsVALUE, get_error_details_and_drop(&slot_two_profile_result.err));
|
383
376
|
}
|
384
377
|
|
385
|
-
state->
|
386
|
-
.profile = slot_one_profile_result.ok,
|
387
|
-
};
|
388
|
-
state->profile_slot_two = (profile_slot) {
|
389
|
-
.profile = slot_two_profile_result.ok,
|
390
|
-
};
|
378
|
+
state->profile_slot_two = (profile_slot) { .profile = slot_two_profile_result.ok };
|
391
379
|
}
|
392
380
|
|
393
381
|
static void stack_recorder_typed_data_free(void *state_ptr) {
|
394
|
-
|
382
|
+
stack_recorder_state *state = (stack_recorder_state *) state_ptr;
|
395
383
|
|
396
384
|
pthread_mutex_destroy(&state->mutex_slot_one);
|
397
385
|
ddog_prof_Profile_drop(&state->profile_slot_one.profile);
|
@@ -426,8 +414,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
426
414
|
ENFORCE_BOOLEAN(timeline_enabled);
|
427
415
|
ENFORCE_BOOLEAN(heap_clean_after_gc_enabled);
|
428
416
|
|
429
|
-
|
430
|
-
TypedData_Get_Struct(recorder_instance,
|
417
|
+
stack_recorder_state *state;
|
418
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
431
419
|
|
432
420
|
state->heap_clean_after_gc_enabled = (heap_clean_after_gc_enabled == Qtrue);
|
433
421
|
|
@@ -517,8 +505,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
517
505
|
}
|
518
506
|
|
519
507
|
static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
520
|
-
|
521
|
-
TypedData_Get_Struct(recorder_instance,
|
508
|
+
stack_recorder_state *state;
|
509
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
522
510
|
|
523
511
|
ddog_Timespec finish_timestamp = system_epoch_now_timespec();
|
524
512
|
// Need to do this while still holding on to the Global VM Lock; see comments on method for why
|
@@ -532,7 +520,7 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
|
|
532
520
|
|
533
521
|
// We'll release the Global VM Lock while we're calling serialize, so that the Ruby VM can continue to work while this
|
534
522
|
// is pending
|
535
|
-
|
523
|
+
call_serialize_without_gvl_arguments args = {
|
536
524
|
.state = state,
|
537
525
|
.finish_timestamp = finish_timestamp,
|
538
526
|
.serialize_ran = false
|
@@ -597,8 +585,8 @@ static VALUE ruby_time_from(ddog_Timespec ddprof_time) {
|
|
597
585
|
}
|
598
586
|
|
599
587
|
void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, sample_values values, sample_labels labels) {
|
600
|
-
|
601
|
-
TypedData_Get_Struct(recorder_instance,
|
588
|
+
stack_recorder_state *state;
|
589
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
602
590
|
|
603
591
|
locked_profile_slot active_slot = sampler_lock_active_profile(state);
|
604
592
|
|
@@ -651,9 +639,9 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
|
|
651
639
|
}
|
652
640
|
}
|
653
641
|
|
654
|
-
void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight, ddog_CharSlice
|
655
|
-
|
656
|
-
TypedData_Get_Struct(recorder_instance,
|
642
|
+
void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight, ddog_CharSlice alloc_class) {
|
643
|
+
stack_recorder_state *state;
|
644
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
657
645
|
// FIXME: Heap sampling currently has to be done in 2 parts because the construction of locations is happening
|
658
646
|
// very late in the allocation-sampling path (which is shared with the cpu sampling path). This can
|
659
647
|
// be fixed with some refactoring but for now this leads to a less impactful change.
|
@@ -661,8 +649,8 @@ void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample
|
|
661
649
|
}
|
662
650
|
|
663
651
|
void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_CharSlice endpoint) {
|
664
|
-
|
665
|
-
TypedData_Get_Struct(recorder_instance,
|
652
|
+
stack_recorder_state *state;
|
653
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
666
654
|
|
667
655
|
locked_profile_slot active_slot = sampler_lock_active_profile(state);
|
668
656
|
|
@@ -676,8 +664,8 @@ void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_
|
|
676
664
|
}
|
677
665
|
|
678
666
|
void recorder_after_gc_step(VALUE recorder_instance) {
|
679
|
-
|
680
|
-
TypedData_Get_Struct(recorder_instance,
|
667
|
+
stack_recorder_state *state;
|
668
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
681
669
|
|
682
670
|
if (state->heap_clean_after_gc_enabled) heap_recorder_update_young_objects(state->heap_recorder);
|
683
671
|
}
|
@@ -687,7 +675,7 @@ void recorder_after_gc_step(VALUE recorder_instance) {
|
|
687
675
|
// Heap recorder iteration context allows us access to stack recorder state and profile being serialized
|
688
676
|
// during iteration of heap recorder live objects.
|
689
677
|
typedef struct heap_recorder_iteration_context {
|
690
|
-
|
678
|
+
stack_recorder_state *state;
|
691
679
|
profile_slot *slot;
|
692
680
|
|
693
681
|
bool error;
|
@@ -749,7 +737,7 @@ static bool add_heap_sample_to_active_profile_without_gvl(heap_recorder_iteratio
|
|
749
737
|
return true;
|
750
738
|
}
|
751
739
|
|
752
|
-
static void build_heap_profile_without_gvl(
|
740
|
+
static void build_heap_profile_without_gvl(stack_recorder_state *state, profile_slot *slot) {
|
753
741
|
heap_recorder_iteration_context iteration_context = {
|
754
742
|
.state = state,
|
755
743
|
.slot = slot,
|
@@ -770,7 +758,7 @@ static void build_heap_profile_without_gvl(struct stack_recorder_state *state, p
|
|
770
758
|
}
|
771
759
|
|
772
760
|
static void *call_serialize_without_gvl(void *call_args) {
|
773
|
-
|
761
|
+
call_serialize_without_gvl_arguments *args = (call_serialize_without_gvl_arguments *) call_args;
|
774
762
|
|
775
763
|
long serialize_no_gvl_start_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
776
764
|
|
@@ -796,7 +784,7 @@ VALUE enforce_recorder_instance(VALUE object) {
|
|
796
784
|
return object;
|
797
785
|
}
|
798
786
|
|
799
|
-
static locked_profile_slot sampler_lock_active_profile(
|
787
|
+
static locked_profile_slot sampler_lock_active_profile(stack_recorder_state *state) {
|
800
788
|
int error;
|
801
789
|
|
802
790
|
for (int attempts = 0; attempts < 2; attempts++) {
|
@@ -823,7 +811,7 @@ static void sampler_unlock_active_profile(locked_profile_slot active_slot) {
|
|
823
811
|
ENFORCE_SUCCESS_GVL(pthread_mutex_unlock(active_slot.mutex));
|
824
812
|
}
|
825
813
|
|
826
|
-
static profile_slot* serializer_flip_active_and_inactive_slots(
|
814
|
+
static profile_slot* serializer_flip_active_and_inactive_slots(stack_recorder_state *state) {
|
827
815
|
int previously_active_slot = state->active_slot;
|
828
816
|
|
829
817
|
if (previously_active_slot != 1 && previously_active_slot != 2) {
|
@@ -849,8 +837,8 @@ static profile_slot* serializer_flip_active_and_inactive_slots(struct stack_reco
|
|
849
837
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
850
838
|
// It SHOULD NOT be used for other purposes.
|
851
839
|
static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
852
|
-
|
853
|
-
TypedData_Get_Struct(recorder_instance,
|
840
|
+
stack_recorder_state *state;
|
841
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
854
842
|
|
855
843
|
return INT2NUM(state->active_slot);
|
856
844
|
}
|
@@ -864,8 +852,8 @@ static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE
|
|
864
852
|
static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) { return test_slot_mutex_state(recorder_instance, 2); }
|
865
853
|
|
866
854
|
static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot) {
|
867
|
-
|
868
|
-
TypedData_Get_Struct(recorder_instance,
|
855
|
+
stack_recorder_state *state;
|
856
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
869
857
|
|
870
858
|
pthread_mutex_t *slot_mutex = (slot == 1) ? &state->mutex_slot_one : &state->mutex_slot_two;
|
871
859
|
|
@@ -895,8 +883,8 @@ static ddog_Timespec system_epoch_now_timespec(void) {
|
|
895
883
|
// Assumption: This method gets called BEFORE restarting profiling -- e.g. there are no components attempting to
|
896
884
|
// trigger samples at the same time.
|
897
885
|
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance) {
|
898
|
-
|
899
|
-
TypedData_Get_Struct(recorder_instance,
|
886
|
+
stack_recorder_state *state;
|
887
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
900
888
|
|
901
889
|
// In case the fork happened halfway through `serializer_flip_active_and_inactive_slots` execution and the
|
902
890
|
// resulting state is inconsistent, we make sure to reset it back to the initial state.
|
@@ -912,7 +900,7 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_
|
|
912
900
|
|
913
901
|
// Assumption 1: This method is called with the GVL being held, because `ddog_prof_Profile_reset` mutates the profile and must
|
914
902
|
// not be interrupted part-way through by a VM fork.
|
915
|
-
static void serializer_set_start_timestamp_for_next_profile(
|
903
|
+
static void serializer_set_start_timestamp_for_next_profile(stack_recorder_state *state, ddog_Timespec start_time) {
|
916
904
|
// Before making this profile active, we reset it so that it uses the correct start_time for its start
|
917
905
|
profile_slot *next_profile_slot = (state->active_slot == 1) ? &state->profile_slot_two : &state->profile_slot_one;
|
918
906
|
reset_profile_slot(next_profile_slot, &start_time);
|
@@ -926,8 +914,7 @@ static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_
|
|
926
914
|
|
927
915
|
static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE new_obj, VALUE weight, VALUE alloc_class) {
|
928
916
|
ENFORCE_TYPE(weight, T_FIXNUM);
|
929
|
-
|
930
|
-
track_object(recorder_instance, new_obj, NUM2UINT(weight), &alloc_class_slice);
|
917
|
+
track_object(recorder_instance, new_obj, NUM2UINT(weight), char_slice_from_ruby_string(alloc_class));
|
931
918
|
return Qtrue;
|
932
919
|
}
|
933
920
|
|
@@ -972,8 +959,8 @@ static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /*
|
|
972
959
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
973
960
|
// It SHOULD NOT be used for other purposes.
|
974
961
|
static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
975
|
-
|
976
|
-
TypedData_Get_Struct(recorder_instance,
|
962
|
+
stack_recorder_state *state;
|
963
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
977
964
|
|
978
965
|
heap_recorder_prepare_iteration(state->heap_recorder);
|
979
966
|
|
@@ -983,8 +970,8 @@ static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _se
|
|
983
970
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
984
971
|
// It SHOULD NOT be used for other purposes.
|
985
972
|
static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
986
|
-
|
987
|
-
TypedData_Get_Struct(recorder_instance,
|
973
|
+
stack_recorder_state *state;
|
974
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
988
975
|
|
989
976
|
heap_recorder_finish_iteration(state->heap_recorder);
|
990
977
|
|
@@ -994,15 +981,15 @@ static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self
|
|
994
981
|
// This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
|
995
982
|
// It SHOULD NOT be used for other purposes.
|
996
983
|
static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
997
|
-
|
998
|
-
TypedData_Get_Struct(recorder_instance,
|
984
|
+
stack_recorder_state *state;
|
985
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
999
986
|
|
1000
987
|
return heap_recorder_testonly_debug(state->heap_recorder);
|
1001
988
|
}
|
1002
989
|
|
1003
990
|
static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE recorder_instance) {
|
1004
|
-
|
1005
|
-
TypedData_Get_Struct(recorder_instance,
|
991
|
+
stack_recorder_state *state;
|
992
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
1006
993
|
|
1007
994
|
uint64_t total_serializations = state->stats_lifetime.serialization_successes + state->stats_lifetime.serialization_failures;
|
1008
995
|
|
@@ -1040,15 +1027,15 @@ static VALUE build_profile_stats(profile_slot *slot, long serialization_time_ns,
|
|
1040
1027
|
static VALUE _native_is_object_recorded(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE obj_id) {
|
1041
1028
|
ENFORCE_TYPE(obj_id, T_FIXNUM);
|
1042
1029
|
|
1043
|
-
|
1044
|
-
TypedData_Get_Struct(recorder_instance,
|
1030
|
+
stack_recorder_state *state;
|
1031
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
1045
1032
|
|
1046
1033
|
return heap_recorder_testonly_is_object_recorded(state->heap_recorder, obj_id);
|
1047
1034
|
}
|
1048
1035
|
|
1049
1036
|
static VALUE _native_heap_recorder_reset_last_update(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
|
1050
|
-
|
1051
|
-
TypedData_Get_Struct(recorder_instance,
|
1037
|
+
stack_recorder_state *state;
|
1038
|
+
TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
|
1052
1039
|
|
1053
1040
|
heap_recorder_testonly_reset_last_update(state->heap_recorder);
|
1054
1041
|
|
@@ -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
|
@@ -26,6 +26,6 @@ typedef struct sample_labels {
|
|
26
26
|
|
27
27
|
void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, sample_values values, sample_labels labels);
|
28
28
|
void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_CharSlice endpoint);
|
29
|
-
void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight, ddog_CharSlice
|
29
|
+
void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight, ddog_CharSlice alloc_class);
|
30
30
|
void recorder_after_gc_step(VALUE recorder_instance);
|
31
31
|
VALUE enforce_recorder_instance(VALUE object);
|
@@ -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 = {
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module AppSec
|
5
|
+
# this module encapsulates functions for handling actions that libddawf returns
|
6
|
+
module ActionsHandler
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def handle(actions_hash)
|
10
|
+
# handle actions according their precedence
|
11
|
+
# stack and schema generation should be done before we throw an interrupt signal
|
12
|
+
generate_stack(actions_hash['generate_stack']) if actions_hash.key?('generate_stack')
|
13
|
+
generate_schema(actions_hash['generate_schema']) if actions_hash.key?('generate_schema')
|
14
|
+
interrupt_execution(actions_hash['redirect_request']) if actions_hash.key?('redirect_request')
|
15
|
+
interrupt_execution(actions_hash['block_request']) if actions_hash.key?('block_request')
|
16
|
+
end
|
17
|
+
|
18
|
+
def interrupt_execution(action_params)
|
19
|
+
throw(Datadog::AppSec::Ext::INTERRUPT, action_params)
|
20
|
+
end
|
21
|
+
|
22
|
+
def generate_stack(_action_params); end
|
23
|
+
|
24
|
+
def generate_schema(_action_params); end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|