datadog 2.28.0 → 2.30.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 +87 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +82 -12
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +32 -11
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +3 -1
- data/ext/datadog_profiling_native_extension/extconf.rb +9 -24
- data/ext/datadog_profiling_native_extension/heap_recorder.c +186 -55
- data/ext/datadog_profiling_native_extension/heap_recorder.h +12 -1
- data/ext/datadog_profiling_native_extension/http_transport.c +51 -64
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -13
- data/ext/datadog_profiling_native_extension/profiling.c +3 -1
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +24 -8
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -3
- data/ext/datadog_profiling_native_extension/stack_recorder.c +63 -48
- data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -1
- data/ext/libdatadog_api/crashtracker.c +5 -0
- data/ext/libdatadog_api/crashtracker_report_exception.c +126 -0
- data/ext/libdatadog_api/extconf.rb +4 -21
- data/ext/libdatadog_extconf_helpers.rb +49 -11
- data/lib/datadog/ai_guard/configuration/settings.rb +3 -0
- data/lib/datadog/appsec/assets/blocked.html +2 -1
- data/lib/datadog/appsec/configuration/settings.rb +14 -0
- data/lib/datadog/appsec/context.rb +44 -9
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +3 -0
- data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/patcher.rb +2 -0
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +55 -6
- data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/faraday/patcher.rb +1 -1
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +60 -7
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +11 -6
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +1 -1
- data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +6 -10
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +4 -4
- data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +26 -5
- data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +2 -2
- data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.rb +2 -0
- data/lib/datadog/appsec/contrib/rest_client/patcher.rb +2 -0
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +72 -7
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +5 -3
- data/lib/datadog/appsec/counter_sampler.rb +25 -0
- data/lib/datadog/appsec/event.rb +1 -17
- data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +2 -3
- data/lib/datadog/appsec/instrumentation/gateway.rb +2 -2
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +18 -0
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +2 -2
- data/lib/datadog/appsec/security_engine/engine.rb +23 -2
- data/lib/datadog/appsec/utils/http/body.rb +38 -0
- data/lib/datadog/appsec/utils/http/media_range.rb +2 -1
- data/lib/datadog/appsec/utils/http/media_type.rb +28 -35
- data/lib/datadog/appsec/utils/http/url_encoded.rb +52 -0
- data/lib/datadog/core/configuration/components.rb +29 -4
- data/lib/datadog/core/configuration/option.rb +2 -1
- data/lib/datadog/core/configuration/options.rb +1 -1
- data/lib/datadog/core/configuration/settings.rb +27 -3
- data/lib/datadog/core/configuration/supported_configurations.rb +19 -0
- data/lib/datadog/core/configuration.rb +2 -2
- data/lib/datadog/core/crashtracking/component.rb +71 -19
- data/lib/datadog/core/environment/agent_info.rb +65 -1
- data/lib/datadog/core/logger.rb +1 -1
- data/lib/datadog/core/metrics/logging.rb +1 -1
- data/lib/datadog/core/process_discovery.rb +20 -19
- data/lib/datadog/core/rate_limiter.rb +2 -0
- data/lib/datadog/core/remote/component.rb +16 -5
- data/lib/datadog/core/remote/transport/config.rb +5 -11
- data/lib/datadog/core/runtime/metrics.rb +1 -2
- data/lib/datadog/core/telemetry/component.rb +0 -13
- data/lib/datadog/core/telemetry/transport/telemetry.rb +5 -6
- data/lib/datadog/core/transport/ext.rb +1 -0
- data/lib/datadog/core/transport/http/response.rb +4 -0
- data/lib/datadog/core/transport/parcel.rb +61 -9
- data/lib/datadog/core/utils/base64.rb +1 -1
- data/lib/datadog/core/utils/fnv.rb +26 -0
- data/lib/datadog/core/workers/interval_loop.rb +13 -6
- data/lib/datadog/core/workers/queue.rb +0 -4
- data/lib/datadog/core/workers/runtime_metrics.rb +9 -1
- data/lib/datadog/core.rb +6 -1
- data/lib/datadog/data_streams/processor.rb +35 -33
- data/lib/datadog/data_streams/transport/http/stats.rb +6 -0
- data/lib/datadog/data_streams/transport/http.rb +0 -4
- data/lib/datadog/data_streams/transport/stats.rb +5 -12
- data/lib/datadog/di/boot.rb +1 -0
- data/lib/datadog/di/component.rb +17 -5
- data/lib/datadog/di/configuration/settings.rb +9 -0
- data/lib/datadog/di/context.rb +6 -0
- data/lib/datadog/di/instrumenter.rb +183 -134
- data/lib/datadog/di/probe.rb +10 -1
- data/lib/datadog/di/probe_file_loader.rb +2 -2
- data/lib/datadog/di/probe_manager.rb +86 -64
- data/lib/datadog/di/probe_notification_builder.rb +46 -18
- data/lib/datadog/di/probe_notifier_worker.rb +65 -9
- data/lib/datadog/di/probe_repository.rb +198 -0
- data/lib/datadog/di/proc_responder.rb +4 -0
- data/lib/datadog/di/remote.rb +6 -7
- data/lib/datadog/di/serializer.rb +127 -9
- data/lib/datadog/di/transport/diagnostics.rb +5 -7
- data/lib/datadog/di/transport/http/diagnostics.rb +3 -1
- data/lib/datadog/di/transport/http/input.rb +1 -1
- data/lib/datadog/di/transport/http.rb +12 -3
- data/lib/datadog/di/transport/input.rb +51 -14
- data/lib/datadog/kit/tracing/method_tracer.rb +132 -0
- data/lib/datadog/open_feature/configuration.rb +2 -0
- data/lib/datadog/open_feature/transport.rb +8 -11
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +13 -0
- data/lib/datadog/profiling/component.rb +20 -6
- data/lib/datadog/profiling/http_transport.rb +5 -6
- data/lib/datadog/profiling/profiler.rb +15 -8
- data/lib/datadog/tracing/contrib/dalli/integration.rb +4 -1
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +2 -2
- data/lib/datadog/tracing/contrib/grape/instrumentation.rb +13 -8
- data/lib/datadog/tracing/contrib/grape/patcher.rb +6 -1
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/http/integration.rb +0 -2
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/httpclient/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/karafka/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +2 -1
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +2 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +10 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +24 -0
- data/lib/datadog/tracing/contrib/que/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/rack/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +18 -6
- data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/rails/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/registerable.rb +11 -0
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +5 -2
- data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/sinatra/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sneakers/integration.rb +15 -4
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +6 -0
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +3 -1
- data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/waterdrop/ext.rb +1 -0
- data/lib/datadog/tracing/metadata/ext.rb +4 -0
- data/lib/datadog/tracing/sync_writer.rb +0 -1
- data/lib/datadog/tracing/transport/io/client.rb +5 -8
- data/lib/datadog/tracing/transport/io/traces.rb +28 -34
- data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
- data/lib/datadog/tracing/transport/traces.rb +4 -10
- data/lib/datadog/tracing/writer.rb +0 -1
- data/lib/datadog/version.rb +1 -1
- metadata +14 -8
- data/lib/datadog/appsec/contrib/rails/ext.rb +0 -13
- data/lib/datadog/tracing/workers/trace_writer.rb +0 -204
|
@@ -79,6 +79,16 @@ static void object_record_free(heap_recorder*, object_record*);
|
|
|
79
79
|
static VALUE object_record_inspect(heap_recorder*, object_record*);
|
|
80
80
|
static object_record SKIPPED_RECORD = {0};
|
|
81
81
|
|
|
82
|
+
// A pending recording is used to defer the object_id call on Ruby 4+
|
|
83
|
+
// where calling rb_obj_id during on_newobj_event is unsafe.
|
|
84
|
+
typedef struct {
|
|
85
|
+
VALUE object_ref;
|
|
86
|
+
heap_record *heap_record;
|
|
87
|
+
live_object_data object_data;
|
|
88
|
+
} pending_recording;
|
|
89
|
+
|
|
90
|
+
#define MAX_PENDING_RECORDINGS 256
|
|
91
|
+
|
|
82
92
|
struct heap_recorder {
|
|
83
93
|
// Config
|
|
84
94
|
// Whether the recorder should try to determine approximate sizes for tracked objects.
|
|
@@ -130,6 +140,15 @@ struct heap_recorder {
|
|
|
130
140
|
// Data for a heap recording that was started but not yet ended
|
|
131
141
|
object_record *active_recording;
|
|
132
142
|
|
|
143
|
+
// Pending recordings that need to be finalized after on_newobj_event completes.
|
|
144
|
+
// On Ruby 4+, we can't call rb_obj_id during the newobj event, so we store the
|
|
145
|
+
// VALUE reference here and finalize it via a postponed job.
|
|
146
|
+
pending_recording pending_recordings[MAX_PENDING_RECORDINGS];
|
|
147
|
+
// Temporary storage for the recording in progress, used between start and end
|
|
148
|
+
VALUE active_deferred_object;
|
|
149
|
+
live_object_data active_deferred_object_data;
|
|
150
|
+
uint16_t pending_recordings_count;
|
|
151
|
+
|
|
133
152
|
// Reusable arrays, implementing a flyweight pattern for things like iteration
|
|
134
153
|
#define REUSABLE_LOCATIONS_SIZE MAX_FRAMES_LIMIT
|
|
135
154
|
ddog_prof_Location *reusable_locations;
|
|
@@ -163,6 +182,9 @@ struct heap_recorder {
|
|
|
163
182
|
double ewma_objects_alive;
|
|
164
183
|
double ewma_objects_dead;
|
|
165
184
|
double ewma_objects_skipped;
|
|
185
|
+
|
|
186
|
+
unsigned long deferred_recordings_skipped_buffer_full;
|
|
187
|
+
unsigned long deferred_recordings_finalized;
|
|
166
188
|
} stats_lifetime;
|
|
167
189
|
};
|
|
168
190
|
|
|
@@ -180,6 +202,7 @@ static int st_object_record_update(st_data_t, st_data_t, st_data_t);
|
|
|
180
202
|
static int st_object_records_iterate(st_data_t, st_data_t, st_data_t);
|
|
181
203
|
static int st_object_records_debug(st_data_t key, st_data_t value, st_data_t extra);
|
|
182
204
|
static int update_object_record_entry(st_data_t*, st_data_t*, st_data_t, int);
|
|
205
|
+
static void inc_tracked_objects_or_fail(heap_record *heap_record);
|
|
183
206
|
static void commit_recording(heap_recorder *, heap_record *, object_record *active_recording);
|
|
184
207
|
static VALUE end_heap_allocation_recording(VALUE end_heap_allocation_args);
|
|
185
208
|
static void heap_recorder_update(heap_recorder *heap_recorder, bool full_update);
|
|
@@ -187,6 +210,7 @@ static inline double ewma_stat(double previous, double current);
|
|
|
187
210
|
static void unintern_or_raise(heap_recorder *, ddog_prof_ManagedStringId);
|
|
188
211
|
static void unintern_all_or_raise(heap_recorder *recorder, ddog_prof_Slice_ManagedStringId ids);
|
|
189
212
|
static VALUE get_ruby_string_or_raise(heap_recorder*, ddog_prof_ManagedStringId);
|
|
213
|
+
static long obj_id_or_fail(VALUE obj);
|
|
190
214
|
|
|
191
215
|
// ==========================
|
|
192
216
|
// Heap Recorder External API
|
|
@@ -210,6 +234,7 @@ heap_recorder* heap_recorder_new(ddog_prof_ManagedStringStorage string_storage)
|
|
|
210
234
|
recorder->size_enabled = true;
|
|
211
235
|
recorder->sample_rate = 1; // By default do no sampling on top of what allocation profiling already does
|
|
212
236
|
recorder->string_storage = string_storage;
|
|
237
|
+
recorder->active_deferred_object = Qnil;
|
|
213
238
|
|
|
214
239
|
return recorder;
|
|
215
240
|
}
|
|
@@ -294,9 +319,9 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder) {
|
|
|
294
319
|
heap_recorder->stats_lifetime = (struct stats_lifetime) {0};
|
|
295
320
|
}
|
|
296
321
|
|
|
297
|
-
|
|
322
|
+
bool start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice alloc_class) {
|
|
298
323
|
if (heap_recorder == NULL) {
|
|
299
|
-
return;
|
|
324
|
+
return false;
|
|
300
325
|
}
|
|
301
326
|
|
|
302
327
|
if (heap_recorder->active_recording != NULL) {
|
|
@@ -316,25 +341,49 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj
|
|
|
316
341
|
|| heap_recorder->updating
|
|
317
342
|
) {
|
|
318
343
|
heap_recorder->active_recording = &SKIPPED_RECORD;
|
|
319
|
-
return;
|
|
344
|
+
return false;
|
|
320
345
|
}
|
|
321
346
|
|
|
322
|
-
|
|
347
|
+
bool needs_after_allocation = false;
|
|
323
348
|
|
|
324
|
-
|
|
325
|
-
if
|
|
326
|
-
|
|
349
|
+
#ifdef USE_DEFERRED_HEAP_ALLOCATION_RECORDING
|
|
350
|
+
// Skip if we've hit the pending recordings limit or if there's already a deferred object being recorded
|
|
351
|
+
if (heap_recorder->pending_recordings_count >= MAX_PENDING_RECORDINGS) {
|
|
352
|
+
heap_recorder->stats_lifetime.deferred_recordings_skipped_buffer_full++;
|
|
353
|
+
heap_recorder->active_recording = &SKIPPED_RECORD;
|
|
354
|
+
return true; // If the buffer is full, we keep asking for a callback (see `needs_after_allocation` below)
|
|
355
|
+
} else {
|
|
356
|
+
// The intuition here is: We start by asking for an `after_allocation` callback when the buffer is about to go
|
|
357
|
+
// from empty -> non-empty, because this is going to be mapped onto a postponed job, so after it gets queued once
|
|
358
|
+
// it doesn't seem worth it to keep spamming requests.
|
|
359
|
+
//
|
|
360
|
+
// Yet, if for some reason the postponed job doesn't flush the pending list (or if e.g. it ran with `during_sample == true` and thus
|
|
361
|
+
// was skipped) we need to have some mechanism to recover -- and so if the buffer starts accumulating too much we
|
|
362
|
+
// start always requesting the callback to happen so that we eventually flush the buffer.
|
|
363
|
+
needs_after_allocation =
|
|
364
|
+
heap_recorder->pending_recordings_count == 0 || heap_recorder->pending_recordings_count >= (MAX_PENDING_RECORDINGS / 2);
|
|
327
365
|
}
|
|
366
|
+
#endif
|
|
328
367
|
|
|
329
|
-
heap_recorder->
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
368
|
+
heap_recorder->num_recordings_skipped = 0;
|
|
369
|
+
|
|
370
|
+
live_object_data object_data = (live_object_data) {
|
|
371
|
+
.weight = weight * heap_recorder->sample_rate,
|
|
372
|
+
.class = intern_or_raise(heap_recorder->string_storage, alloc_class),
|
|
373
|
+
.alloc_gen = rb_gc_count(),
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
#ifdef USE_DEFERRED_HEAP_ALLOCATION_RECORDING
|
|
377
|
+
// On Ruby 4+, we can't call rb_obj_id during on_newobj_event as it mutates the object.
|
|
378
|
+
// Instead, we store the VALUE reference and will get the object_id later via a postponed job.
|
|
379
|
+
// active_deferred_object != Qnil indicates we're in deferred mode.
|
|
380
|
+
heap_recorder->active_deferred_object = new_obj;
|
|
381
|
+
heap_recorder->active_deferred_object_data = object_data;
|
|
382
|
+
#else
|
|
383
|
+
heap_recorder->active_recording = object_record_new(obj_id_or_fail(new_obj), NULL, object_data);
|
|
384
|
+
#endif
|
|
385
|
+
|
|
386
|
+
return needs_after_allocation;
|
|
338
387
|
}
|
|
339
388
|
|
|
340
389
|
// end_heap_allocation_recording_with_rb_protect gets called while the stack_recorder is holding one of the profile
|
|
@@ -351,7 +400,6 @@ int end_heap_allocation_recording_with_rb_protect(heap_recorder *heap_recorder,
|
|
|
351
400
|
return 0;
|
|
352
401
|
}
|
|
353
402
|
|
|
354
|
-
|
|
355
403
|
int exception_state;
|
|
356
404
|
end_heap_allocation_args args = {
|
|
357
405
|
.heap_recorder = heap_recorder,
|
|
@@ -367,26 +415,48 @@ static VALUE end_heap_allocation_recording(VALUE protect_args) {
|
|
|
367
415
|
heap_recorder *heap_recorder = args->heap_recorder;
|
|
368
416
|
ddog_prof_Slice_Location locations = args->locations;
|
|
369
417
|
|
|
370
|
-
|
|
418
|
+
#ifdef USE_DEFERRED_HEAP_ALLOCATION_RECORDING
|
|
419
|
+
if (heap_recorder->active_deferred_object == Qnil) {
|
|
420
|
+
// Recording ended without having been started?
|
|
421
|
+
raise_error(rb_eRuntimeError, "Ended a heap recording that was not started");
|
|
422
|
+
}
|
|
423
|
+
#else
|
|
424
|
+
object_record *active_recording = heap_recorder->active_recording;
|
|
371
425
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
426
|
+
if (active_recording == NULL) {
|
|
427
|
+
// Recording ended without having been started?
|
|
428
|
+
raise_error(rb_eRuntimeError, "Ended a heap recording that was not started");
|
|
429
|
+
}
|
|
430
|
+
// From now on, mark the global active recording as invalid so we can short-circuit at any point
|
|
431
|
+
// and not end up with a still active recording. the local active_recording still holds the
|
|
432
|
+
// data required for committing though.
|
|
433
|
+
heap_recorder->active_recording = NULL;
|
|
380
434
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
435
|
+
if (active_recording == &SKIPPED_RECORD) {
|
|
436
|
+
raise_error(
|
|
437
|
+
rb_eRuntimeError,
|
|
438
|
+
"BUG: end_heap_allocation_recording should never observe SKIPPED_RECORDING because " \
|
|
439
|
+
"end_heap_allocation_recording_with_rb_protect is supposed to test for it directly"
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
#endif
|
|
385
443
|
|
|
386
444
|
heap_record *heap_record = get_or_create_heap_record(heap_recorder, locations);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
445
|
+
inc_tracked_objects_or_fail(heap_record);
|
|
446
|
+
|
|
447
|
+
#ifdef USE_DEFERRED_HEAP_ALLOCATION_RECORDING
|
|
448
|
+
// Commit is delayed, so we need to record all we'll need for it
|
|
449
|
+
pending_recording *pending = &heap_recorder->pending_recordings[heap_recorder->pending_recordings_count++];
|
|
450
|
+
pending->object_ref = heap_recorder->active_deferred_object;
|
|
451
|
+
pending->heap_record = heap_record;
|
|
452
|
+
pending->object_data = heap_recorder->active_deferred_object_data;
|
|
453
|
+
|
|
454
|
+
heap_recorder->active_deferred_object = Qnil;
|
|
455
|
+
heap_recorder->active_deferred_object_data = (live_object_data) {0};
|
|
456
|
+
#else
|
|
457
|
+
// And then commit the new allocation
|
|
458
|
+
commit_recording(heap_recorder, heap_record, active_recording);
|
|
459
|
+
#endif
|
|
390
460
|
|
|
391
461
|
return Qnil;
|
|
392
462
|
}
|
|
@@ -399,6 +469,48 @@ void heap_recorder_update_young_objects(heap_recorder *heap_recorder) {
|
|
|
399
469
|
heap_recorder_update(heap_recorder, /* full_update: */ false);
|
|
400
470
|
}
|
|
401
471
|
|
|
472
|
+
void heap_recorder_finalize_pending_recordings(heap_recorder *heap_recorder) {
|
|
473
|
+
if (heap_recorder == NULL) {
|
|
474
|
+
return; // Nothing to do
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
uint count = heap_recorder->pending_recordings_count;
|
|
478
|
+
if (count == 0) {
|
|
479
|
+
return; // Nothing to do
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
heap_recorder->stats_lifetime.deferred_recordings_finalized += count;
|
|
483
|
+
|
|
484
|
+
for (uint i = 0; i < count; i++) {
|
|
485
|
+
pending_recording *pending = &heap_recorder->pending_recordings[i];
|
|
486
|
+
|
|
487
|
+
// This is the step we couldn't do during the original sample call -- we're now expected to be called in a context
|
|
488
|
+
// where it's finally safe to call this
|
|
489
|
+
long obj_id = obj_id_or_fail(pending->object_ref);
|
|
490
|
+
|
|
491
|
+
// Create the object record now that we have the object_id
|
|
492
|
+
object_record *record = object_record_new(obj_id, pending->heap_record, pending->object_data);
|
|
493
|
+
|
|
494
|
+
commit_recording(heap_recorder, pending->heap_record, record);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
heap_recorder->pending_recordings_count = 0;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Mark pending recordings to prevent GC from collecting the objects
|
|
501
|
+
// while they're waiting to be finalized
|
|
502
|
+
void heap_recorder_mark_pending_recordings(heap_recorder *heap_recorder) {
|
|
503
|
+
if (heap_recorder == NULL) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
for (uint i = 0; i < heap_recorder->pending_recordings_count; i++) {
|
|
508
|
+
rb_gc_mark(heap_recorder->pending_recordings[i].object_ref);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
rb_gc_mark(heap_recorder->active_deferred_object);
|
|
512
|
+
}
|
|
513
|
+
|
|
402
514
|
// NOTE: This function needs and assumes it gets called with the GVL being held.
|
|
403
515
|
// But importantly **some of the operations inside `st_object_record_update` may cause a thread switch**,
|
|
404
516
|
// so we can't assume a single update happens in a single "atomic" step -- other threads may get some running time
|
|
@@ -547,20 +659,21 @@ bool heap_recorder_for_each_live_object(
|
|
|
547
659
|
|
|
548
660
|
VALUE heap_recorder_state_snapshot(heap_recorder *heap_recorder) {
|
|
549
661
|
VALUE arguments[] = {
|
|
550
|
-
ID2SYM(rb_intern("num_object_records")), /* => */
|
|
551
|
-
ID2SYM(rb_intern("num_heap_records")), /* => */
|
|
662
|
+
ID2SYM(rb_intern("num_object_records")), /* => */ ULONG2NUM(heap_recorder->object_records->num_entries),
|
|
663
|
+
ID2SYM(rb_intern("num_heap_records")), /* => */ ULONG2NUM(heap_recorder->heap_records->num_entries),
|
|
664
|
+
ID2SYM(rb_intern("pending_recordings_count")), /* => */ ULONG2NUM(heap_recorder->pending_recordings_count),
|
|
552
665
|
|
|
553
666
|
// Stats as of last update
|
|
554
|
-
ID2SYM(rb_intern("last_update_objects_alive")), /* => */
|
|
555
|
-
ID2SYM(rb_intern("last_update_objects_dead")), /* => */
|
|
556
|
-
ID2SYM(rb_intern("last_update_objects_skipped")), /* => */
|
|
557
|
-
ID2SYM(rb_intern("last_update_objects_frozen")), /* => */
|
|
667
|
+
ID2SYM(rb_intern("last_update_objects_alive")), /* => */ ULONG2NUM(heap_recorder->stats_last_update.objects_alive),
|
|
668
|
+
ID2SYM(rb_intern("last_update_objects_dead")), /* => */ ULONG2NUM(heap_recorder->stats_last_update.objects_dead),
|
|
669
|
+
ID2SYM(rb_intern("last_update_objects_skipped")), /* => */ ULONG2NUM(heap_recorder->stats_last_update.objects_skipped),
|
|
670
|
+
ID2SYM(rb_intern("last_update_objects_frozen")), /* => */ ULONG2NUM(heap_recorder->stats_last_update.objects_frozen),
|
|
558
671
|
|
|
559
672
|
// Lifetime stats
|
|
560
|
-
ID2SYM(rb_intern("lifetime_updates_successful")), /* => */
|
|
561
|
-
ID2SYM(rb_intern("lifetime_updates_skipped_concurrent")), /* => */
|
|
562
|
-
ID2SYM(rb_intern("lifetime_updates_skipped_gcgen")), /* => */
|
|
563
|
-
ID2SYM(rb_intern("lifetime_updates_skipped_time")), /* => */
|
|
673
|
+
ID2SYM(rb_intern("lifetime_updates_successful")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.updates_successful),
|
|
674
|
+
ID2SYM(rb_intern("lifetime_updates_skipped_concurrent")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.updates_skipped_concurrent),
|
|
675
|
+
ID2SYM(rb_intern("lifetime_updates_skipped_gcgen")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.updates_skipped_gcgen),
|
|
676
|
+
ID2SYM(rb_intern("lifetime_updates_skipped_time")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.updates_skipped_time),
|
|
564
677
|
ID2SYM(rb_intern("lifetime_ewma_young_objects_alive")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_young_objects_alive),
|
|
565
678
|
ID2SYM(rb_intern("lifetime_ewma_young_objects_dead")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_young_objects_dead),
|
|
566
679
|
// Note: Here "young" refers to the young update; objects skipped includes non-young objects
|
|
@@ -568,15 +681,19 @@ VALUE heap_recorder_state_snapshot(heap_recorder *heap_recorder) {
|
|
|
568
681
|
ID2SYM(rb_intern("lifetime_ewma_objects_alive")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_objects_alive),
|
|
569
682
|
ID2SYM(rb_intern("lifetime_ewma_objects_dead")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_objects_dead),
|
|
570
683
|
ID2SYM(rb_intern("lifetime_ewma_objects_skipped")), /* => */ DBL2NUM(heap_recorder->stats_lifetime.ewma_objects_skipped),
|
|
684
|
+
|
|
685
|
+
ID2SYM(rb_intern("lifetime_deferred_recordings_skipped_buffer_full")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.deferred_recordings_skipped_buffer_full),
|
|
686
|
+
ID2SYM(rb_intern("lifetime_deferred_recordings_finalized")), /* => */ ULONG2NUM(heap_recorder->stats_lifetime.deferred_recordings_finalized),
|
|
571
687
|
};
|
|
572
688
|
VALUE hash = rb_hash_new();
|
|
573
689
|
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(hash, arguments[i], arguments[i+1]);
|
|
690
|
+
|
|
574
691
|
return hash;
|
|
575
692
|
}
|
|
576
693
|
|
|
577
694
|
typedef struct {
|
|
578
695
|
heap_recorder *recorder;
|
|
579
|
-
VALUE
|
|
696
|
+
VALUE debug_ary;
|
|
580
697
|
} debug_context;
|
|
581
698
|
|
|
582
699
|
VALUE heap_recorder_testonly_debug(heap_recorder *heap_recorder) {
|
|
@@ -584,13 +701,14 @@ VALUE heap_recorder_testonly_debug(heap_recorder *heap_recorder) {
|
|
|
584
701
|
raise_error(rb_eArgError, "heap_recorder is NULL");
|
|
585
702
|
}
|
|
586
703
|
|
|
587
|
-
VALUE
|
|
588
|
-
debug_context context = (debug_context) {.recorder = heap_recorder, .
|
|
704
|
+
VALUE debug_ary = rb_ary_new();
|
|
705
|
+
debug_context context = (debug_context) {.recorder = heap_recorder, .debug_ary = debug_ary};
|
|
589
706
|
st_foreach(heap_recorder->object_records, st_object_records_debug, (st_data_t) &context);
|
|
590
707
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
708
|
+
return rb_ary_new_from_args(2,
|
|
709
|
+
rb_ary_new_from_args(2, ID2SYM(rb_intern("records")), debug_ary),
|
|
710
|
+
rb_ary_new_from_args(2, ID2SYM(rb_intern("state")), heap_recorder_state_snapshot(heap_recorder))
|
|
711
|
+
);
|
|
594
712
|
}
|
|
595
713
|
|
|
596
714
|
// ==========================
|
|
@@ -710,11 +828,10 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
|
|
|
710
828
|
|
|
711
829
|
static int st_object_records_debug(DDTRACE_UNUSED st_data_t key, st_data_t value, st_data_t extra) {
|
|
712
830
|
debug_context *context = (debug_context*) extra;
|
|
713
|
-
VALUE debug_str = context->debug_str;
|
|
714
831
|
|
|
715
832
|
object_record *record = (object_record*) value;
|
|
716
833
|
|
|
717
|
-
|
|
834
|
+
rb_ary_push(context->debug_ary, object_record_inspect(context->recorder, record));
|
|
718
835
|
|
|
719
836
|
return ST_CONTINUE;
|
|
720
837
|
}
|
|
@@ -728,14 +845,17 @@ static int update_object_record_entry(DDTRACE_UNUSED st_data_t *key, st_data_t *
|
|
|
728
845
|
return ST_CONTINUE;
|
|
729
846
|
}
|
|
730
847
|
|
|
731
|
-
static void
|
|
732
|
-
// Link the object record with the corresponding heap record. This was the last remaining thing we
|
|
733
|
-
// needed to fully build the object_record.
|
|
734
|
-
active_recording->heap_record = heap_record;
|
|
848
|
+
static void inc_tracked_objects_or_fail(heap_record *heap_record) {
|
|
735
849
|
if (heap_record->num_tracked_objects == UINT32_MAX) {
|
|
736
850
|
raise_error(rb_eRuntimeError, "Reached maximum number of tracked objects for heap record");
|
|
737
851
|
}
|
|
738
852
|
heap_record->num_tracked_objects++;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
static void commit_recording(heap_recorder *heap_recorder, heap_record *heap_record, object_record *active_recording) {
|
|
856
|
+
// Link the object record with the corresponding heap record. This was the last remaining thing we
|
|
857
|
+
// needed to fully build the object_record.
|
|
858
|
+
active_recording->heap_record = heap_record;
|
|
739
859
|
|
|
740
860
|
int existing_error = st_update(heap_recorder->object_records, active_recording->obj_id, update_object_record_entry, (st_data_t) active_recording);
|
|
741
861
|
if (existing_error) {
|
|
@@ -955,6 +1075,17 @@ static VALUE get_ruby_string_or_raise(heap_recorder *recorder, ddog_prof_Managed
|
|
|
955
1075
|
return ruby_string;
|
|
956
1076
|
}
|
|
957
1077
|
|
|
1078
|
+
static long obj_id_or_fail(VALUE obj) {
|
|
1079
|
+
VALUE ruby_obj_id = rb_obj_id(obj);
|
|
1080
|
+
if (!FIXNUM_P(ruby_obj_id)) {
|
|
1081
|
+
// Bignum object ids indicate the fixnum range is exhausted - all future IDs will also be bignums.
|
|
1082
|
+
// Heap profiling cannot continue.
|
|
1083
|
+
raise_error(rb_eRuntimeError, "Heap profiling: bignum object id detected. Heap profiling cannot continue.");
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
return FIX2LONG(ruby_obj_id);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
958
1089
|
static inline double ewma_stat(double previous, double current) {
|
|
959
1090
|
double alpha = 0.3;
|
|
960
1091
|
return (1 - alpha) * previous + alpha * current;
|
|
@@ -105,7 +105,9 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder);
|
|
|
105
105
|
// The sampling weight of this object.
|
|
106
106
|
//
|
|
107
107
|
// WARN: It needs to be paired with a ::end_heap_allocation_recording call.
|
|
108
|
-
|
|
108
|
+
// Returns needs_after_allocation: true whenever the pending_recordings buffer goes from empty to non-empty and thus
|
|
109
|
+
// a after_sample callback is required to flush it
|
|
110
|
+
bool start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice alloc_class);
|
|
109
111
|
|
|
110
112
|
// End a previously started heap allocation recording on the heap recorder.
|
|
111
113
|
//
|
|
@@ -123,6 +125,15 @@ int end_heap_allocation_recording_with_rb_protect(heap_recorder *heap_recorder,
|
|
|
123
125
|
// these objects quicker) and hopefully reduces tail latency (because there's less objects at serialization time to check).
|
|
124
126
|
void heap_recorder_update_young_objects(heap_recorder *heap_recorder);
|
|
125
127
|
|
|
128
|
+
// Finalize any pending heap allocation recordings by getting their object IDs.
|
|
129
|
+
// This should be called via a postponed job, after the on_newobj_event has completed.
|
|
130
|
+
// Raises an exception if a fatal error occurs (e.g., bignum object ID detected).
|
|
131
|
+
void heap_recorder_finalize_pending_recordings(heap_recorder *heap_recorder);
|
|
132
|
+
|
|
133
|
+
// Mark pending recordings to prevent GC from collecting the objects
|
|
134
|
+
// while they're waiting for the recordings to be finalized.
|
|
135
|
+
void heap_recorder_mark_pending_recordings(heap_recorder *heap_recorder);
|
|
136
|
+
|
|
126
137
|
// Update the heap recorder to reflect the latest state of the VM and prepare internal structures
|
|
127
138
|
// for efficient iteration.
|
|
128
139
|
//
|
|
@@ -16,7 +16,11 @@ static VALUE library_version_string = Qnil;
|
|
|
16
16
|
|
|
17
17
|
typedef struct {
|
|
18
18
|
ddog_prof_ProfileExporter *exporter;
|
|
19
|
-
|
|
19
|
+
ddog_prof_EncodedProfile *profile;
|
|
20
|
+
ddog_prof_Exporter_Slice_File files_to_compress_and_export;
|
|
21
|
+
ddog_CharSlice internal_metadata;
|
|
22
|
+
ddog_CharSlice info;
|
|
23
|
+
ddog_CharSlice *process_tags;
|
|
20
24
|
ddog_CancellationToken *cancel_token;
|
|
21
25
|
ddog_prof_Result_HttpStatus result;
|
|
22
26
|
bool send_ran;
|
|
@@ -29,7 +33,6 @@ static VALUE handle_exporter_failure(ddog_prof_ProfileExporter_Result exporter_r
|
|
|
29
33
|
static VALUE _native_do_export(
|
|
30
34
|
VALUE self,
|
|
31
35
|
VALUE exporter_configuration,
|
|
32
|
-
VALUE upload_timeout_milliseconds,
|
|
33
36
|
VALUE flush
|
|
34
37
|
);
|
|
35
38
|
static void *call_exporter_without_gvl(void *call_args);
|
|
@@ -39,7 +42,7 @@ void http_transport_init(VALUE profiling_module) {
|
|
|
39
42
|
VALUE http_transport_class = rb_define_class_under(profiling_module, "HttpTransport", rb_cObject);
|
|
40
43
|
|
|
41
44
|
rb_define_singleton_method(http_transport_class, "_native_validate_exporter", _native_validate_exporter, 1);
|
|
42
|
-
rb_define_singleton_method(http_transport_class, "_native_do_export", _native_do_export,
|
|
45
|
+
rb_define_singleton_method(http_transport_class, "_native_do_export", _native_do_export, 2);
|
|
43
46
|
|
|
44
47
|
ok_symbol = ID2SYM(rb_intern_const("ok"));
|
|
45
48
|
error_symbol = ID2SYM(rb_intern_const("error"));
|
|
@@ -75,15 +78,31 @@ static ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration) {
|
|
|
75
78
|
ENFORCE_TYPE(exporter_working_mode, T_SYMBOL);
|
|
76
79
|
ID working_mode = SYM2ID(exporter_working_mode);
|
|
77
80
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
VALUE timeout_milliseconds_value = rb_ary_entry(exporter_configuration, 1);
|
|
82
|
+
ENFORCE_TYPE(timeout_milliseconds_value, T_FIXNUM);
|
|
83
|
+
uint64_t timeout_milliseconds = NUM2ULONG(timeout_milliseconds_value);
|
|
84
|
+
|
|
85
|
+
VALUE use_system_dns = rb_ary_entry(exporter_configuration, 2);
|
|
86
|
+
ENFORCE_BOOLEAN(use_system_dns);
|
|
81
87
|
|
|
82
|
-
|
|
88
|
+
if (working_mode == rb_intern("agentless")) {
|
|
89
|
+
VALUE site = rb_ary_entry(exporter_configuration, 3);
|
|
90
|
+
VALUE api_key = rb_ary_entry(exporter_configuration, 4);
|
|
91
|
+
|
|
92
|
+
return ddog_prof_Endpoint_agentless(
|
|
93
|
+
char_slice_from_ruby_string(site),
|
|
94
|
+
char_slice_from_ruby_string(api_key),
|
|
95
|
+
timeout_milliseconds,
|
|
96
|
+
use_system_dns == Qtrue
|
|
97
|
+
);
|
|
83
98
|
} else if (working_mode == rb_intern("agent")) {
|
|
84
|
-
VALUE base_url = rb_ary_entry(exporter_configuration,
|
|
99
|
+
VALUE base_url = rb_ary_entry(exporter_configuration, 3);
|
|
85
100
|
|
|
86
|
-
return ddog_prof_Endpoint_agent(
|
|
101
|
+
return ddog_prof_Endpoint_agent(
|
|
102
|
+
char_slice_from_ruby_string(base_url),
|
|
103
|
+
timeout_milliseconds,
|
|
104
|
+
use_system_dns == Qtrue
|
|
105
|
+
);
|
|
87
106
|
} else {
|
|
88
107
|
raise_error(rb_eArgError, "Failed to initialize transport: Unexpected working mode, expected :agentless or :agent");
|
|
89
108
|
}
|
|
@@ -123,41 +142,18 @@ static VALUE handle_exporter_failure(ddog_prof_ProfileExporter_Result exporter_r
|
|
|
123
142
|
|
|
124
143
|
// Note: This function handles a bunch of libdatadog dynamically-allocated objects, so it MUST not use any Ruby APIs
|
|
125
144
|
// which can raise exceptions, otherwise the objects will be leaked.
|
|
126
|
-
static VALUE perform_export(
|
|
127
|
-
ddog_prof_ProfileExporter *exporter,
|
|
128
|
-
ddog_prof_EncodedProfile *profile,
|
|
129
|
-
ddog_prof_Exporter_Slice_File files_to_compress_and_export,
|
|
130
|
-
ddog_CharSlice internal_metadata,
|
|
131
|
-
ddog_CharSlice info,
|
|
132
|
-
ddog_CharSlice *process_tags
|
|
133
|
-
) {
|
|
134
|
-
ddog_prof_Request_Result build_result = ddog_prof_Exporter_Request_build(
|
|
135
|
-
exporter,
|
|
136
|
-
profile,
|
|
137
|
-
files_to_compress_and_export,
|
|
138
|
-
/* files_to_export_unmodified: */ ddog_prof_Exporter_Slice_File_empty(),
|
|
139
|
-
/* optional_additional_tags: */ NULL,
|
|
140
|
-
/* optional_process_tags: */ process_tags,
|
|
141
|
-
&internal_metadata,
|
|
142
|
-
&info
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
if (build_result.tag == DDOG_PROF_REQUEST_RESULT_ERR_HANDLE_REQUEST) {
|
|
146
|
-
ddog_prof_Exporter_drop(exporter);
|
|
147
|
-
return rb_ary_new_from_args(2, error_symbol, get_error_details_and_drop(&build_result.err));
|
|
148
|
-
}
|
|
149
|
-
|
|
145
|
+
static VALUE perform_export(call_exporter_without_gvl_arguments args) {
|
|
150
146
|
ddog_CancellationToken cancel_token_request = ddog_CancellationToken_new();
|
|
151
147
|
ddog_CancellationToken cancel_token_interrupt = ddog_CancellationToken_clone(&cancel_token_request);
|
|
152
148
|
|
|
153
149
|
validate_token(cancel_token_request, __FILE__, __LINE__);
|
|
154
150
|
validate_token(cancel_token_interrupt, __FILE__, __LINE__);
|
|
155
151
|
|
|
152
|
+
args.cancel_token = &cancel_token_request;
|
|
153
|
+
args.send_ran = false;
|
|
154
|
+
|
|
156
155
|
// We'll release the Global VM Lock while we're calling send, so that the Ruby VM can continue to work while this
|
|
157
156
|
// is pending
|
|
158
|
-
call_exporter_without_gvl_arguments args =
|
|
159
|
-
{.exporter = exporter, .build_result = &build_result, .cancel_token = &cancel_token_request, .send_ran = false};
|
|
160
|
-
|
|
161
157
|
// We use rb_thread_call_without_gvl2 instead of rb_thread_call_without_gvl as the gvl2 variant never raises any
|
|
162
158
|
// exceptions.
|
|
163
159
|
//
|
|
@@ -182,18 +178,13 @@ static VALUE perform_export(
|
|
|
182
178
|
// Cleanup exporter and token, no longer needed
|
|
183
179
|
ddog_CancellationToken_drop(&cancel_token_request);
|
|
184
180
|
ddog_CancellationToken_drop(&cancel_token_interrupt);
|
|
185
|
-
ddog_prof_Exporter_drop(exporter);
|
|
181
|
+
ddog_prof_Exporter_drop(args.exporter);
|
|
186
182
|
|
|
187
183
|
if (pending_exception) {
|
|
188
|
-
// If we got here send did not run, so we need to explicitly dispose of the request
|
|
189
|
-
ddog_prof_Exporter_Request_drop(&build_result.ok);
|
|
190
|
-
|
|
191
184
|
// Let Ruby propagate the exception. This will not return.
|
|
192
185
|
rb_jump_tag(pending_exception);
|
|
193
186
|
}
|
|
194
187
|
|
|
195
|
-
// The request itself does not need to be freed as libdatadog takes ownership of it as part of sending.
|
|
196
|
-
|
|
197
188
|
ddog_prof_Result_HttpStatus result = args.result;
|
|
198
189
|
|
|
199
190
|
return result.tag == DDOG_PROF_RESULT_HTTP_STATUS_OK_HTTP_STATUS ?
|
|
@@ -204,7 +195,6 @@ static VALUE perform_export(
|
|
|
204
195
|
static VALUE _native_do_export(
|
|
205
196
|
DDTRACE_UNUSED VALUE _self,
|
|
206
197
|
VALUE exporter_configuration,
|
|
207
|
-
VALUE upload_timeout_milliseconds,
|
|
208
198
|
VALUE flush
|
|
209
199
|
) {
|
|
210
200
|
VALUE encoded_profile = rb_funcall(flush, rb_intern("encoded_profile"), 0);
|
|
@@ -215,7 +205,6 @@ static VALUE _native_do_export(
|
|
|
215
205
|
VALUE info_json = rb_funcall(flush, rb_intern("info_json"), 0);
|
|
216
206
|
VALUE process_tags = rb_funcall(flush, rb_intern("process_tags"), 0);
|
|
217
207
|
|
|
218
|
-
ENFORCE_TYPE(upload_timeout_milliseconds, T_FIXNUM);
|
|
219
208
|
enforce_encoded_profile_instance(encoded_profile);
|
|
220
209
|
ENFORCE_TYPE(code_provenance_file_name, T_STRING);
|
|
221
210
|
ENFORCE_TYPE(tags_as_array, T_ARRAY);
|
|
@@ -227,8 +216,6 @@ static VALUE _native_do_export(
|
|
|
227
216
|
bool have_code_provenance = !NIL_P(code_provenance_data);
|
|
228
217
|
if (have_code_provenance) ENFORCE_TYPE(code_provenance_data, T_STRING);
|
|
229
218
|
|
|
230
|
-
uint64_t timeout_milliseconds = NUM2ULONG(upload_timeout_milliseconds);
|
|
231
|
-
|
|
232
219
|
int to_compress_length = have_code_provenance ? 1 : 0;
|
|
233
220
|
ddog_prof_Exporter_File to_compress[to_compress_length];
|
|
234
221
|
ddog_prof_Exporter_Slice_File files_to_compress_and_export = {.ptr = to_compress, .len = to_compress_length};
|
|
@@ -250,29 +237,29 @@ static VALUE _native_do_export(
|
|
|
250
237
|
VALUE failure_tuple = handle_exporter_failure(exporter_result);
|
|
251
238
|
if (!NIL_P(failure_tuple)) return failure_tuple;
|
|
252
239
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return perform_export(
|
|
263
|
-
&exporter_result.ok,
|
|
264
|
-
to_ddog_prof_EncodedProfile(encoded_profile),
|
|
265
|
-
files_to_compress_and_export,
|
|
266
|
-
internal_metadata,
|
|
267
|
-
info,
|
|
268
|
-
&process_tags_slice
|
|
269
|
-
);
|
|
240
|
+
return perform_export((call_exporter_without_gvl_arguments) {
|
|
241
|
+
.exporter = &exporter_result.ok,
|
|
242
|
+
.profile = to_ddog_prof_EncodedProfile(encoded_profile),
|
|
243
|
+
.files_to_compress_and_export = files_to_compress_and_export,
|
|
244
|
+
.internal_metadata = internal_metadata,
|
|
245
|
+
.info = info,
|
|
246
|
+
.process_tags = &process_tags_slice
|
|
247
|
+
});
|
|
270
248
|
}
|
|
271
249
|
|
|
272
250
|
static void *call_exporter_without_gvl(void *call_args) {
|
|
273
251
|
call_exporter_without_gvl_arguments *args = (call_exporter_without_gvl_arguments*) call_args;
|
|
274
252
|
|
|
275
|
-
args->result =
|
|
253
|
+
args->result = ddog_prof_Exporter_send_blocking(
|
|
254
|
+
args->exporter,
|
|
255
|
+
args->profile,
|
|
256
|
+
args->files_to_compress_and_export,
|
|
257
|
+
/* optional_additional_tags: */ NULL,
|
|
258
|
+
/* optional_process_tags: */ args->process_tags,
|
|
259
|
+
&args->internal_metadata,
|
|
260
|
+
&args->info,
|
|
261
|
+
args->cancel_token
|
|
262
|
+
);
|
|
276
263
|
args->send_ran = true;
|
|
277
264
|
|
|
278
265
|
return NULL; // Unused
|
|
@@ -104,19 +104,6 @@ module Datadog
|
|
|
104
104
|
suggested: CONTACT_SUPPORT,
|
|
105
105
|
)
|
|
106
106
|
|
|
107
|
-
# Validation for this check is done in extconf.rb because it relies on mkmf
|
|
108
|
-
PKG_CONFIG_IS_MISSING = explain_issue(
|
|
109
|
-
# ----------------------------------------------------------------------------+
|
|
110
|
-
"the `pkg-config` system tool is missing.",
|
|
111
|
-
"This issue can usually be fixed by installing one of the following:",
|
|
112
|
-
"the `pkg-config` package on Homebrew and Debian/Ubuntu-based Linux;",
|
|
113
|
-
"the `pkgconf` package on Arch and Alpine-based Linux;",
|
|
114
|
-
"the `pkgconf-pkg-config` package on Fedora/Red Hat-based Linux.",
|
|
115
|
-
"(Tip: When fixing this, ensure `pkg-config` is installed **before**",
|
|
116
|
-
"running `bundle install`, and remember to clear any installed gems cache).",
|
|
117
|
-
suggested: CONTACT_SUPPORT,
|
|
118
|
-
)
|
|
119
|
-
|
|
120
107
|
# Validation for this check is done in extconf.rb because it relies on mkmf
|
|
121
108
|
COMPILER_ATOMIC_MISSING = explain_issue(
|
|
122
109
|
"your C compiler is missing support for the <stdatomic.h> header.",
|
|
@@ -25,6 +25,7 @@ void encoded_profile_init(VALUE profiling_module);
|
|
|
25
25
|
void http_transport_init(VALUE profiling_module);
|
|
26
26
|
void stack_recorder_init(VALUE profiling_module);
|
|
27
27
|
void crashtracking_runtime_stacks_init(void);
|
|
28
|
+
void setup_signal_handler_init(VALUE profiling_module);
|
|
28
29
|
|
|
29
30
|
static VALUE native_working_p(VALUE self);
|
|
30
31
|
static VALUE _native_grab_gvl_and_raise(DDTRACE_UNUSED VALUE _self, VALUE exception_class, VALUE test_message, VALUE test_message_arg, VALUE release_gvl);
|
|
@@ -62,6 +63,7 @@ void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
|
|
|
62
63
|
rb_funcall(native_extension_module, rb_intern("private_class_method"), 1, ID2SYM(rb_intern("native_working?")));
|
|
63
64
|
|
|
64
65
|
ruby_helpers_init();
|
|
66
|
+
setup_signal_handler_init(profiling_module);
|
|
65
67
|
collectors_cpu_and_wall_time_worker_init(profiling_module);
|
|
66
68
|
collectors_discrete_dynamic_sampler_init(profiling_module);
|
|
67
69
|
collectors_dynamic_sampling_rate_init(profiling_module);
|
|
@@ -251,7 +253,7 @@ static VALUE _native_trigger_holding_the_gvl_signal_handler_on(DDTRACE_UNUSED VA
|
|
|
251
253
|
|
|
252
254
|
ENFORCE_SUCCESS_GVL(pthread_mutex_unlock(&holding_the_gvl_signal_handler_mutex));
|
|
253
255
|
|
|
254
|
-
replace_sigprof_signal_handler_with_empty_handler(holding_the_gvl_signal_handler);
|
|
256
|
+
replace_sigprof_signal_handler_with_empty_handler(holding_the_gvl_signal_handler, true);
|
|
255
257
|
|
|
256
258
|
if (holding_the_gvl_signal_handler_result[0] == Qfalse) raise_error(rb_eRuntimeError, "Could not signal background_thread");
|
|
257
259
|
|