datadog 2.37.0 → 2.38.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 +29 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +23 -15
- data/ext/datadog_profiling_native_extension/collectors_stack.c +19 -24
- data/ext/datadog_profiling_native_extension/collectors_stack.h +0 -1
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +155 -141
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +0 -3
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +5 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +2 -0
- data/ext/datadog_profiling_native_extension/time_helpers.h +12 -7
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +5 -0
- data/lib/datadog/appsec/contrib/rails/gateway/request.rb +5 -0
- data/lib/datadog/core/configuration/agentless_settings_resolver.rb +2 -2
- data/lib/datadog/core/configuration/settings.rb +4 -4
- data/lib/datadog/core/configuration/supported_configurations.rb +1 -0
- data/lib/datadog/core/knuth_sampler.rb +3 -1
- data/lib/datadog/core/metrics/client.rb +1 -1
- data/lib/datadog/core/transport/http/builder.rb +1 -1
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +1 -1
- data/lib/datadog/core/utils/time.rb +5 -1
- data/lib/datadog/di/boot.rb +3 -0
- data/lib/datadog/di/capture_expression.rb +21 -0
- data/lib/datadog/di/capture_expression_evaluator.rb +71 -0
- data/lib/datadog/di/capture_limits.rb +41 -0
- data/lib/datadog/di/component.rb +2 -2
- data/lib/datadog/di/configuration.rb +6 -0
- data/lib/datadog/di/context.rb +9 -3
- data/lib/datadog/di/el/compiler.rb +1 -1
- data/lib/datadog/di/instrumenter.rb +34 -3
- data/lib/datadog/di/probe.rb +47 -2
- data/lib/datadog/di/probe_builder.rb +83 -1
- data/lib/datadog/di/probe_notification_builder.rb +48 -7
- data/lib/datadog/di/remote.rb +1 -1
- data/lib/datadog/di/serializer.rb +22 -10
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +5 -0
- data/lib/datadog/profiling/collectors/thread_context.rb +9 -1
- data/lib/datadog/profiling/component.rb +17 -0
- data/lib/datadog/tracing/contrib/active_job/data_streams.rb +62 -0
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +12 -0
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +2 -3
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +12 -12
- data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/sequel/utils.rb +2 -2
- data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +1 -1
- data/lib/datadog/tracing/contrib/sinatra/framework.rb +1 -1
- data/lib/datadog/tracing/transport/trace_formatter.rb +1 -1
- data/lib/datadog/version.rb +1 -1
- metadata +9 -5
|
@@ -43,11 +43,8 @@
|
|
|
43
43
|
// When `thread_context_collector_on_gc_start` gets called, the current cpu and wall-time get recorded to the thread
|
|
44
44
|
// context: `cpu_time_at_gc_start_ns` and `wall_time_at_gc_start_ns`.
|
|
45
45
|
//
|
|
46
|
-
// While `cpu_time_at_gc_start_ns` is set,
|
|
47
|
-
//
|
|
48
|
-
// Wall-time accounting is not affected by this (e.g. we still record 60 seconds every 60 seconds).
|
|
49
|
-
//
|
|
50
|
-
// (Regular samples can still account for the cpu-time between the previous sample and the start of GC.)
|
|
46
|
+
// While `cpu_time_at_gc_start_ns` is set, we don't expect the thread to be sampled: the VM is doing GC
|
|
47
|
+
// on the thread holding the GVL so no other samples can/will be triggered until GC finishes.
|
|
51
48
|
//
|
|
52
49
|
// When `thread_context_collector_on_gc_finish` gets called, the cpu-time and wall-time spent during GC gets recorded
|
|
53
50
|
// into the global gc_tracking structure, and further samples are not affected. (The `cpu_time_at_previous_sample_ns`
|
|
@@ -76,8 +73,6 @@
|
|
|
76
73
|
|
|
77
74
|
#define THREAD_ID_LIMIT_CHARS 44 // Why 44? "#{2**64} (#{2**64})".size + 1 for \0
|
|
78
75
|
#define THREAD_INVOKE_LOCATION_LIMIT_CHARS 512
|
|
79
|
-
#define IS_WALL_TIME true
|
|
80
|
-
#define IS_CPU_TIME false
|
|
81
76
|
#define MISSING_TRACER_CONTEXT_KEY 0
|
|
82
77
|
#define TIME_BETWEEN_GC_EVENTS_NS MILLIS_AS_NS(10)
|
|
83
78
|
#define GVL_SUSPENDED ((uint64_t)1)
|
|
@@ -109,7 +104,13 @@ static ID server_id; // id of :server in Ruby
|
|
|
109
104
|
static ID otel_context_storage_id; // id of :__opentelemetry_context_storage__ in Ruby
|
|
110
105
|
static ID otel_fiber_context_storage_id; // id of :@opentelemetry_context in Ruby
|
|
111
106
|
|
|
112
|
-
// This is mutable and gets set last-writer-wins style
|
|
107
|
+
// This is mutable and gets set last-writer-wins style by
|
|
108
|
+
// `thread_context_collector_reset_all_per_thread_contexts`, which is called whenever
|
|
109
|
+
// profiling is starting or restating.
|
|
110
|
+
//
|
|
111
|
+
// Note: We must be careful to not change this value while the profiler is still running,
|
|
112
|
+
// otherwise new threads can get the new value and cause profiling to stop with an
|
|
113
|
+
// exception due to the mismatched size.
|
|
113
114
|
//
|
|
114
115
|
// The initial value should be kept in sync with the default for DD_PROFILING_MAX_FRAMES
|
|
115
116
|
// in settings.rb. See `initialize_context` for details on why this is needed/used.
|
|
@@ -118,7 +119,6 @@ static uint16_t latest_max_frames = 400;
|
|
|
118
119
|
// Global tracepoint for RUBY_EVENT_THREAD_BEGIN. Created and enabled once when the first ThreadContext collector is initialized.
|
|
119
120
|
static VALUE thread_begin_tracepoint = Qnil;
|
|
120
121
|
|
|
121
|
-
|
|
122
122
|
typedef enum { OTEL_CONTEXT_ENABLED_FALSE, OTEL_CONTEXT_ENABLED_ONLY, OTEL_CONTEXT_ENABLED_BOTH } otel_context_enabled;
|
|
123
123
|
typedef enum { OTEL_CONTEXT_SOURCE_UNKNOWN, OTEL_CONTEXT_SOURCE_FIBER_IVAR, OTEL_CONTEXT_SOURCE_FIBER_LOCAL } otel_context_source;
|
|
124
124
|
|
|
@@ -187,7 +187,8 @@ typedef struct {
|
|
|
187
187
|
// The state is created early on for all threads on the main Ractor
|
|
188
188
|
// (enabling a TracePoint only enables it for the current Ractor).
|
|
189
189
|
// The state is either created when the Thread starts running (via the RUBY_EVENT_THREAD_BEGIN TracePoint),
|
|
190
|
-
// or
|
|
190
|
+
// or via `thread_context_collector_reset_all_per_thread_contexts` when the profiler starts.
|
|
191
|
+
//
|
|
191
192
|
// Unfortunately that RUBY_EVENT_THREAD_BEGIN TracePoint still fires after some other events:
|
|
192
193
|
// * RUBY_INTERNAL_THREAD_EVENT_RESUMED for the Thread acquiring the GVL for the first time
|
|
193
194
|
// * an early SIGPROF calling handle_sampling_signal()
|
|
@@ -326,7 +327,8 @@ static VALUE per_thread_context_to_ruby_hash(per_thread_context *thread_context)
|
|
|
326
327
|
static VALUE stats_to_ruby_hash(thread_context_collector_state *state, VALUE hash);
|
|
327
328
|
static VALUE gc_tracking_as_ruby_hash(thread_context_collector_state *state);
|
|
328
329
|
static VALUE _native_per_thread_context(VALUE self, VALUE collector_instance);
|
|
329
|
-
static long
|
|
330
|
+
static long update_cpu_time_since_previous_sample(per_thread_context *thread_context, long current_cpu_time_ns);
|
|
331
|
+
static long update_wall_time_since_previous_sample(per_thread_context *thread_context, long current_wall_time_ns);
|
|
330
332
|
static long cpu_time_now_ns(per_thread_context *thread_context);
|
|
331
333
|
static long thread_id_for(VALUE thread);
|
|
332
334
|
static VALUE _native_stats(VALUE self, VALUE collector_instance);
|
|
@@ -359,11 +361,11 @@ static bool handle_gvl_waiting(
|
|
|
359
361
|
per_thread_context *thread_context,
|
|
360
362
|
long current_cpu_time_ns
|
|
361
363
|
);
|
|
364
|
+
static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
365
|
+
static VALUE _native_on_gvl_released(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
362
366
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
363
|
-
static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
364
367
|
static VALUE _native_gvl_waiting_at_for(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
365
368
|
static VALUE _native_on_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread);
|
|
366
|
-
static VALUE _native_on_gvl_released(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
367
369
|
static VALUE _native_sample_after_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread, VALUE allow_exception);
|
|
368
370
|
#endif
|
|
369
371
|
static VALUE _native_apply_delta_to_cpu_time_at_previous_sample_ns(DDTRACE_UNUSED VALUE self, VALUE thread, VALUE delta_ns);
|
|
@@ -378,9 +380,9 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id);
|
|
|
378
380
|
static VALUE safely_lookup_hash_without_going_into_ruby_code(VALUE hash, VALUE key);
|
|
379
381
|
static VALUE _native_system_epoch_time_now_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
|
380
382
|
static VALUE _native_prepare_sample_inside_signal_handler(DDTRACE_UNUSED VALUE self);
|
|
381
|
-
static VALUE _native_clear_per_thread_context_for(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
382
383
|
static VALUE _native_mark_thread_as_profiler_internal(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
383
384
|
static VALUE _native_remove_per_thread_context_for(DDTRACE_UNUSED VALUE self, VALUE thread);
|
|
385
|
+
static VALUE _native_global_reset_per_thread_context(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
|
384
386
|
static bool skip_sample(thread_context_collector_state *state, per_thread_context *thread_context, bool is_gvl_waiting_state, bool force_sample);
|
|
385
387
|
static void on_thread_begin_event(VALUE tracepoint_data, DDTRACE_UNUSED void *unused);
|
|
386
388
|
|
|
@@ -417,14 +419,14 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
|
417
419
|
rb_define_singleton_method(testing_module, "_native_sample_skipped_allocation_samples", _native_sample_skipped_allocation_samples, 2);
|
|
418
420
|
rb_define_singleton_method(testing_module, "_native_system_epoch_time_now_ns", _native_system_epoch_time_now_ns, 1);
|
|
419
421
|
rb_define_singleton_method(testing_module, "_native_prepare_sample_inside_signal_handler", _native_prepare_sample_inside_signal_handler, 0);
|
|
420
|
-
rb_define_singleton_method(testing_module, "_native_clear_per_thread_context_for", _native_clear_per_thread_context_for, 1);
|
|
421
422
|
rb_define_singleton_method(testing_module, "_native_remove_per_thread_context_for", _native_remove_per_thread_context_for, 1);
|
|
423
|
+
rb_define_singleton_method(testing_module, "_native_global_reset_per_thread_context", _native_global_reset_per_thread_context, 1);
|
|
422
424
|
rb_define_singleton_method(testing_module, "_native_mark_thread_as_profiler_internal", _native_mark_thread_as_profiler_internal, 1);
|
|
425
|
+
rb_define_singleton_method(testing_module, "_native_on_gvl_waiting", _native_on_gvl_waiting, 1);
|
|
426
|
+
rb_define_singleton_method(testing_module, "_native_on_gvl_released", _native_on_gvl_released, 1);
|
|
423
427
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
424
|
-
rb_define_singleton_method(testing_module, "_native_on_gvl_waiting", _native_on_gvl_waiting, 1);
|
|
425
428
|
rb_define_singleton_method(testing_module, "_native_gvl_waiting_at_for", _native_gvl_waiting_at_for, 1);
|
|
426
429
|
rb_define_singleton_method(testing_module, "_native_on_gvl_running", _native_on_gvl_running, 2);
|
|
427
|
-
rb_define_singleton_method(testing_module, "_native_on_gvl_released", _native_on_gvl_released, 1);
|
|
428
430
|
rb_define_singleton_method(testing_module, "_native_sample_after_gvl_running", _native_sample_after_gvl_running, 3);
|
|
429
431
|
#endif
|
|
430
432
|
rb_define_singleton_method(testing_module, "_native_apply_delta_to_cpu_time_at_previous_sample_ns", _native_apply_delta_to_cpu_time_at_previous_sample_ns, 2);
|
|
@@ -523,14 +525,9 @@ static void per_thread_context_typed_data_free(void *ctx_ptr) {
|
|
|
523
525
|
free(ctx);
|
|
524
526
|
}
|
|
525
527
|
|
|
526
|
-
static VALUE _native_clear_per_thread_context_for(VALUE self, VALUE thread) {
|
|
527
|
-
_native_remove_per_thread_context_for(self, thread);
|
|
528
|
-
get_or_create_context_for(thread);
|
|
529
|
-
return Qnil;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
528
|
// Only for testing: removes the per-thread context without recreating it, so the thread has no context.
|
|
533
|
-
// This simulates a thread
|
|
529
|
+
// This simulates situations where we observe a thread running before our RUBY_EVENT_THREAD_BEGIN tracepoint fires,
|
|
530
|
+
// so that we can test our `get_per_thread_context(...) => null` code paths.
|
|
534
531
|
static VALUE _native_remove_per_thread_context_for(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
535
532
|
check_frozen_thread(thread);
|
|
536
533
|
per_thread_context *ctx = get_per_thread_context(thread);
|
|
@@ -541,6 +538,11 @@ static VALUE _native_remove_per_thread_context_for(DDTRACE_UNUSED VALUE self, VA
|
|
|
541
538
|
return Qnil;
|
|
542
539
|
}
|
|
543
540
|
|
|
541
|
+
static VALUE _native_global_reset_per_thread_context(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
|
542
|
+
thread_context_collector_reset_all_per_thread_contexts(collector_instance);
|
|
543
|
+
return Qnil;
|
|
544
|
+
}
|
|
545
|
+
|
|
544
546
|
static VALUE _native_new(VALUE klass) {
|
|
545
547
|
thread_context_collector_state *state = ruby_xcalloc(1, sizeof(thread_context_collector_state));
|
|
546
548
|
|
|
@@ -600,7 +602,6 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
600
602
|
ENFORCE_TYPE(overhead_filename, T_STRING);
|
|
601
603
|
|
|
602
604
|
uint16_t max_frame_int = sampling_buffer_check_max_frames(NUM2INT(max_frames));
|
|
603
|
-
latest_max_frames = max_frame_int;
|
|
604
605
|
|
|
605
606
|
thread_context_collector_state *state;
|
|
606
607
|
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
@@ -636,14 +637,6 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
|
636
637
|
if (thread_begin_tracepoint == Qnil) {
|
|
637
638
|
thread_begin_tracepoint = rb_tracepoint_new(Qnil, RUBY_EVENT_THREAD_BEGIN, on_thread_begin_event, NULL);
|
|
638
639
|
rb_tracepoint_enable(thread_begin_tracepoint);
|
|
639
|
-
|
|
640
|
-
VALUE thread_list = rb_ary_new();
|
|
641
|
-
ddtrace_thread_list(thread_list);
|
|
642
|
-
long thread_count = RARRAY_LEN(thread_list);
|
|
643
|
-
for (long i = 0; i < thread_count; i++) {
|
|
644
|
-
get_or_create_context_for(RARRAY_AREF(thread_list, i));
|
|
645
|
-
}
|
|
646
|
-
RB_GC_GUARD(thread_list);
|
|
647
640
|
}
|
|
648
641
|
|
|
649
642
|
return Qtrue;
|
|
@@ -704,17 +697,8 @@ static void record_sampling_overhead(thread_context_collector_state *state, per_
|
|
|
704
697
|
long wall_time_after_sampling = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
|
|
705
698
|
long cpu_time_after_sampling = cpu_time_now_ns(current_thread_context);
|
|
706
699
|
|
|
707
|
-
long overhead_cpu_time_ns =
|
|
708
|
-
|
|
709
|
-
cpu_time_after_sampling,
|
|
710
|
-
current_thread_context->gc_tracking.cpu_time_at_start_ns,
|
|
711
|
-
IS_CPU_TIME);
|
|
712
|
-
|
|
713
|
-
long overhead_wall_time_ns = update_time_since_previous_sample(
|
|
714
|
-
¤t_thread_context->wall_time_at_previous_sample_ns,
|
|
715
|
-
wall_time_after_sampling,
|
|
716
|
-
INVALID_TIME,
|
|
717
|
-
IS_WALL_TIME);
|
|
700
|
+
long overhead_cpu_time_ns = update_cpu_time_since_previous_sample(current_thread_context, cpu_time_after_sampling);
|
|
701
|
+
long overhead_wall_time_ns = update_wall_time_since_previous_sample(current_thread_context, wall_time_after_sampling);
|
|
718
702
|
|
|
719
703
|
ddog_prof_Label overhead_labels[] = {
|
|
720
704
|
{.key = DDOG_CHARSLICE_C("thread id"), .str = DDOG_CHARSLICE_C("0"), .num = 0},
|
|
@@ -810,19 +794,9 @@ static void update_metrics_and_sample(
|
|
|
810
794
|
if (skip_sample(state, thread_context, is_gvl_waiting_state, force_sample)) return;
|
|
811
795
|
|
|
812
796
|
// Don't assign/update cpu during "Waiting for GVL"
|
|
813
|
-
long cpu_time_elapsed_ns = is_gvl_waiting_state ? 0 :
|
|
814
|
-
&thread_context->cpu_time_at_previous_sample_ns,
|
|
815
|
-
current_cpu_time_ns,
|
|
816
|
-
thread_context->gc_tracking.cpu_time_at_start_ns,
|
|
817
|
-
IS_CPU_TIME
|
|
818
|
-
);
|
|
797
|
+
long cpu_time_elapsed_ns = is_gvl_waiting_state ? 0 : update_cpu_time_since_previous_sample(thread_context, current_cpu_time_ns);
|
|
819
798
|
|
|
820
|
-
long wall_time_elapsed_ns =
|
|
821
|
-
&thread_context->wall_time_at_previous_sample_ns,
|
|
822
|
-
current_monotonic_wall_time_ns,
|
|
823
|
-
INVALID_TIME,
|
|
824
|
-
IS_WALL_TIME
|
|
825
|
-
);
|
|
799
|
+
long wall_time_elapsed_ns = update_wall_time_since_previous_sample(thread_context, current_monotonic_wall_time_ns);
|
|
826
800
|
|
|
827
801
|
// A thread enters "Waiting for GVL", well, as the name implies, without the GVL.
|
|
828
802
|
//
|
|
@@ -973,9 +947,9 @@ bool thread_context_collector_on_gc_finish(VALUE self_instance) {
|
|
|
973
947
|
state->gc_tracking.accumulated_wall_time_ns += gc_wall_time_elapsed_ns;
|
|
974
948
|
state->gc_tracking.wall_time_at_previous_gc_ns = wall_time_at_finish_ns;
|
|
975
949
|
|
|
976
|
-
// Update cpu-time accounting so it doesn't include the cpu-time spent in GC during the next sample
|
|
977
|
-
// We don't
|
|
978
|
-
//
|
|
950
|
+
// Update cpu-time accounting so it doesn't include the cpu-time spent in GC during the next sample.
|
|
951
|
+
// We don't do the same for wall-time, because GC is just like any other reason a thread didn't make
|
|
952
|
+
// progress -- time always goes forward regardless of the thread making progress on what it wanted.
|
|
979
953
|
if (thread_context->cpu_time_at_previous_sample_ns != INVALID_TIME) {
|
|
980
954
|
thread_context->cpu_time_at_previous_sample_ns += gc_cpu_time_elapsed_ns;
|
|
981
955
|
}
|
|
@@ -1255,10 +1229,9 @@ static bool is_logging_gem_monkey_patch(VALUE invoke_file_location) {
|
|
|
1255
1229
|
}
|
|
1256
1230
|
|
|
1257
1231
|
static void initialize_context(VALUE thread, per_thread_context *thread_context) {
|
|
1258
|
-
// We always create per_thread_context's with latest_max_frames
|
|
1259
|
-
//
|
|
1260
|
-
//
|
|
1261
|
-
// This is fine because sample_thread() handles when they don't match and resizes as needed.
|
|
1232
|
+
// We always create per_thread_context's with latest_max_frames; that value is kept in sync with the
|
|
1233
|
+
// active profiler's max_frames by the global reset that runs when profiling starts
|
|
1234
|
+
// so we expect to always see here the latest correct value to be used.
|
|
1262
1235
|
sampling_buffer_initialize(&thread_context->sampling_buffer, latest_max_frames);
|
|
1263
1236
|
|
|
1264
1237
|
snprintf(thread_context->thread_id, THREAD_ID_LIMIT_CHARS, "%"PRIu64" (%lu)", native_thread_id_for(thread), (unsigned long) thread_id_for(thread));
|
|
@@ -1303,6 +1276,45 @@ static void initialize_context(VALUE thread, per_thread_context *thread_context)
|
|
|
1303
1276
|
thread_context->gvl_state_change_count = 0;
|
|
1304
1277
|
}
|
|
1305
1278
|
|
|
1279
|
+
// This MUST be called before profiling starts, so that a new profiler session starts from a fresh state and never
|
|
1280
|
+
// observes or includes any leftover stale state from a previous session.
|
|
1281
|
+
// Such a call MUST happen while the CpuAndWallTimeWorker is stopped (e.g. no tracepoints active, no signals
|
|
1282
|
+
// triggering samples, no gvl hooks, etc).
|
|
1283
|
+
//
|
|
1284
|
+
// It updates the global `latest_max_frames` from the given (latest) ThreadContext and (re)creates every per-thread
|
|
1285
|
+
// context's sampling buffer sized accordingly, so the buffers always match the collector that's about to start
|
|
1286
|
+
// sampling -- even if a previous session used a different max_frames.
|
|
1287
|
+
void thread_context_collector_reset_all_per_thread_contexts(VALUE self_instance) {
|
|
1288
|
+
thread_context_collector_state *state;
|
|
1289
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1290
|
+
|
|
1291
|
+
// Update global max frames to be used when allocating sampling buffers
|
|
1292
|
+
if (state->locations.len == 0) {
|
|
1293
|
+
raise_error(rb_eRuntimeError, "BUG: Unexpected locations.len == 0. Is this ThreadContext not initialized?");
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
latest_max_frames = state->locations.len;
|
|
1297
|
+
|
|
1298
|
+
VALUE threads = thread_list(state);
|
|
1299
|
+
const long thread_count = RARRAY_LEN(threads);
|
|
1300
|
+
for (long i = 0; i < thread_count; i++) {
|
|
1301
|
+
VALUE thread = rb_ary_entry(threads, i);
|
|
1302
|
+
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
1303
|
+
if (thread_context != NULL) {
|
|
1304
|
+
bool is_profiler_internal_thread = thread_context->is_profiler_internal_thread;
|
|
1305
|
+
|
|
1306
|
+
sampling_buffer_free(&thread_context->sampling_buffer);
|
|
1307
|
+
memset(thread_context, 0, sizeof(per_thread_context));
|
|
1308
|
+
initialize_context(thread, thread_context);
|
|
1309
|
+
|
|
1310
|
+
thread_context->is_profiler_internal_thread = is_profiler_internal_thread;
|
|
1311
|
+
} else {
|
|
1312
|
+
// If thread didn't have a context, let's trigger its creation
|
|
1313
|
+
get_or_create_context_for(thread);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1306
1318
|
static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
|
1307
1319
|
thread_context_collector_state *state;
|
|
1308
1320
|
TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
@@ -1409,48 +1421,59 @@ static VALUE _native_per_thread_context(DDTRACE_UNUSED VALUE _self, VALUE collec
|
|
|
1409
1421
|
return result;
|
|
1410
1422
|
}
|
|
1411
1423
|
|
|
1412
|
-
|
|
1413
|
-
static long update_time_since_previous_sample(long *time_at_previous_sample_ns, long current_time_ns, long gc_start_time_ns, bool is_wall_time) {
|
|
1424
|
+
static long update_time_since_previous_sample(long *time_at_previous_sample_ns, long current_time_ns, per_thread_context *thread_context) {
|
|
1414
1425
|
// If we didn't have a time for the previous sample, we use the current one
|
|
1415
1426
|
if (*time_at_previous_sample_ns == INVALID_TIME) *time_at_previous_sample_ns = current_time_ns;
|
|
1416
1427
|
|
|
1417
|
-
// We don't
|
|
1418
|
-
//
|
|
1419
|
-
|
|
1420
|
-
|
|
1428
|
+
// We don't expect to be sampling a thread (and thus updating these counters) while Ruby is doing GC (between
|
|
1429
|
+
// `thread_context_collector_on_gc_start` and `thread_context_collector_on_gc_finish`)
|
|
1430
|
+
if (thread_context->gc_tracking.cpu_time_at_start_ns != INVALID_TIME) {
|
|
1431
|
+
raise_error(
|
|
1432
|
+
rb_eRuntimeError,
|
|
1433
|
+
"BUG: Unexpected sample during GC (thread_id=%s, gc_tracking.cpu_time_at_start_ns=%ld, "
|
|
1434
|
+
"gc_tracking.wall_time_at_start_ns=%ld, monotonic_wall_time_now_ns=%ld)",
|
|
1435
|
+
thread_context->thread_id,
|
|
1436
|
+
thread_context->gc_tracking.cpu_time_at_start_ns,
|
|
1437
|
+
thread_context->gc_tracking.wall_time_at_start_ns,
|
|
1438
|
+
monotonic_wall_time_now_ns(RAISE_ON_FAILURE)
|
|
1439
|
+
);
|
|
1440
|
+
}
|
|
1421
1441
|
|
|
1422
|
-
|
|
1423
|
-
|
|
1442
|
+
long elapsed_time_ns = current_time_ns - *time_at_previous_sample_ns; // Capture all time since previous sample
|
|
1443
|
+
*time_at_previous_sample_ns = current_time_ns;
|
|
1424
1444
|
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
} else {
|
|
1428
|
-
elapsed_time_ns = gc_start_time_ns - *time_at_previous_sample_ns; // Capture time between previous sample and start of GC only
|
|
1429
|
-
}
|
|
1445
|
+
return elapsed_time_ns;
|
|
1446
|
+
}
|
|
1430
1447
|
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1448
|
+
static long update_cpu_time_since_previous_sample(per_thread_context *thread_context, long current_cpu_time_ns) {
|
|
1449
|
+
long elapsed_time_ns = update_time_since_previous_sample(
|
|
1450
|
+
&thread_context->cpu_time_at_previous_sample_ns,
|
|
1451
|
+
current_cpu_time_ns,
|
|
1452
|
+
thread_context
|
|
1453
|
+
);
|
|
1437
1454
|
|
|
1455
|
+
// We don't expect cpu-time to go backwards, so let's flag this as a bug
|
|
1438
1456
|
if (elapsed_time_ns < 0) {
|
|
1439
|
-
|
|
1440
|
-
// Wall-time can actually go backwards (e.g. when the system clock gets set) so we can't assume time going backwards
|
|
1441
|
-
// was a bug.
|
|
1442
|
-
// @ivoanjo: I've also observed time going backwards spuriously on macOS, see discussion on
|
|
1443
|
-
// https://github.com/DataDog/dd-trace-rb/pull/2336.
|
|
1444
|
-
elapsed_time_ns = 0;
|
|
1445
|
-
} else {
|
|
1446
|
-
// We don't expect non-wall time to go backwards, so let's flag this as a bug
|
|
1447
|
-
raise_error(rb_eRuntimeError, "BUG: Unexpected negative elapsed_time_ns between samples");
|
|
1448
|
-
}
|
|
1457
|
+
raise_error(rb_eRuntimeError, "BUG: Unexpected CPU time going backwards between samples");
|
|
1449
1458
|
}
|
|
1450
1459
|
|
|
1451
1460
|
return elapsed_time_ns;
|
|
1452
1461
|
}
|
|
1453
1462
|
|
|
1463
|
+
static long update_wall_time_since_previous_sample(per_thread_context *thread_context, long current_wall_time_ns) {
|
|
1464
|
+
long elapsed_time_ns = update_time_since_previous_sample(
|
|
1465
|
+
&thread_context->wall_time_at_previous_sample_ns,
|
|
1466
|
+
current_wall_time_ns,
|
|
1467
|
+
thread_context
|
|
1468
|
+
);
|
|
1469
|
+
|
|
1470
|
+
// Wall-time can actually go backwards (e.g. when the system clock gets set) so we can't assume time going backwards
|
|
1471
|
+
// was a bug.
|
|
1472
|
+
// @ivoanjo: I've also observed time going backwards spuriously on macOS, see discussion on
|
|
1473
|
+
// https://github.com/DataDog/dd-trace-rb/pull/2336.
|
|
1474
|
+
return long_max_of(elapsed_time_ns, 0);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1454
1477
|
// Safety: This function is assumed never to raise exceptions by callers
|
|
1455
1478
|
static long cpu_time_now_ns(per_thread_context *thread_context) {
|
|
1456
1479
|
thread_cpu_time cpu_time = thread_cpu_time_for(thread_context->thread_cpu_time_id);
|
|
@@ -1607,8 +1630,8 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE collector
|
|
|
1607
1630
|
|
|
1608
1631
|
state->stats = (struct stats) {}; // Resets all stats back to zero
|
|
1609
1632
|
|
|
1610
|
-
//
|
|
1611
|
-
|
|
1633
|
+
// No need to clean-up the per-thread context because the CpuAndWallTimeWorker always cleans
|
|
1634
|
+
// it up unconditionally on every start/restart and that includes after a fork.
|
|
1612
1635
|
|
|
1613
1636
|
rb_funcall(state->recorder_instance, rb_intern("reset_after_fork"), 0);
|
|
1614
1637
|
|
|
@@ -2129,18 +2152,44 @@ void thread_context_collector_on_serialize(VALUE self_instance) {
|
|
|
2129
2152
|
}
|
|
2130
2153
|
}
|
|
2131
2154
|
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
}
|
|
2155
|
+
void thread_context_collector_on_gvl_released(per_thread_context *thread_context) {
|
|
2156
|
+
thread_context->gvl_state_change_count |= GVL_SUSPENDED;
|
|
2157
|
+
}
|
|
2136
2158
|
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2159
|
+
void thread_context_collector_on_gvl_waiting(per_thread_context *thread_context) {
|
|
2160
|
+
long current_monotonic_wall_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
|
|
2161
|
+
if (current_monotonic_wall_time_ns <= 0) return;
|
|
2140
2162
|
|
|
2141
|
-
|
|
2142
|
-
|
|
2163
|
+
thread_context->gvl_waiting_at = current_monotonic_wall_time_ns;
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
2167
|
+
ENFORCE_THREAD(thread);
|
|
2168
|
+
|
|
2169
|
+
debug_enter_unsafe_context();
|
|
2170
|
+
|
|
2171
|
+
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
2172
|
+
if (thread_context) thread_context_collector_on_gvl_waiting(thread_context);
|
|
2173
|
+
|
|
2174
|
+
debug_leave_unsafe_context();
|
|
2175
|
+
|
|
2176
|
+
return Qnil;
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
static VALUE _native_on_gvl_released(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
2180
|
+
ENFORCE_THREAD(thread);
|
|
2181
|
+
|
|
2182
|
+
debug_enter_unsafe_context();
|
|
2183
|
+
|
|
2184
|
+
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
2185
|
+
if (thread_context) thread_context_collector_on_gvl_released(thread_context);
|
|
2186
|
+
|
|
2187
|
+
debug_leave_unsafe_context();
|
|
2188
|
+
|
|
2189
|
+
return Qnil;
|
|
2190
|
+
}
|
|
2143
2191
|
|
|
2192
|
+
#ifndef NO_GVL_INSTRUMENTATION
|
|
2144
2193
|
// This function runs on the passed thread and has the GVL because it gets called just after the Ruby thread acquired the GVL
|
|
2145
2194
|
__attribute__((warn_unused_result))
|
|
2146
2195
|
on_gvl_running_result thread_context_collector_on_gvl_running(VALUE self_instance, VALUE thread, per_thread_context *thread_context) {
|
|
@@ -2324,19 +2373,10 @@ void thread_context_collector_on_serialize(VALUE self_instance) {
|
|
|
2324
2373
|
long gvl_waiting_started_wall_time_ns = labs(gvl_waiting_at);
|
|
2325
2374
|
|
|
2326
2375
|
if (thread_context->wall_time_at_previous_sample_ns < gvl_waiting_started_wall_time_ns) { // situation 1 above
|
|
2327
|
-
long cpu_time_elapsed_ns =
|
|
2328
|
-
&thread_context->cpu_time_at_previous_sample_ns,
|
|
2329
|
-
current_cpu_time_ns,
|
|
2330
|
-
thread_context->gc_tracking.cpu_time_at_start_ns,
|
|
2331
|
-
IS_CPU_TIME
|
|
2332
|
-
);
|
|
2376
|
+
long cpu_time_elapsed_ns = update_cpu_time_since_previous_sample(thread_context, current_cpu_time_ns);
|
|
2333
2377
|
|
|
2334
|
-
long duration_until_start_of_gvl_waiting_ns =
|
|
2335
|
-
|
|
2336
|
-
gvl_waiting_started_wall_time_ns,
|
|
2337
|
-
INVALID_TIME,
|
|
2338
|
-
IS_WALL_TIME
|
|
2339
|
-
);
|
|
2378
|
+
long duration_until_start_of_gvl_waiting_ns =
|
|
2379
|
+
update_wall_time_since_previous_sample(thread_context, gvl_waiting_started_wall_time_ns);
|
|
2340
2380
|
|
|
2341
2381
|
// Push extra sample
|
|
2342
2382
|
trigger_sample_for_thread(
|
|
@@ -2355,19 +2395,6 @@ void thread_context_collector_on_serialize(VALUE self_instance) {
|
|
|
2355
2395
|
return true;
|
|
2356
2396
|
}
|
|
2357
2397
|
|
|
2358
|
-
static VALUE _native_on_gvl_waiting(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
2359
|
-
ENFORCE_THREAD(thread);
|
|
2360
|
-
|
|
2361
|
-
debug_enter_unsafe_context();
|
|
2362
|
-
|
|
2363
|
-
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
2364
|
-
if (thread_context) thread_context_collector_on_gvl_waiting(thread_context);
|
|
2365
|
-
|
|
2366
|
-
debug_leave_unsafe_context();
|
|
2367
|
-
|
|
2368
|
-
return Qnil;
|
|
2369
|
-
}
|
|
2370
|
-
|
|
2371
2398
|
static VALUE _native_gvl_waiting_at_for(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
2372
2399
|
ENFORCE_THREAD(thread);
|
|
2373
2400
|
|
|
@@ -2399,19 +2426,6 @@ void thread_context_collector_on_serialize(VALUE self_instance) {
|
|
|
2399
2426
|
return result;
|
|
2400
2427
|
}
|
|
2401
2428
|
|
|
2402
|
-
static VALUE _native_on_gvl_released(DDTRACE_UNUSED VALUE self, VALUE thread) {
|
|
2403
|
-
ENFORCE_THREAD(thread);
|
|
2404
|
-
|
|
2405
|
-
debug_enter_unsafe_context();
|
|
2406
|
-
|
|
2407
|
-
per_thread_context *thread_context = get_per_thread_context(thread);
|
|
2408
|
-
if (thread_context) thread_context_collector_on_gvl_released(thread_context);
|
|
2409
|
-
|
|
2410
|
-
debug_leave_unsafe_context();
|
|
2411
|
-
|
|
2412
|
-
return Qnil;
|
|
2413
|
-
}
|
|
2414
|
-
|
|
2415
2429
|
static VALUE _native_sample_after_gvl_running(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE thread, VALUE allow_exception) {
|
|
2416
2430
|
ENFORCE_THREAD(thread);
|
|
2417
2431
|
ENFORCE_BOOLEAN(allow_exception);
|
|
@@ -21,6 +21,7 @@ VALUE enforce_thread_context_collector_instance(VALUE object);
|
|
|
21
21
|
void thread_context_collector_stats(VALUE self_instance, VALUE stats_hash);
|
|
22
22
|
void thread_context_collector_stats_reset_not_thread_safe(VALUE self_instance);
|
|
23
23
|
void thread_context_collector_on_serialize(VALUE self_instance);
|
|
24
|
+
void thread_context_collector_reset_all_per_thread_contexts(VALUE self_instance);
|
|
24
25
|
void thread_context_collector_profiler_internal_thread_started(void);
|
|
25
26
|
void thread_context_collector_profiler_internal_thread_done(VALUE self_instance);
|
|
26
27
|
|
|
@@ -132,9 +132,6 @@ if RUBY_PLATFORM.include?("linux")
|
|
|
132
132
|
# but it's slower to build
|
|
133
133
|
# so instead we just assume that we have the function we need on Linux, and nowhere else
|
|
134
134
|
$defs << "-DHAVE_PTHREAD_GETCPUCLOCKID"
|
|
135
|
-
|
|
136
|
-
# Not available on macOS
|
|
137
|
-
$defs << "-DHAVE_CLOCK_MONOTONIC_COARSE"
|
|
138
135
|
elsif RUBY_PLATFORM.include?("darwin")
|
|
139
136
|
# On macOS, we use Mach thread APIs to get per-thread CPU time
|
|
140
137
|
$defs << "-DHAVE_MACH_THREAD_INFO"
|
|
@@ -871,3 +871,8 @@ bool is_raised_flag_set(VALUE thread) { return thread_struct_from_object(thread)
|
|
|
871
871
|
VALUE current_fiber_for(DDTRACE_UNUSED VALUE thread) { rb_raise(rb_eRuntimeError, "Not implemented for Ruby < 3.1"); }
|
|
872
872
|
void self_test_current_fiber_for(void) { } // Nothing to do
|
|
873
873
|
#endif
|
|
874
|
+
|
|
875
|
+
bool pathobj_is_null(VALUE iseq) {
|
|
876
|
+
const rb_iseq_t *iseq_ptr = (const rb_iseq_t *) iseq;
|
|
877
|
+
return ISEQ_BODY(iseq_ptr)->location.pathobj == 0;
|
|
878
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
#include <errno.h>
|
|
5
5
|
#include <time.h>
|
|
6
6
|
|
|
7
|
-
#include "extconf.h"
|
|
7
|
+
#include "extconf.h"
|
|
8
8
|
#include "ruby_helpers.h"
|
|
9
9
|
|
|
10
10
|
#define SECONDS_AS_NS(value) (value * 1000 * 1000 * 1000L)
|
|
@@ -33,7 +33,16 @@ static inline long retrieve_clock_as_ns(clockid_t clock_id, raise_on_failure_set
|
|
|
33
33
|
return clock_value.tv_nsec + SECONDS_AS_NS(clock_value.tv_sec);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
// CLOCK_MONOTONIC on macOS only has microsecond precision, CLOCK_MONOTONIC_RAW has nanosecond precision
|
|
37
|
+
#ifdef __APPLE__
|
|
38
|
+
#define CLOCK_MONOTONIC_FOR_PROFILING CLOCK_MONOTONIC_RAW
|
|
39
|
+
#define CLOCK_MONOTONIC_COARSE_FOR_PROFILING CLOCK_MONOTONIC_RAW_APPROX
|
|
40
|
+
#else
|
|
41
|
+
#define CLOCK_MONOTONIC_FOR_PROFILING CLOCK_MONOTONIC
|
|
42
|
+
#define CLOCK_MONOTONIC_COARSE_FOR_PROFILING CLOCK_MONOTONIC_COARSE
|
|
43
|
+
#endif
|
|
44
|
+
|
|
45
|
+
static inline long monotonic_wall_time_now_ns(raise_on_failure_setting raise_on_failure) { return retrieve_clock_as_ns(CLOCK_MONOTONIC_FOR_PROFILING, raise_on_failure); }
|
|
37
46
|
static inline long system_epoch_time_now_ns(raise_on_failure_setting raise_on_failure) { return retrieve_clock_as_ns(CLOCK_REALTIME, raise_on_failure); }
|
|
38
47
|
|
|
39
48
|
// Coarse instants use CLOCK_MONOTONIC_COARSE on Linux which is expected to provide resolution in the millisecond range:
|
|
@@ -47,11 +56,7 @@ typedef struct {
|
|
|
47
56
|
static inline coarse_instant to_coarse_instant(long timestamp_ns) { return (coarse_instant) {.timestamp_ns = timestamp_ns}; }
|
|
48
57
|
|
|
49
58
|
static inline coarse_instant monotonic_coarse_wall_time_now_ns(void) {
|
|
50
|
-
|
|
51
|
-
return to_coarse_instant(retrieve_clock_as_ns(CLOCK_MONOTONIC_COARSE, DO_NOT_RAISE_ON_FAILURE));
|
|
52
|
-
#else // macOS
|
|
53
|
-
return to_coarse_instant(retrieve_clock_as_ns(CLOCK_MONOTONIC, DO_NOT_RAISE_ON_FAILURE));
|
|
54
|
-
#endif
|
|
59
|
+
return to_coarse_instant(retrieve_clock_as_ns(CLOCK_MONOTONIC_COARSE_FOR_PROFILING, DO_NOT_RAISE_ON_FAILURE));
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
long monotonic_to_system_epoch_ns(monotonic_to_system_epoch_state *state, long monotonic_wall_time_ns);
|
|
@@ -83,6 +83,11 @@ module Datadog
|
|
|
83
83
|
# usually Hash[String, String] but can be a more complex
|
|
84
84
|
# Hash[String, (String|Array|Hash)] when e.g coming from JSON
|
|
85
85
|
env['rack.request.form_hash']
|
|
86
|
+
rescue => e
|
|
87
|
+
Datadog.logger.debug { "AppSec: Failed to parse request body: #{e.class}: #{e.message}" }
|
|
88
|
+
AppSec.telemetry.report(e, description: 'AppSec: Failed to parse request body')
|
|
89
|
+
|
|
90
|
+
nil
|
|
86
91
|
end
|
|
87
92
|
|
|
88
93
|
# Returns the request body size in bytes using all available methods,
|
|
@@ -52,6 +52,11 @@ module Datadog
|
|
|
52
52
|
body.reject do |k, _v|
|
|
53
53
|
request.env['action_dispatch.request.path_parameters'].key?(k)
|
|
54
54
|
end
|
|
55
|
+
rescue => e
|
|
56
|
+
Datadog.logger.debug { "AppSec: Failed to parse request body: #{e.class}: #{e.message}" }
|
|
57
|
+
AppSec.telemetry.report(e, description: 'AppSec: Failed to parse request body')
|
|
58
|
+
|
|
59
|
+
nil
|
|
55
60
|
end
|
|
56
61
|
|
|
57
62
|
def route_params
|
|
@@ -824,14 +824,14 @@ module Datadog
|
|
|
824
824
|
# It must respect the interface of [Datadog::Core::Utils::Time#get_time] method.
|
|
825
825
|
#
|
|
826
826
|
# For [Timecop](https://rubygems.org/gems/timecop), for example,
|
|
827
|
-
# `->(unit = :float_second) { ::Process.clock_gettime_without_mock(::
|
|
827
|
+
# `->(unit = :float_second) { ::Process.clock_gettime_without_mock(Datadog::Core::Utils::Time::MONOTONIC_CLOCK_ID, unit) }`
|
|
828
828
|
# allows Datadog features to use the real monotonic time when time is frozen with
|
|
829
829
|
# `Timecop.mock_process_clock = true`.
|
|
830
830
|
#
|
|
831
|
-
# @default `->(unit = :float_second) { ::Process.clock_gettime(::
|
|
831
|
+
# @default `->(unit = :float_second) { ::Process.clock_gettime(Core::Utils::Time::MONOTONIC_CLOCK_ID, unit) }`
|
|
832
832
|
# @return [Proc<Numeric>]
|
|
833
833
|
option :get_time_provider do |o|
|
|
834
|
-
o.default_proc { |unit = :float_second| ::Process.clock_gettime(::
|
|
834
|
+
o.default_proc { |unit = :float_second| ::Process.clock_gettime(Core::Utils::Time::MONOTONIC_CLOCK_ID, unit) }
|
|
835
835
|
o.type :proc
|
|
836
836
|
|
|
837
837
|
o.after_set do |get_time_provider|
|
|
@@ -839,7 +839,7 @@ module Datadog
|
|
|
839
839
|
end
|
|
840
840
|
|
|
841
841
|
o.resetter do |_value|
|
|
842
|
-
->(unit = :float_second) { ::Process.clock_gettime(::
|
|
842
|
+
->(unit = :float_second) { ::Process.clock_gettime(Core::Utils::Time::MONOTONIC_CLOCK_ID, unit) }.tap do |default|
|
|
843
843
|
Core::Utils::Time.get_time_provider = default
|
|
844
844
|
end
|
|
845
845
|
end
|
|
@@ -50,6 +50,7 @@ module Datadog
|
|
|
50
50
|
"DD_DBM_PROPAGATION_MODE",
|
|
51
51
|
"DD_DISABLE_DATADOG_RAILS",
|
|
52
52
|
"DD_DYNAMIC_INSTRUMENTATION_ENABLED",
|
|
53
|
+
"DD_DYNAMIC_INSTRUMENTATION_MAX_TIME_TO_SERIALIZE",
|
|
53
54
|
"DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE",
|
|
54
55
|
"DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS",
|
|
55
56
|
"DD_DYNAMIC_INSTRUMENTATION_REDACTED_TYPES",
|