datadog 2.16.0 → 2.18.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 +72 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +12 -46
- data/ext/datadog_profiling_native_extension/collectors_stack.c +227 -49
- data/ext/datadog_profiling_native_extension/collectors_stack.h +19 -3
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +63 -12
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
- data/ext/datadog_profiling_native_extension/encoded_profile.c +22 -12
- data/ext/datadog_profiling_native_extension/encoded_profile.h +1 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +7 -0
- data/ext/datadog_profiling_native_extension/heap_recorder.c +239 -363
- data/ext/datadog_profiling_native_extension/heap_recorder.h +4 -6
- data/ext/datadog_profiling_native_extension/http_transport.c +45 -72
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +22 -0
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +8 -5
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +1 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +6 -3
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +1 -13
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +2 -10
- data/ext/datadog_profiling_native_extension/stack_recorder.c +156 -60
- data/ext/libdatadog_api/crashtracker.c +10 -3
- data/ext/libdatadog_api/extconf.rb +2 -2
- data/ext/libdatadog_api/library_config.c +54 -12
- data/ext/libdatadog_api/library_config.h +6 -0
- data/ext/libdatadog_api/macos_development.md +3 -3
- data/ext/libdatadog_api/process_discovery.c +2 -7
- data/ext/libdatadog_extconf_helpers.rb +2 -2
- data/lib/datadog/appsec/api_security/lru_cache.rb +56 -0
- data/lib/datadog/appsec/api_security/route_extractor.rb +65 -0
- data/lib/datadog/appsec/api_security/sampler.rb +59 -0
- data/lib/datadog/appsec/api_security.rb +23 -0
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +257 -85
- data/lib/datadog/appsec/assets/waf_rules/strict.json +10 -78
- data/lib/datadog/appsec/component.rb +30 -54
- data/lib/datadog/appsec/configuration/settings.rb +60 -2
- data/lib/datadog/appsec/context.rb +6 -6
- data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +27 -16
- data/lib/datadog/appsec/processor/rule_loader.rb +5 -6
- data/lib/datadog/appsec/remote.rb +15 -55
- data/lib/datadog/appsec/security_engine/engine.rb +194 -0
- data/lib/datadog/appsec/security_engine/runner.rb +10 -11
- data/lib/datadog/appsec.rb +4 -7
- data/lib/datadog/core/buffer/random.rb +18 -2
- data/lib/datadog/core/configuration/agent_settings.rb +52 -0
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +4 -46
- data/lib/datadog/core/configuration/components.rb +31 -24
- data/lib/datadog/core/configuration/components_state.rb +23 -0
- data/lib/datadog/core/configuration/option.rb +27 -27
- data/lib/datadog/core/configuration/option_definition.rb +4 -4
- data/lib/datadog/core/configuration/options.rb +1 -1
- data/lib/datadog/core/configuration/settings.rb +32 -20
- data/lib/datadog/core/configuration/stable_config.rb +1 -2
- data/lib/datadog/core/configuration.rb +16 -16
- data/lib/datadog/core/crashtracking/component.rb +2 -1
- data/lib/datadog/core/crashtracking/tag_builder.rb +4 -22
- data/lib/datadog/core/encoding.rb +1 -1
- data/lib/datadog/core/environment/cgroup.rb +10 -12
- data/lib/datadog/core/environment/container.rb +38 -40
- data/lib/datadog/core/environment/ext.rb +6 -6
- data/lib/datadog/core/environment/identity.rb +3 -3
- data/lib/datadog/core/environment/platform.rb +3 -3
- data/lib/datadog/core/error.rb +11 -9
- data/lib/datadog/core/logger.rb +2 -2
- data/lib/datadog/core/metrics/client.rb +12 -14
- data/lib/datadog/core/metrics/logging.rb +5 -5
- data/lib/datadog/core/process_discovery/tracer_memfd.rb +15 -0
- data/lib/datadog/core/process_discovery.rb +5 -1
- data/lib/datadog/core/rate_limiter.rb +4 -2
- data/lib/datadog/core/remote/client.rb +32 -31
- data/lib/datadog/core/remote/component.rb +3 -3
- data/lib/datadog/core/remote/configuration/digest.rb +7 -7
- data/lib/datadog/core/remote/configuration/path.rb +1 -1
- data/lib/datadog/core/remote/configuration/repository.rb +12 -0
- data/lib/datadog/core/remote/transport/http/client.rb +1 -1
- data/lib/datadog/core/remote/transport/http/config.rb +21 -5
- data/lib/datadog/core/remote/transport/http/negotiation.rb +1 -1
- data/lib/datadog/core/runtime/metrics.rb +3 -3
- data/lib/datadog/core/tag_builder.rb +56 -0
- data/lib/datadog/core/telemetry/component.rb +39 -24
- data/lib/datadog/core/telemetry/emitter.rb +7 -1
- data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +66 -0
- data/lib/datadog/core/telemetry/event/app_closing.rb +18 -0
- data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +33 -0
- data/lib/datadog/core/telemetry/event/app_heartbeat.rb +18 -0
- data/lib/datadog/core/telemetry/event/app_integrations_change.rb +58 -0
- data/lib/datadog/core/telemetry/event/app_started.rb +269 -0
- data/lib/datadog/core/telemetry/event/base.rb +40 -0
- data/lib/datadog/core/telemetry/event/distributions.rb +18 -0
- data/lib/datadog/core/telemetry/event/generate_metrics.rb +43 -0
- data/lib/datadog/core/telemetry/event/log.rb +76 -0
- data/lib/datadog/core/telemetry/event/message_batch.rb +42 -0
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +43 -0
- data/lib/datadog/core/telemetry/event.rb +17 -475
- data/lib/datadog/core/telemetry/logger.rb +5 -4
- data/lib/datadog/core/telemetry/logging.rb +11 -5
- data/lib/datadog/core/telemetry/metric.rb +3 -3
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +2 -2
- data/lib/datadog/core/telemetry/transport/telemetry.rb +0 -1
- data/lib/datadog/core/telemetry/worker.rb +48 -27
- data/lib/datadog/core/transport/http/adapters/net.rb +17 -2
- data/lib/datadog/core/transport/http/adapters/test.rb +2 -1
- data/lib/datadog/core/transport/http/builder.rb +14 -14
- data/lib/datadog/core/transport/http/env.rb +8 -0
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +6 -6
- data/lib/datadog/core/utils/duration.rb +32 -32
- data/lib/datadog/core/utils/forking.rb +2 -2
- data/lib/datadog/core/utils/network.rb +6 -6
- data/lib/datadog/core/utils/only_once_successful.rb +16 -5
- data/lib/datadog/core/utils/time.rb +10 -2
- data/lib/datadog/core/utils/truncation.rb +21 -0
- data/lib/datadog/core/utils.rb +7 -0
- data/lib/datadog/core/vendor/multipart-post/multipart/post/composite_read_io.rb +1 -1
- data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +8 -8
- data/lib/datadog/core/vendor/multipart-post/multipart/post/parts.rb +7 -7
- data/lib/datadog/core/worker.rb +1 -1
- data/lib/datadog/core/workers/async.rb +9 -10
- data/lib/datadog/di/instrumenter.rb +52 -2
- data/lib/datadog/di/probe_notification_builder.rb +31 -41
- data/lib/datadog/di/probe_notifier_worker.rb +9 -1
- data/lib/datadog/di/serializer.rb +6 -2
- data/lib/datadog/di/transport/http/input.rb +10 -0
- data/lib/datadog/di/transport/input.rb +10 -2
- data/lib/datadog/error_tracking/component.rb +2 -2
- data/lib/datadog/profiling/collectors/code_provenance.rb +18 -9
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +4 -0
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -0
- data/lib/datadog/profiling/collectors/thread_context.rb +16 -1
- data/lib/datadog/profiling/component.rb +7 -9
- data/lib/datadog/profiling/ext.rb +0 -13
- data/lib/datadog/profiling/flush.rb +1 -1
- data/lib/datadog/profiling/http_transport.rb +3 -8
- data/lib/datadog/profiling/profiler.rb +2 -0
- data/lib/datadog/profiling/scheduler.rb +10 -2
- data/lib/datadog/profiling/stack_recorder.rb +5 -5
- data/lib/datadog/profiling/tag_builder.rb +5 -41
- data/lib/datadog/profiling/tasks/setup.rb +2 -0
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +15 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +19 -12
- data/lib/datadog/tracing/contrib/action_pack/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +4 -1
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +33 -0
- data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +4 -0
- data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +2 -4
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +10 -0
- data/lib/datadog/tracing/contrib/aws/parsed_context.rb +5 -1
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +1 -5
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +4 -2
- data/lib/datadog/tracing/contrib/patcher.rb +5 -2
- data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +5 -2
- data/lib/datadog/tracing/contrib/support.rb +28 -0
- data/lib/datadog/tracing/metadata/errors.rb +4 -4
- data/lib/datadog/tracing/sync_writer.rb +1 -1
- data/lib/datadog/tracing/trace_operation.rb +12 -4
- data/lib/datadog/tracing/tracer.rb +6 -2
- data/lib/datadog/version.rb +1 -1
- metadata +31 -12
- data/lib/datadog/appsec/assets/waf_rules/processors.json +0 -321
- data/lib/datadog/appsec/assets/waf_rules/scanners.json +0 -1023
- data/lib/datadog/appsec/processor/rule_merger.rb +0 -171
- data/lib/datadog/appsec/processor.rb +0 -107
@@ -2,19 +2,29 @@
|
|
2
2
|
|
3
3
|
#include <datadog/profiling.h>
|
4
4
|
|
5
|
+
#include "private_vm_api_access.h"
|
5
6
|
#include "stack_recorder.h"
|
6
7
|
|
7
8
|
#define MAX_FRAMES_LIMIT 3000
|
8
9
|
#define MAX_FRAMES_LIMIT_AS_STRING "3000"
|
9
10
|
|
10
|
-
|
11
|
+
// Used as scratch space during sampling
|
12
|
+
typedef struct {
|
13
|
+
uint16_t max_frames;
|
14
|
+
ddog_prof_Location *locations;
|
15
|
+
frame_info *stack_buffer;
|
16
|
+
bool pending_sample;
|
17
|
+
int pending_sample_result;
|
18
|
+
} sampling_buffer;
|
11
19
|
|
12
20
|
void sample_thread(
|
13
21
|
VALUE thread,
|
14
22
|
sampling_buffer* buffer,
|
15
23
|
VALUE recorder_instance,
|
16
24
|
sample_values values,
|
17
|
-
sample_labels labels
|
25
|
+
sample_labels labels,
|
26
|
+
bool native_filenames_enabled,
|
27
|
+
st_table *native_filenames_cache
|
18
28
|
);
|
19
29
|
void record_placeholder_stack(
|
20
30
|
VALUE recorder_instance,
|
@@ -22,6 +32,12 @@ void record_placeholder_stack(
|
|
22
32
|
sample_labels labels,
|
23
33
|
ddog_CharSlice placeholder_stack
|
24
34
|
);
|
35
|
+
void prepare_sample_thread(VALUE thread, sampling_buffer *buffer);
|
36
|
+
|
25
37
|
uint16_t sampling_buffer_check_max_frames(int max_frames);
|
26
|
-
sampling_buffer *
|
38
|
+
void sampling_buffer_initialize(sampling_buffer *buffer, uint16_t max_frames, ddog_prof_Location *locations);
|
27
39
|
void sampling_buffer_free(sampling_buffer *buffer);
|
40
|
+
void sampling_buffer_mark(sampling_buffer *buffer);
|
41
|
+
static inline bool sampling_buffer_needs_marking(sampling_buffer *buffer) {
|
42
|
+
return buffer->pending_sample && buffer->pending_sample_result > 0;
|
43
|
+
}
|
@@ -11,6 +11,7 @@
|
|
11
11
|
#include "stack_recorder.h"
|
12
12
|
#include "time_helpers.h"
|
13
13
|
#include "unsafe_api_calls_check.h"
|
14
|
+
#include "extconf.h"
|
14
15
|
|
15
16
|
// Used to trigger sampling of threads, based on external "events", such as:
|
16
17
|
// * periodic timer for cpu-time and wall-time
|
@@ -124,6 +125,7 @@ typedef struct {
|
|
124
125
|
ddog_prof_Location *locations;
|
125
126
|
uint16_t max_frames;
|
126
127
|
// Hashmap <Thread Object, per_thread_context>
|
128
|
+
// Note: Be very careful when mutating this map, as it gets read e.g. in the middle of GC and signal handlers.
|
127
129
|
st_table *hash_map_per_thread_context;
|
128
130
|
// Datadog::Profiling::StackRecorder instance
|
129
131
|
VALUE recorder_instance;
|
@@ -153,6 +155,10 @@ typedef struct {
|
|
153
155
|
// Qtrue serves as a marker we've not yet extracted it; when we try to extract it, we set it to an object if
|
154
156
|
// successful and Qnil if not.
|
155
157
|
VALUE otel_current_span_key;
|
158
|
+
// Used to enable native filenames in stack traces
|
159
|
+
bool native_filenames_enabled;
|
160
|
+
// Used to cache native filename lookup results (Map[void *function_pointer, char *filename])
|
161
|
+
st_table *native_filenames_cache;
|
156
162
|
|
157
163
|
struct stats {
|
158
164
|
// Track how many garbage collection samples we've taken.
|
@@ -172,7 +178,7 @@ typedef struct {
|
|
172
178
|
|
173
179
|
// Tracks per-thread state
|
174
180
|
typedef struct {
|
175
|
-
sampling_buffer
|
181
|
+
sampling_buffer sampling_buffer;
|
176
182
|
char thread_id[THREAD_ID_LIMIT_CHARS];
|
177
183
|
ddog_CharSlice thread_id_char_slice;
|
178
184
|
char thread_invoke_location[THREAD_INVOKE_LOCATION_LIMIT_CHARS];
|
@@ -205,7 +211,7 @@ typedef struct {
|
|
205
211
|
|
206
212
|
static void thread_context_collector_typed_data_mark(void *state_ptr);
|
207
213
|
static void thread_context_collector_typed_data_free(void *state_ptr);
|
208
|
-
static int hash_map_per_thread_context_mark(st_data_t key_thread, st_data_t
|
214
|
+
static int hash_map_per_thread_context_mark(st_data_t key_thread, st_data_t value_thread_context, DDTRACE_UNUSED st_data_t _argument);
|
209
215
|
static int hash_map_per_thread_context_free_values(st_data_t _thread, st_data_t value_per_thread_context, st_data_t _argument);
|
210
216
|
static VALUE _native_new(VALUE klass);
|
211
217
|
static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
|
@@ -298,6 +304,7 @@ static otel_span otel_span_from(VALUE otel_context, VALUE otel_current_span_key)
|
|
298
304
|
static uint64_t otel_span_id_to_uint(VALUE otel_span_id);
|
299
305
|
static VALUE safely_lookup_hash_without_going_into_ruby_code(VALUE hash, VALUE key);
|
300
306
|
static VALUE _native_system_epoch_time_now_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
307
|
+
static VALUE _native_prepare_sample_inside_signal_handler(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
|
301
308
|
|
302
309
|
void collectors_thread_context_init(VALUE profiling_module) {
|
303
310
|
VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
|
@@ -330,6 +337,7 @@ void collectors_thread_context_init(VALUE profiling_module) {
|
|
330
337
|
rb_define_singleton_method(testing_module, "_native_new_empty_thread", _native_new_empty_thread, 0);
|
331
338
|
rb_define_singleton_method(testing_module, "_native_sample_skipped_allocation_samples", _native_sample_skipped_allocation_samples, 2);
|
332
339
|
rb_define_singleton_method(testing_module, "_native_system_epoch_time_now_ns", _native_system_epoch_time_now_ns, 1);
|
340
|
+
rb_define_singleton_method(testing_module, "_native_prepare_sample_inside_signal_handler", _native_prepare_sample_inside_signal_handler, 1);
|
333
341
|
#ifndef NO_GVL_INSTRUMENTATION
|
334
342
|
rb_define_singleton_method(testing_module, "_native_on_gvl_waiting", _native_on_gvl_waiting, 1);
|
335
343
|
rb_define_singleton_method(testing_module, "_native_gvl_waiting_at_for", _native_gvl_waiting_at_for, 1);
|
@@ -405,13 +413,21 @@ static void thread_context_collector_typed_data_free(void *state_ptr) {
|
|
405
413
|
// ...and then the map
|
406
414
|
st_free_table(state->hash_map_per_thread_context);
|
407
415
|
|
416
|
+
st_free_table(state->native_filenames_cache);
|
417
|
+
|
408
418
|
ruby_xfree(state);
|
409
419
|
}
|
410
420
|
|
411
421
|
// Mark Ruby thread references we keep as keys in hash_map_per_thread_context
|
412
|
-
static int hash_map_per_thread_context_mark(st_data_t key_thread,
|
422
|
+
static int hash_map_per_thread_context_mark(st_data_t key_thread, st_data_t value_thread_context, DDTRACE_UNUSED st_data_t _argument) {
|
413
423
|
VALUE thread = (VALUE) key_thread;
|
424
|
+
per_thread_context *thread_context = (per_thread_context *) value_thread_context;
|
425
|
+
|
414
426
|
rb_gc_mark(thread);
|
427
|
+
if (sampling_buffer_needs_marking(&thread_context->sampling_buffer)) {
|
428
|
+
sampling_buffer_mark(&thread_context->sampling_buffer);
|
429
|
+
}
|
430
|
+
|
415
431
|
return ST_CONTINUE;
|
416
432
|
}
|
417
433
|
|
@@ -440,6 +456,8 @@ static VALUE _native_new(VALUE klass) {
|
|
440
456
|
state->thread_list_buffer = thread_list_buffer;
|
441
457
|
state->endpoint_collection_enabled = true;
|
442
458
|
state->timeline_enabled = true;
|
459
|
+
state->native_filenames_enabled = false;
|
460
|
+
state->native_filenames_cache = st_init_numtable();
|
443
461
|
state->otel_context_enabled = OTEL_CONTEXT_ENABLED_FALSE;
|
444
462
|
state->otel_context_source = OTEL_CONTEXT_SOURCE_UNKNOWN;
|
445
463
|
state->time_converter_state = (monotonic_to_system_epoch_state) MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER;
|
@@ -474,11 +492,13 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
474
492
|
VALUE timeline_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("timeline_enabled")));
|
475
493
|
VALUE waiting_for_gvl_threshold_ns = rb_hash_fetch(options, ID2SYM(rb_intern("waiting_for_gvl_threshold_ns")));
|
476
494
|
VALUE otel_context_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("otel_context_enabled")));
|
495
|
+
VALUE native_filenames_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("native_filenames_enabled")));
|
477
496
|
|
478
497
|
ENFORCE_TYPE(max_frames, T_FIXNUM);
|
479
498
|
ENFORCE_BOOLEAN(endpoint_collection_enabled);
|
480
499
|
ENFORCE_BOOLEAN(timeline_enabled);
|
481
500
|
ENFORCE_TYPE(waiting_for_gvl_threshold_ns, T_FIXNUM);
|
501
|
+
ENFORCE_BOOLEAN(native_filenames_enabled);
|
482
502
|
|
483
503
|
thread_context_collector_state *state;
|
484
504
|
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
@@ -490,6 +510,7 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
|
|
490
510
|
state->recorder_instance = enforce_recorder_instance(recorder_instance);
|
491
511
|
state->endpoint_collection_enabled = (endpoint_collection_enabled == Qtrue);
|
492
512
|
state->timeline_enabled = (timeline_enabled == Qtrue);
|
513
|
+
state->native_filenames_enabled = (native_filenames_enabled == Qtrue);
|
493
514
|
if (otel_context_enabled == Qfalse || otel_context_enabled == Qnil) {
|
494
515
|
state->otel_context_enabled = OTEL_CONTEXT_ENABLED_FALSE;
|
495
516
|
} else if (otel_context_enabled == ID2SYM(rb_intern("only"))) {
|
@@ -599,7 +620,7 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
|
|
599
620
|
/* thread_being_sampled: */ thread,
|
600
621
|
/* stack_from_thread: */ thread,
|
601
622
|
thread_context,
|
602
|
-
thread_context->sampling_buffer,
|
623
|
+
&thread_context->sampling_buffer,
|
603
624
|
current_cpu_time_ns,
|
604
625
|
current_monotonic_wall_time_ns
|
605
626
|
);
|
@@ -617,7 +638,7 @@ void thread_context_collector_sample(VALUE self_instance, long current_monotonic
|
|
617
638
|
/* stack_from_thread: */ profiler_overhead_stack_thread,
|
618
639
|
current_thread_context,
|
619
640
|
// Here we use the overhead thread's sampling buffer so as to not invalidate the cache in the buffer of the thread being sampled
|
620
|
-
get_or_create_context_for(profiler_overhead_stack_thread, state)->sampling_buffer,
|
641
|
+
&get_or_create_context_for(profiler_overhead_stack_thread, state)->sampling_buffer,
|
621
642
|
cpu_time_now_ns(current_thread_context),
|
622
643
|
monotonic_wall_time_now_ns(RAISE_ON_FAILURE)
|
623
644
|
);
|
@@ -998,7 +1019,9 @@ static void trigger_sample_for_thread(
|
|
998
1019
|
.state_label = state_label,
|
999
1020
|
.end_timestamp_ns = end_timestamp_ns,
|
1000
1021
|
.is_gvl_waiting_state = is_gvl_waiting_state,
|
1001
|
-
}
|
1022
|
+
},
|
1023
|
+
state->native_filenames_enabled,
|
1024
|
+
state->native_filenames_cache
|
1002
1025
|
);
|
1003
1026
|
}
|
1004
1027
|
|
@@ -1023,7 +1046,7 @@ static per_thread_context *get_or_create_context_for(VALUE thread, thread_contex
|
|
1023
1046
|
if (st_lookup(state->hash_map_per_thread_context, (st_data_t) thread, &value_context)) {
|
1024
1047
|
thread_context = (per_thread_context*) value_context;
|
1025
1048
|
} else {
|
1026
|
-
thread_context =
|
1049
|
+
thread_context = calloc(1, sizeof(per_thread_context)); // See "note on calloc vs ruby_xcalloc use" in heap_recorder.c
|
1027
1050
|
initialize_context(thread, thread_context, state);
|
1028
1051
|
st_insert(state->hash_map_per_thread_context, (st_data_t) thread, (st_data_t) thread_context);
|
1029
1052
|
}
|
@@ -1064,7 +1087,7 @@ static bool is_logging_gem_monkey_patch(VALUE invoke_file_location) {
|
|
1064
1087
|
}
|
1065
1088
|
|
1066
1089
|
static void initialize_context(VALUE thread, per_thread_context *thread_context, thread_context_collector_state *state) {
|
1067
|
-
thread_context->sampling_buffer
|
1090
|
+
sampling_buffer_initialize(&thread_context->sampling_buffer, state->max_frames, state->locations);
|
1068
1091
|
|
1069
1092
|
snprintf(thread_context->thread_id, THREAD_ID_LIMIT_CHARS, "%"PRIu64" (%lu)", native_thread_id_for(thread), (unsigned long) thread_id_for(thread));
|
1070
1093
|
thread_context->thread_id_char_slice = (ddog_CharSlice) {.ptr = thread_context->thread_id, .len = strlen(thread_context->thread_id)};
|
@@ -1121,8 +1144,8 @@ static void initialize_context(VALUE thread, per_thread_context *thread_context,
|
|
1121
1144
|
}
|
1122
1145
|
|
1123
1146
|
static void free_context(per_thread_context* thread_context) {
|
1124
|
-
sampling_buffer_free(thread_context->sampling_buffer);
|
1125
|
-
|
1147
|
+
sampling_buffer_free(&thread_context->sampling_buffer);
|
1148
|
+
free(thread_context); // See "note on calloc vs ruby_xcalloc use" in heap_recorder.c
|
1126
1149
|
}
|
1127
1150
|
|
1128
1151
|
static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
@@ -1141,6 +1164,9 @@ static VALUE _native_inspect(DDTRACE_UNUSED VALUE _self, VALUE collector_instanc
|
|
1141
1164
|
rb_str_concat(result, rb_sprintf(" stats=%"PRIsVALUE, stats_as_ruby_hash(state)));
|
1142
1165
|
rb_str_concat(result, rb_sprintf(" endpoint_collection_enabled=%"PRIsVALUE, state->endpoint_collection_enabled ? Qtrue : Qfalse));
|
1143
1166
|
rb_str_concat(result, rb_sprintf(" timeline_enabled=%"PRIsVALUE, state->timeline_enabled ? Qtrue : Qfalse));
|
1167
|
+
rb_str_concat(result, rb_sprintf(" native_filenames_enabled=%"PRIsVALUE, state->native_filenames_enabled ? Qtrue : Qfalse));
|
1168
|
+
// Note: `st_table_size()` is available from Ruby 3.2+ but not before
|
1169
|
+
rb_str_concat(result, rb_sprintf(" native_filenames_cache_size=%zu", state->native_filenames_cache->num_entries));
|
1144
1170
|
rb_str_concat(result, rb_sprintf(" otel_context_enabled=%d", state->otel_context_enabled));
|
1145
1171
|
rb_str_concat(result, rb_sprintf(
|
1146
1172
|
" time_converter_state={.system_epoch_ns_reference=%ld, .delta_to_epoch_ns=%ld}",
|
@@ -1436,6 +1462,26 @@ static VALUE thread_list(thread_context_collector_state *state) {
|
|
1436
1462
|
return result;
|
1437
1463
|
}
|
1438
1464
|
|
1465
|
+
// Inside a signal handler, we don't want to do the whole work of recording a sample, but we only record the stack of
|
1466
|
+
// the current thread.
|
1467
|
+
//
|
1468
|
+
// Assumptions for this function are same as for `thread_context_collector_sample` except that this function is
|
1469
|
+
// expected to be called from a signal handler and to be async-signal-safe.
|
1470
|
+
//
|
1471
|
+
// Also, no allocation (Ruby or malloc) can happen.
|
1472
|
+
void thread_context_collector_prepare_sample_inside_signal_handler(VALUE self_instance) {
|
1473
|
+
thread_context_collector_state *state;
|
1474
|
+
if (!rb_typeddata_is_kind_of(self_instance, &thread_context_collector_typed_data)) return;
|
1475
|
+
// This should never fail if the above check passes
|
1476
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
1477
|
+
|
1478
|
+
VALUE current_thread = rb_thread_current();
|
1479
|
+
per_thread_context *thread_context = get_context_for(current_thread, state);
|
1480
|
+
if (thread_context == NULL) return;
|
1481
|
+
|
1482
|
+
prepare_sample_thread(current_thread, &thread_context->sampling_buffer);
|
1483
|
+
}
|
1484
|
+
|
1439
1485
|
void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object) {
|
1440
1486
|
thread_context_collector_state *state;
|
1441
1487
|
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
@@ -1515,7 +1561,7 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
1515
1561
|
/* thread: */ current_thread,
|
1516
1562
|
/* stack_from_thread: */ current_thread,
|
1517
1563
|
thread_context,
|
1518
|
-
thread_context->sampling_buffer,
|
1564
|
+
&thread_context->sampling_buffer,
|
1519
1565
|
(sample_values) {.alloc_samples = sample_weight, .alloc_samples_unscaled = 1, .heap_sample = true},
|
1520
1566
|
INVALID_TIME, // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
|
1521
1567
|
&ruby_vm_type,
|
@@ -1941,7 +1987,7 @@ static uint64_t otel_span_id_to_uint(VALUE otel_span_id) {
|
|
1941
1987
|
/* thread_being_sampled: */ current_thread,
|
1942
1988
|
/* stack_from_thread: */ current_thread,
|
1943
1989
|
thread_context,
|
1944
|
-
thread_context->sampling_buffer,
|
1990
|
+
&thread_context->sampling_buffer,
|
1945
1991
|
cpu_time_for_thread,
|
1946
1992
|
current_monotonic_wall_time_ns
|
1947
1993
|
);
|
@@ -2169,3 +2215,8 @@ static VALUE _native_system_epoch_time_now_ns(DDTRACE_UNUSED VALUE self, VALUE c
|
|
2169
2215
|
|
2170
2216
|
return LONG2NUM(system_epoch_time_ns);
|
2171
2217
|
}
|
2218
|
+
|
2219
|
+
static VALUE _native_prepare_sample_inside_signal_handler(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
|
2220
|
+
thread_context_collector_prepare_sample_inside_signal_handler(collector_instance);
|
2221
|
+
return Qtrue;
|
2222
|
+
}
|
@@ -10,6 +10,7 @@ void thread_context_collector_sample(
|
|
10
10
|
long current_monotonic_wall_time_ns,
|
11
11
|
VALUE profiler_overhead_stack_thread
|
12
12
|
);
|
13
|
+
void thread_context_collector_prepare_sample_inside_signal_handler(VALUE self_instance);
|
13
14
|
void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object);
|
14
15
|
void thread_context_collector_sample_skipped_allocation_samples(VALUE self_instance, unsigned int skipped_samples);
|
15
16
|
VALUE thread_context_collector_sample_after_gc(VALUE self_instance);
|
@@ -38,6 +38,26 @@ VALUE from_ddog_prof_EncodedProfile(ddog_prof_EncodedProfile profile) {
|
|
38
38
|
return TypedData_Wrap_Struct(encoded_profile_class, &encoded_profile_typed_data, state);
|
39
39
|
}
|
40
40
|
|
41
|
+
static ddog_ByteSlice get_bytes(ddog_prof_EncodedProfile *state) {
|
42
|
+
ddog_prof_Result_ByteSlice raw_bytes = ddog_prof_EncodedProfile_bytes(state);
|
43
|
+
if (raw_bytes.tag == DDOG_PROF_RESULT_BYTE_SLICE_ERR_BYTE_SLICE) {
|
44
|
+
rb_raise(rb_eRuntimeError, "Failed to get bytes from profile: %"PRIsVALUE, get_error_details_and_drop(&raw_bytes.err));
|
45
|
+
}
|
46
|
+
return raw_bytes.ok;
|
47
|
+
}
|
48
|
+
|
49
|
+
static ddog_prof_EncodedProfile *internal_to_ddog_prof_EncodedProfile(VALUE object) {
|
50
|
+
ddog_prof_EncodedProfile *state;
|
51
|
+
TypedData_Get_Struct(object, ddog_prof_EncodedProfile, &encoded_profile_typed_data, state);
|
52
|
+
return state;
|
53
|
+
}
|
54
|
+
|
55
|
+
ddog_prof_EncodedProfile *to_ddog_prof_EncodedProfile(VALUE object) {
|
56
|
+
ddog_prof_EncodedProfile *state = internal_to_ddog_prof_EncodedProfile(object);
|
57
|
+
get_bytes(state); // Validate profile is still usable -- if it's not, this will raise an exception
|
58
|
+
return state;
|
59
|
+
}
|
60
|
+
|
41
61
|
static void encoded_profile_typed_data_free(void *state_ptr) {
|
42
62
|
ddog_prof_EncodedProfile *state = (ddog_prof_EncodedProfile *) state_ptr;
|
43
63
|
|
@@ -49,18 +69,8 @@ static void encoded_profile_typed_data_free(void *state_ptr) {
|
|
49
69
|
}
|
50
70
|
|
51
71
|
static VALUE _native_bytes(VALUE self) {
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
return ruby_string_from_vec_u8(state->buffer);
|
56
|
-
|
57
|
-
// TODO: This will be used for libdatadog 17
|
58
|
-
/*ddog_prof_Result_ByteSlice raw_bytes = ddog_prof_EncodedProfile_bytes(state);
|
59
|
-
if (raw_bytes.tag == DDOG_PROF_RESULT_BYTE_SLICE_ERR_BYTE_SLICE) {
|
60
|
-
rb_raise(rb_eRuntimeError, "Failed to get bytes from profile: %"PRIsVALUE, get_error_details_and_drop(&raw_bytes.err));
|
61
|
-
}
|
62
|
-
|
63
|
-
return rb_str_new((const char *) raw_bytes.ok.ptr, raw_bytes.ok.len);*/
|
72
|
+
ddog_ByteSlice bytes = get_bytes(internal_to_ddog_prof_EncodedProfile(self));
|
73
|
+
return rb_str_new((const char *) bytes.ptr, bytes.len);
|
64
74
|
}
|
65
75
|
|
66
76
|
VALUE enforce_encoded_profile_instance(VALUE object) {
|
@@ -131,6 +131,13 @@ end
|
|
131
131
|
|
132
132
|
have_func "malloc_stats"
|
133
133
|
|
134
|
+
# Used to get native filenames (dladdr1 is preferred, so we only check for the other if not available)
|
135
|
+
# Note it's possible none are available
|
136
|
+
if have_header("dlfcn.h")
|
137
|
+
(have_struct_member("struct link_map", "l_name", "link.h") && have_func("dladdr1")) ||
|
138
|
+
have_func("dladdr")
|
139
|
+
end
|
140
|
+
|
134
141
|
# On Ruby 3.5, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347)
|
135
142
|
$defs << "-DNO_IMEMO_OBJECT_ID" unless RUBY_VERSION < "3.5"
|
136
143
|
|