datadog 2.1.0 → 2.3.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 +101 -1
- data/ext/datadog_profiling_loader/extconf.rb +15 -15
- data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
- data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
- data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +132 -44
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +49 -26
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
- data/ext/datadog_profiling_native_extension/collectors_stack.c +90 -37
- data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +81 -19
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +110 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +57 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +69 -62
- data/ext/datadog_profiling_native_extension/heap_recorder.c +34 -6
- data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
- data/ext/datadog_profiling_native_extension/helpers.h +6 -17
- data/ext/datadog_profiling_native_extension/http_transport.c +3 -3
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -126
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +64 -138
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +17 -11
- data/ext/datadog_profiling_native_extension/profiling.c +0 -2
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +1 -1
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +27 -8
- data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
- data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
- data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
- data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +20 -7
- data/ext/libdatadog_api/datadog_ruby_common.c +110 -0
- data/ext/libdatadog_api/datadog_ruby_common.h +57 -0
- data/ext/libdatadog_api/extconf.rb +108 -0
- data/ext/libdatadog_api/macos_development.md +26 -0
- data/ext/libdatadog_extconf_helpers.rb +130 -0
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +49 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +73 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +68 -0
- data/lib/datadog/appsec/contrib/graphql/integration.rb +41 -0
- data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
- data/lib/datadog/appsec/extensions.rb +1 -0
- data/lib/datadog/appsec/processor/actions.rb +1 -1
- data/lib/datadog/appsec/response.rb +15 -1
- data/lib/datadog/appsec.rb +1 -0
- data/lib/datadog/core/configuration/components.rb +17 -12
- data/lib/datadog/core/configuration/settings.rb +93 -7
- data/lib/datadog/core/configuration.rb +3 -17
- data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
- data/lib/datadog/core/crashtracking/component.rb +111 -0
- data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
- data/lib/datadog/core/deprecations.rb +58 -0
- data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
- data/lib/datadog/core/environment/yjit.rb +5 -0
- data/lib/datadog/core/runtime/ext.rb +1 -0
- data/lib/datadog/core/runtime/metrics.rb +6 -0
- data/lib/datadog/core/telemetry/component.rb +154 -0
- data/lib/datadog/core/telemetry/emitter.rb +9 -11
- data/lib/datadog/core/telemetry/event.rb +132 -26
- data/lib/datadog/core/telemetry/ext.rb +3 -0
- data/lib/datadog/core/telemetry/http/adapters/net.rb +11 -13
- data/lib/datadog/core/telemetry/http/ext.rb +3 -0
- data/lib/datadog/core/telemetry/http/transport.rb +38 -9
- data/lib/datadog/core/telemetry/logging.rb +35 -0
- data/lib/datadog/core/telemetry/metric.rb +167 -0
- data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
- data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
- data/lib/datadog/core/telemetry/request.rb +1 -1
- data/lib/datadog/core/telemetry/worker.rb +173 -0
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
- data/lib/datadog/core/utils/only_once_successful.rb +76 -0
- data/lib/datadog/core.rb +2 -19
- data/lib/datadog/kit/appsec/events.rb +2 -4
- data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +15 -2
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +24 -11
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +17 -17
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
- data/lib/datadog/profiling/collectors/info.rb +3 -3
- data/lib/datadog/profiling/collectors/thread_context.rb +4 -2
- data/lib/datadog/profiling/component.rb +85 -90
- data/lib/datadog/profiling/exporter.rb +3 -3
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
- data/lib/datadog/profiling/ext.rb +21 -21
- data/lib/datadog/profiling/flush.rb +1 -1
- data/lib/datadog/profiling/http_transport.rb +8 -6
- data/lib/datadog/profiling/load_native_extension.rb +5 -5
- data/lib/datadog/profiling/preload.rb +1 -1
- data/lib/datadog/profiling/profiler.rb +5 -8
- data/lib/datadog/profiling/scheduler.rb +31 -25
- data/lib/datadog/profiling/tag_builder.rb +2 -2
- data/lib/datadog/profiling/tasks/exec.rb +5 -5
- data/lib/datadog/profiling/tasks/setup.rb +16 -35
- data/lib/datadog/profiling.rb +5 -5
- data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
- data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
- data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
- data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
- data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
- data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +2 -1
- data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
- data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
- data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
- data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
- data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
- data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
- data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
- data/lib/datadog/tracing/contrib/analytics.rb +5 -0
- data/lib/datadog/tracing/contrib/ext.rb +14 -0
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +28 -0
- data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +16 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
- data/lib/datadog/tracing/contrib/racecar/event.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
- data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
- data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
- data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
- data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
- data/lib/datadog/tracing/distributed/datadog.rb +2 -2
- data/lib/datadog/tracing/distributed/propagation.rb +9 -2
- data/lib/datadog/tracing/distributed/trace_context.rb +3 -2
- data/lib/datadog/tracing/metadata/errors.rb +9 -1
- data/lib/datadog/tracing/metadata/ext.rb +4 -0
- data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
- data/lib/datadog/tracing/span.rb +9 -2
- data/lib/datadog/tracing/span_event.rb +41 -0
- data/lib/datadog/tracing/span_operation.rb +9 -4
- data/lib/datadog/tracing/trace_operation.rb +7 -3
- data/lib/datadog/tracing/trace_segment.rb +4 -1
- data/lib/datadog/tracing/tracer.rb +9 -2
- data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
- data/lib/datadog/tracing.rb +5 -1
- data/lib/datadog/version.rb +2 -2
- metadata +43 -12
- data/lib/datadog/core/telemetry/client.rb +0 -95
- data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
- data/lib/datadog/profiling/crashtracker.rb +0 -91
- data/lib/datadog/profiling/ext/forking.rb +0 -98
@@ -92,7 +92,8 @@ struct thread_context_collector_state {
|
|
92
92
|
// "Update this when modifying state struct"
|
93
93
|
|
94
94
|
// Required by Datadog::Profiling::Collectors::Stack as a scratch buffer during sampling
|
95
|
-
|
95
|
+
ddog_prof_Location *locations;
|
96
|
+
uint16_t max_frames;
|
96
97
|
// Hashmap <Thread Object, struct per_thread_context>
|
97
98
|
st_table *hash_map_per_thread_context;
|
98
99
|
// Datadog::Profiling::StackRecorder instance
|
@@ -138,6 +139,7 @@ struct thread_context_collector_state {
|
|
138
139
|
|
139
140
|
// Tracks per-thread state
|
140
141
|
struct per_thread_context {
|
142
|
+
sampling_buffer *sampling_buffer;
|
141
143
|
char thread_id[THREAD_ID_LIMIT_CHARS];
|
142
144
|
ddog_CharSlice thread_id_char_slice;
|
143
145
|
char thread_invoke_location[THREAD_INVOKE_LOCATION_LIMIT_CHARS];
|
@@ -184,8 +186,9 @@ static VALUE _native_sample_after_gc(DDTRACE_UNUSED VALUE self, VALUE collector_
|
|
184
186
|
void update_metrics_and_sample(
|
185
187
|
struct thread_context_collector_state *state,
|
186
188
|
VALUE thread_being_sampled,
|
187
|
-
VALUE
|
189
|
+
VALUE stack_from_thread,
|
188
190
|
struct per_thread_context *thread_context,
|
191
|
+
sampling_buffer* sampling_buffer,
|
189
192
|
long current_cpu_time_ns,
|
190
193
|
long current_monotonic_wall_time_ns
|
191
194
|
);
|
@@ -194,6 +197,7 @@ static void trigger_sample_for_thread(
|
|
194
197
|
VALUE thread,
|
195
198
|
VALUE stack_from_thread,
|
196
199
|
struct per_thread_context *thread_context,
|
200
|
+
sampling_buffer* sampling_buffer,
|
197
201
|
sample_values values,
|
198
202
|
long current_monotonic_wall_time_ns,
|
199
203
|
ddog_CharSlice *ruby_vm_type,
|
@@ -203,6 +207,7 @@ static VALUE _native_thread_list(VALUE self);
|
|
203
207
|
static struct per_thread_context *get_or_create_context_for(VALUE thread, struct thread_context_collector_state *state);
|
204
208
|
static struct per_thread_context *get_context_for(VALUE thread, struct thread_context_collector_state *state);
|
205
209
|
static void initialize_context(VALUE thread, struct per_thread_context *thread_context, struct thread_context_collector_state *state);
|
210
|
+
static void free_context(struct per_thread_context* thread_context);
|
206
211
|
static VALUE _native_inspect(VALUE self, VALUE collector_instance);
|
207
212
|
static VALUE per_thread_context_st_table_as_ruby_hash(struct thread_context_collector_state *state);
|
208
213
|
static int per_thread_context_as_ruby_hash(st_data_t key_thread, st_data_t value_context, st_data_t result_hash);
|
@@ -231,6 +236,7 @@ static void ddtrace_otel_trace_identifiers_for(
|
|
231
236
|
VALUE active_span,
|
232
237
|
VALUE otel_values
|
233
238
|
);
|
239
|
+
static VALUE _native_sample_skipped_allocation_samples(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE skipped_samples);
|
234
240
|
|
235
241
|
void collectors_thread_context_init(VALUE profiling_module) {
|
236
242
|
VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
|
@@ -261,6 +267,7 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
261
267
|
rb_define_singleton_method(testing_module, "_native_stats", _native_stats, 1);
|
262
268
|
rb_define_singleton_method(testing_module, "_native_gc_tracking", _native_gc_tracking, 1);
|
263
269
|
rb_define_singleton_method(testing_module, "_native_new_empty_thread", _native_new_empty_thread, 0);
|
270
|
+
rb_define_singleton_method(testing_module, "_native_sample_skipped_allocation_samples", _native_sample_skipped_allocation_samples, 2);
|
264
271
|
|
265
272
|
at_active_span_id = rb_intern_const("@active_span");
|
266
273
|
at_active_trace_id = rb_intern_const("@active_trace");
|
@@ -308,7 +315,7 @@ static void thread_context_collector_typed_data_free(void *state_ptr) {
|
|
308
315
|
|
309
316
|
// Important: Remember that we're only guaranteed to see here what's been set in _native_new, aka
|
310
317
|
// pointers that have been set NULL there may still be NULL here.
|
311
|
-
if (state->
|
318
|
+
if (state->locations != NULL) ruby_xfree(state->locations);
|
312
319
|
|
313
320
|
// Free each entry in the map
|
314
321
|
st_foreach(state->hash_map_per_thread_context, hash_map_per_thread_context_free_values, 0 /* unused */);
|
@@ -327,8 +334,8 @@ static int hash_map_per_thread_context_mark(st_data_t key_thread, DDTRACE_UNUSED
|
|
327
334
|
|
328
335
|
// Used to clear each of the per_thread_contexts inside the hash_map_per_thread_context
|
329
336
|
static int hash_map_per_thread_context_free_values(DDTRACE_UNUSED st_data_t _thread, st_data_t value_per_thread_context, DDTRACE_UNUSED st_data_t _argument) {
|
330
|
-
struct per_thread_context *
|
331
|
-
|
337
|
+
struct per_thread_context *thread_context = (struct per_thread_context*) value_per_thread_context;
|
338
|
+
free_context(thread_context);
|
332
339
|
return ST_CONTINUE;
|
333
340
|
}
|
334
341
|
|
@@ -339,23 +346,35 @@ static VALUE _native_new(VALUE klass) {
|
|
339
346
|
// being leaked.
|
340
347
|
|
341
348
|
// Update this when modifying state struct
|
342
|
-
state->
|
349
|
+
state->locations = NULL;
|
350
|
+
state->max_frames = 0;
|
343
351
|
state->hash_map_per_thread_context =
|
344
352
|
// "numtable" is an awful name, but TL;DR it's what should be used when keys are `VALUE`s.
|
345
353
|
st_init_numtable();
|
346
354
|
state->recorder_instance = Qnil;
|
347
355
|
state->tracer_context_key = MISSING_TRACER_CONTEXT_KEY;
|
348
|
-
|
356
|
+
VALUE thread_list_buffer = rb_ary_new();
|
357
|
+
state->thread_list_buffer = thread_list_buffer;
|
349
358
|
state->endpoint_collection_enabled = true;
|
350
359
|
state->timeline_enabled = true;
|
351
360
|
state->allocation_type_enabled = true;
|
352
361
|
state->time_converter_state = (monotonic_to_system_epoch_state) MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER;
|
353
|
-
|
362
|
+
VALUE main_thread = rb_thread_main();
|
363
|
+
state->main_thread = main_thread;
|
354
364
|
state->otel_current_span_key = Qnil;
|
355
365
|
state->gc_tracking.wall_time_at_previous_gc_ns = INVALID_TIME;
|
356
366
|
state->gc_tracking.wall_time_at_last_flushed_gc_event_ns = 0;
|
357
367
|
|
358
|
-
|
368
|
+
// Note: Remember to keep any new allocated objects that get stored in the state also on the stack + mark them with
|
369
|
+
// RB_GC_GUARD -- otherwise it's possible for a GC to run and
|
370
|
+
// since the instance representing the state does not yet exist, such objects will not get marked.
|
371
|
+
|
372
|
+
VALUE instance = TypedData_Wrap_Struct(klass, &thread_context_collector_typed_data, state);
|
373
|
+
|
374
|
+
RB_GC_GUARD(thread_list_buffer);
|
375
|
+
RB_GC_GUARD(main_thread); // Arguably not needed, but perhaps can be move in some future Ruby release?
|
376
|
+
|
377
|
+
return instance;
|
359
378
|
}
|
360
379
|
|
361
380
|
static VALUE _native_initialize(
|
@@ -375,11 +394,9 @@ static VALUE _native_initialize(
|
|
375
394
|
struct thread_context_collector_state *state;
|
376
395
|
TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
377
396
|
|
378
|
-
int max_frames_requested = NUM2INT(max_frames);
|
379
|
-
if (max_frames_requested < 0) rb_raise(rb_eArgError, "Invalid max_frames: value must not be negative");
|
380
|
-
|
381
397
|
// Update this when modifying state struct
|
382
|
-
state->
|
398
|
+
state->max_frames = sampling_buffer_check_max_frames(NUM2INT(max_frames));
|
399
|
+
state->locations = ruby_xcalloc(state->max_frames, sizeof(ddog_prof_Location));
|
383
400
|
// hash_map_per_thread_context is already initialized, nothing to do here
|
384
401
|
state->recorder_instance = enforce_recorder_instance(recorder_instance);
|
385
402
|
state->endpoint_collection_enabled = (endpoint_collection_enabled == Qtrue);
|
@@ -461,6 +478,7 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
|
|
461
478
|
/* thread_being_sampled: */ thread,
|
462
479
|
/* stack_from_thread: */ thread,
|
463
480
|
thread_context,
|
481
|
+
thread_context->sampling_buffer,
|
464
482
|
current_cpu_time_ns,
|
465
483
|
current_monotonic_wall_time_ns
|
466
484
|
);
|
@@ -477,6 +495,8 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
|
|
477
495
|
/* thread_being_sampled: */ current_thread,
|
478
496
|
/* stack_from_thread: */ profiler_overhead_stack_thread,
|
479
497
|
current_thread_context,
|
498
|
+
// Here we use the overhead thread's sampling buffer so as to not invalidate the cache in the buffer of the thread being sampled
|
499
|
+
get_or_create_context_for(profiler_overhead_stack_thread, state)->sampling_buffer,
|
480
500
|
cpu_time_now_ns(current_thread_context),
|
481
501
|
monotonic_wall_time_now_ns(RAISE_ON_FAILURE)
|
482
502
|
);
|
@@ -487,6 +507,7 @@ void update_metrics_and_sample(
|
|
487
507
|
VALUE thread_being_sampled,
|
488
508
|
VALUE stack_from_thread, // This can be different when attributing profiler overhead using a different stack
|
489
509
|
struct per_thread_context *thread_context,
|
510
|
+
sampling_buffer* sampling_buffer,
|
490
511
|
long current_cpu_time_ns,
|
491
512
|
long current_monotonic_wall_time_ns
|
492
513
|
) {
|
@@ -512,6 +533,7 @@ void update_metrics_and_sample(
|
|
512
533
|
thread_being_sampled,
|
513
534
|
stack_from_thread,
|
514
535
|
thread_context,
|
536
|
+
sampling_buffer,
|
515
537
|
(sample_values) {.cpu_time_ns = cpu_time_elapsed_ns, .cpu_or_wall_samples = 1, .wall_time_ns = wall_time_elapsed_ns},
|
516
538
|
current_monotonic_wall_time_ns,
|
517
539
|
NULL,
|
@@ -661,7 +683,6 @@ VALUE thread_context_collector_sample_after_gc(VALUE self_instance) {
|
|
661
683
|
}
|
662
684
|
|
663
685
|
record_placeholder_stack(
|
664
|
-
state->sampling_buffer,
|
665
686
|
state->recorder_instance,
|
666
687
|
(sample_values) {
|
667
688
|
// This event gets both a regular cpu/wall-time duration, as a normal cpu/wall-time sample would, as well as a
|
@@ -692,6 +713,7 @@ static void trigger_sample_for_thread(
|
|
692
713
|
VALUE thread,
|
693
714
|
VALUE stack_from_thread, // This can be different when attributing profiler overhead using a different stack
|
694
715
|
struct per_thread_context *thread_context,
|
716
|
+
sampling_buffer* sampling_buffer,
|
695
717
|
sample_values values,
|
696
718
|
long current_monotonic_wall_time_ns,
|
697
719
|
// These two labels are only used for allocation profiling; @ivoanjo: may want to refactor this at some point?
|
@@ -812,7 +834,7 @@ static void trigger_sample_for_thread(
|
|
812
834
|
|
813
835
|
sample_thread(
|
814
836
|
stack_from_thread,
|
815
|
-
|
837
|
+
sampling_buffer,
|
816
838
|
state->recorder_instance,
|
817
839
|
values,
|
818
840
|
(sample_labels) {.labels = slice_labels, .state_label = state_label, .end_timestamp_ns = end_timestamp_ns}
|
@@ -875,6 +897,8 @@ static bool is_logging_gem_monkey_patch(VALUE invoke_file_location) {
|
|
875
897
|
}
|
876
898
|
|
877
899
|
static void initialize_context(VALUE thread, struct per_thread_context *thread_context, struct thread_context_collector_state *state) {
|
900
|
+
thread_context->sampling_buffer = sampling_buffer_new(state->max_frames, state->locations);
|
901
|
+
|
878
902
|
snprintf(thread_context->thread_id, THREAD_ID_LIMIT_CHARS, "%"PRIu64" (%lu)", native_thread_id_for(thread), (unsigned long) thread_id_for(thread));
|
879
903
|
thread_context->thread_id_char_slice = (ddog_CharSlice) {.ptr = thread_context->thread_id, .len = strlen(thread_context->thread_id)};
|
880
904
|
|
@@ -915,6 +939,11 @@ static void initialize_context(VALUE thread, struct per_thread_context *thread_c
|
|
915
939
|
thread_context->gc_tracking.wall_time_at_start_ns = INVALID_TIME;
|
916
940
|
}
|
917
941
|
|
942
|
+
static void free_context(struct per_thread_context* thread_context) {
|
943
|
+
sampling_buffer_free(thread_context->sampling_buffer);
|
944
|
+
ruby_xfree(thread_context);
|
945
|
+
}
|
946
|
+
|
918
947
|
static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
919
948
|
struct thread_context_collector_state *state;
|
920
949
|
TypedData_Get_Struct(collector_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
@@ -922,6 +951,7 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
|
|
922
951
|
VALUE result = rb_str_new2(" (native state)");
|
923
952
|
|
924
953
|
// Update this when modifying state struct
|
954
|
+
rb_str_concat(result, rb_sprintf(" max_frames=%d", state->max_frames));
|
925
955
|
rb_str_concat(result, rb_sprintf(" hash_map_per_thread_context=%"PRIsVALUE, per_thread_context_st_table_as_ruby_hash(state)));
|
926
956
|
rb_str_concat(result, rb_sprintf(" recorder_instance=%"PRIsVALUE, state->recorder_instance));
|
927
957
|
VALUE tracer_context_key = state->tracer_context_key == MISSING_TRACER_CONTEXT_KEY ? Qnil : ID2SYM(state->tracer_context_key);
|
@@ -1006,7 +1036,7 @@ static int remove_if_dead_thread(st_data_t key_thread, st_data_t value_context,
|
|
1006
1036
|
|
1007
1037
|
if (is_thread_alive(thread)) return ST_CONTINUE;
|
1008
1038
|
|
1009
|
-
|
1039
|
+
free_context(thread_context);
|
1010
1040
|
return ST_DELETE;
|
1011
1041
|
}
|
1012
1042
|
|
@@ -1257,7 +1287,7 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
1257
1287
|
// Thus, we need to make sure there's actually a class before getting its name.
|
1258
1288
|
|
1259
1289
|
if (klass != 0) {
|
1260
|
-
const char *name =
|
1290
|
+
const char *name = rb_class2name(klass);
|
1261
1291
|
size_t name_length = name != NULL ? strlen(name) : 0;
|
1262
1292
|
|
1263
1293
|
if (name_length > 0) {
|
@@ -1285,12 +1315,15 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
1285
1315
|
|
1286
1316
|
track_object(state->recorder_instance, new_object, sample_weight, optional_class_name);
|
1287
1317
|
|
1318
|
+
struct per_thread_context *thread_context = get_or_create_context_for(current_thread, state);
|
1319
|
+
|
1288
1320
|
trigger_sample_for_thread(
|
1289
1321
|
state,
|
1290
1322
|
/* thread: */ current_thread,
|
1291
1323
|
/* stack_from_thread: */ current_thread,
|
1292
|
-
|
1293
|
-
|
1324
|
+
thread_context,
|
1325
|
+
thread_context->sampling_buffer,
|
1326
|
+
(sample_values) {.alloc_samples = sample_weight, .alloc_samples_unscaled = 1, .heap_sample = true},
|
1294
1327
|
INVALID_TIME, // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
|
1295
1328
|
&ruby_vm_type,
|
1296
1329
|
optional_class_name
|
@@ -1400,3 +1433,32 @@ static void ddtrace_otel_trace_identifiers_for(
|
|
1400
1433
|
*active_trace = current_trace;
|
1401
1434
|
*numeric_span_id = resolved_numeric_span_id;
|
1402
1435
|
}
|
1436
|
+
|
1437
|
+
void thread_context_collector_sample_skipped_allocation_samples(VALUE self_instance, unsigned int skipped_samples) {
|
1438
|
+
struct thread_context_collector_state *state;
|
1439
|
+
TypedData_Get_Struct(self_instance, struct thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1440
|
+
|
1441
|
+
ddog_prof_Label labels[] = {
|
1442
|
+
// Providing .num = 0 should not be needed but the tracer-2.7 docker image ships a buggy gcc that complains about this
|
1443
|
+
{.key = DDOG_CHARSLICE_C("thread id"), .str = DDOG_CHARSLICE_C("SS"), .num = 0},
|
1444
|
+
{.key = DDOG_CHARSLICE_C("thread name"), .str = DDOG_CHARSLICE_C("Skipped Samples"), .num = 0},
|
1445
|
+
{.key = DDOG_CHARSLICE_C("allocation class"), .str = DDOG_CHARSLICE_C("(Skipped Samples)"), .num = 0},
|
1446
|
+
};
|
1447
|
+
ddog_prof_Slice_Label slice_labels = {.ptr = labels, .len = sizeof(labels) / sizeof(labels[0])};
|
1448
|
+
|
1449
|
+
record_placeholder_stack(
|
1450
|
+
state->recorder_instance,
|
1451
|
+
(sample_values) {.alloc_samples = skipped_samples},
|
1452
|
+
(sample_labels) {
|
1453
|
+
.labels = slice_labels,
|
1454
|
+
.state_label = NULL,
|
1455
|
+
.end_timestamp_ns = 0, // For now we're not collecting timestamps for allocation events
|
1456
|
+
},
|
1457
|
+
DDOG_CHARSLICE_C("Skipped Samples")
|
1458
|
+
);
|
1459
|
+
}
|
1460
|
+
|
1461
|
+
static VALUE _native_sample_skipped_allocation_samples(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE skipped_samples) {
|
1462
|
+
thread_context_collector_sample_skipped_allocation_samples(collector_instance, NUM2UINT(skipped_samples));
|
1463
|
+
return Qtrue;
|
1464
|
+
}
|
@@ -9,6 +9,7 @@ void thread_context_collector_sample(
|
|
9
9
|
VALUE profiler_overhead_stack_thread
|
10
10
|
);
|
11
11
|
void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object);
|
12
|
+
void thread_context_collector_sample_skipped_allocation_samples(VALUE self_instance, unsigned int skipped_samples);
|
12
13
|
VALUE thread_context_collector_sample_after_gc(VALUE self_instance);
|
13
14
|
void thread_context_collector_on_gc_start(VALUE self_instance);
|
14
15
|
bool thread_context_collector_on_gc_finish(VALUE self_instance);
|
@@ -0,0 +1,110 @@
|
|
1
|
+
#include "datadog_ruby_common.h"
|
2
|
+
|
3
|
+
// IMPORTANT: Currently this file is copy-pasted between extensions. Make sure to update all versions when doing any change!
|
4
|
+
|
5
|
+
void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name) {
|
6
|
+
rb_exc_raise(
|
7
|
+
rb_exc_new_str(
|
8
|
+
rb_eTypeError,
|
9
|
+
rb_sprintf("wrong argument %"PRIsVALUE" for '%s' (expected a %s) at %s:%d:in `%s'",
|
10
|
+
rb_inspect(value),
|
11
|
+
value_name,
|
12
|
+
type_name,
|
13
|
+
file,
|
14
|
+
line,
|
15
|
+
function_name
|
16
|
+
)
|
17
|
+
)
|
18
|
+
);
|
19
|
+
}
|
20
|
+
|
21
|
+
VALUE datadog_gem_version(void) {
|
22
|
+
VALUE ddtrace_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
|
23
|
+
ENFORCE_TYPE(ddtrace_module, T_MODULE);
|
24
|
+
VALUE version_module = rb_const_get(ddtrace_module, rb_intern("VERSION"));
|
25
|
+
ENFORCE_TYPE(version_module, T_MODULE);
|
26
|
+
VALUE version_string = rb_const_get(version_module, rb_intern("STRING"));
|
27
|
+
ENFORCE_TYPE(version_string, T_STRING);
|
28
|
+
return version_string;
|
29
|
+
}
|
30
|
+
|
31
|
+
__attribute__((warn_unused_result))
|
32
|
+
ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration) {
|
33
|
+
ENFORCE_TYPE(exporter_configuration, T_ARRAY);
|
34
|
+
|
35
|
+
VALUE exporter_working_mode = rb_ary_entry(exporter_configuration, 0);
|
36
|
+
ENFORCE_TYPE(exporter_working_mode, T_SYMBOL);
|
37
|
+
ID working_mode = SYM2ID(exporter_working_mode);
|
38
|
+
|
39
|
+
ID agentless_id = rb_intern("agentless");
|
40
|
+
ID agent_id = rb_intern("agent");
|
41
|
+
|
42
|
+
if (working_mode != agentless_id && working_mode != agent_id) {
|
43
|
+
rb_raise(rb_eArgError, "Failed to initialize transport: Unexpected working mode, expected :agentless or :agent");
|
44
|
+
}
|
45
|
+
|
46
|
+
if (working_mode == agentless_id) {
|
47
|
+
VALUE site = rb_ary_entry(exporter_configuration, 1);
|
48
|
+
VALUE api_key = rb_ary_entry(exporter_configuration, 2);
|
49
|
+
|
50
|
+
return ddog_prof_Endpoint_agentless(char_slice_from_ruby_string(site), char_slice_from_ruby_string(api_key));
|
51
|
+
} else { // agent_id
|
52
|
+
VALUE base_url = rb_ary_entry(exporter_configuration, 1);
|
53
|
+
|
54
|
+
return ddog_prof_Endpoint_agent(char_slice_from_ruby_string(base_url));
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
static VALUE log_failure_to_process_tag(VALUE err_details) {
|
59
|
+
VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
|
60
|
+
VALUE logger = rb_funcall(datadog_module, rb_intern("logger"), 0);
|
61
|
+
|
62
|
+
return rb_funcall(logger, rb_intern("warn"), 1, rb_sprintf("Failed to convert tag: %"PRIsVALUE, err_details));
|
63
|
+
}
|
64
|
+
|
65
|
+
__attribute__((warn_unused_result))
|
66
|
+
ddog_Vec_Tag convert_tags(VALUE tags_as_array) {
|
67
|
+
ENFORCE_TYPE(tags_as_array, T_ARRAY);
|
68
|
+
|
69
|
+
long tags_count = RARRAY_LEN(tags_as_array);
|
70
|
+
ddog_Vec_Tag tags = ddog_Vec_Tag_new();
|
71
|
+
|
72
|
+
for (long i = 0; i < tags_count; i++) {
|
73
|
+
VALUE name_value_pair = rb_ary_entry(tags_as_array, i);
|
74
|
+
|
75
|
+
if (!RB_TYPE_P(name_value_pair, T_ARRAY)) {
|
76
|
+
ddog_Vec_Tag_drop(tags);
|
77
|
+
ENFORCE_TYPE(name_value_pair, T_ARRAY);
|
78
|
+
}
|
79
|
+
|
80
|
+
// Note: We can index the array without checking its size first because rb_ary_entry returns Qnil if out of bounds
|
81
|
+
VALUE tag_name = rb_ary_entry(name_value_pair, 0);
|
82
|
+
VALUE tag_value = rb_ary_entry(name_value_pair, 1);
|
83
|
+
|
84
|
+
if (!(RB_TYPE_P(tag_name, T_STRING) && RB_TYPE_P(tag_value, T_STRING))) {
|
85
|
+
ddog_Vec_Tag_drop(tags);
|
86
|
+
ENFORCE_TYPE(tag_name, T_STRING);
|
87
|
+
ENFORCE_TYPE(tag_value, T_STRING);
|
88
|
+
}
|
89
|
+
|
90
|
+
ddog_Vec_Tag_PushResult push_result =
|
91
|
+
ddog_Vec_Tag_push(&tags, char_slice_from_ruby_string(tag_name), char_slice_from_ruby_string(tag_value));
|
92
|
+
|
93
|
+
if (push_result.tag == DDOG_VEC_TAG_PUSH_RESULT_ERR) {
|
94
|
+
// libdatadog validates tags and may catch invalid tags that ddtrace didn't actually catch.
|
95
|
+
// We warn users about such tags, and then just ignore them.
|
96
|
+
|
97
|
+
int exception_state;
|
98
|
+
rb_protect(log_failure_to_process_tag, get_error_details_and_drop(&push_result.err), &exception_state);
|
99
|
+
|
100
|
+
// Since we are calling into Ruby code, it may raise an exception. Ensure that dynamically-allocated tags
|
101
|
+
// get cleaned before propagating the exception.
|
102
|
+
if (exception_state) {
|
103
|
+
ddog_Vec_Tag_drop(tags);
|
104
|
+
rb_jump_tag(exception_state); // "Re-raise" exception
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
return tags;
|
110
|
+
}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
// IMPORTANT: Currently this file is copy-pasted between extensions. Make sure to update all versions when doing any change!
|
4
|
+
|
5
|
+
#include <ruby.h>
|
6
|
+
#include <datadog/profiling.h>
|
7
|
+
|
8
|
+
// Used to mark symbols to be exported to the outside of the extension.
|
9
|
+
// Consider very carefully before tagging a function with this.
|
10
|
+
#define DDTRACE_EXPORT __attribute__ ((visibility ("default")))
|
11
|
+
|
12
|
+
// Used to mark function arguments that are deliberately left unused
|
13
|
+
#ifdef __GNUC__
|
14
|
+
#define DDTRACE_UNUSED __attribute__((unused))
|
15
|
+
#else
|
16
|
+
#define DDTRACE_UNUSED
|
17
|
+
#endif
|
18
|
+
|
19
|
+
#define ADD_QUOTES_HELPER(x) #x
|
20
|
+
#define ADD_QUOTES(x) ADD_QUOTES_HELPER(x)
|
21
|
+
|
22
|
+
// Ruby has a Check_Type(value, type) that is roughly equivalent to this BUT Ruby's version is rather cryptic when it fails
|
23
|
+
// e.g. "wrong argument type nil (expected String)". This is a replacement that prints more information to help debugging.
|
24
|
+
#define ENFORCE_TYPE(value, type) \
|
25
|
+
{ if (RB_UNLIKELY(!RB_TYPE_P(value, type))) raise_unexpected_type(value, ADD_QUOTES(value), ADD_QUOTES(type), __FILE__, __LINE__, __func__); }
|
26
|
+
|
27
|
+
#define ENFORCE_BOOLEAN(value) \
|
28
|
+
{ if (RB_UNLIKELY(value != Qtrue && value != Qfalse)) raise_unexpected_type(value, ADD_QUOTES(value), "true or false", __FILE__, __LINE__, __func__); }
|
29
|
+
|
30
|
+
// Called by ENFORCE_TYPE; should not be used directly
|
31
|
+
NORETURN(void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name));
|
32
|
+
|
33
|
+
// Helper to retrieve Datadog::VERSION::STRING
|
34
|
+
VALUE datadog_gem_version(void);
|
35
|
+
|
36
|
+
static inline ddog_CharSlice char_slice_from_ruby_string(VALUE string) {
|
37
|
+
ENFORCE_TYPE(string, T_STRING);
|
38
|
+
ddog_CharSlice char_slice = {.ptr = RSTRING_PTR(string), .len = RSTRING_LEN(string)};
|
39
|
+
return char_slice;
|
40
|
+
}
|
41
|
+
|
42
|
+
__attribute__((warn_unused_result))
|
43
|
+
ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration);
|
44
|
+
|
45
|
+
__attribute__((warn_unused_result))
|
46
|
+
ddog_Vec_Tag convert_tags(VALUE tags_as_array);
|
47
|
+
|
48
|
+
static inline VALUE ruby_string_from_error(const ddog_Error *error) {
|
49
|
+
ddog_CharSlice char_slice = ddog_Error_message(error);
|
50
|
+
return rb_str_new(char_slice.ptr, char_slice.len);
|
51
|
+
}
|
52
|
+
|
53
|
+
static inline VALUE get_error_details_and_drop(ddog_Error *error) {
|
54
|
+
VALUE result = ruby_string_from_error(error);
|
55
|
+
ddog_Error_drop(error);
|
56
|
+
return result;
|
57
|
+
}
|