datadog 2.28.0 → 2.29.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 +46 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +64 -3
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +23 -4
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +3 -1
- data/ext/datadog_profiling_native_extension/extconf.rb +5 -0
- data/ext/datadog_profiling_native_extension/heap_recorder.c +183 -51
- data/ext/datadog_profiling_native_extension/heap_recorder.h +12 -1
- data/ext/datadog_profiling_native_extension/stack_recorder.c +34 -5
- 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 +236 -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/excon/ssrf_detection_middleware.rb +54 -5
- 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/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +6 -10
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +1 -3
- data/lib/datadog/appsec/contrib/rails/patches/process_action_patch.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 +2 -0
- data/lib/datadog/appsec/counter_sampler.rb +25 -0
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +18 -0
- 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/supported_configurations.rb +3 -0
- data/lib/datadog/core/configuration.rb +2 -2
- data/lib/datadog/core/crashtracking/component.rb +79 -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 +15 -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/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/fnv.rb +26 -0
- data/lib/datadog/core.rb +6 -1
- data/lib/datadog/data_streams/processor.rb +34 -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/component.rb +1 -1
- data/lib/datadog/di/configuration/settings.rb +9 -0
- data/lib/datadog/di/context.rb +6 -0
- data/lib/datadog/di/instrumenter.rb +178 -133
- 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 +7 -2
- data/lib/datadog/di/probe_notification_builder.rb +29 -8
- data/lib/datadog/di/probe_notifier_worker.rb +13 -3
- data/lib/datadog/di/proc_responder.rb +4 -0
- data/lib/datadog/di/remote.rb +2 -2
- 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/input.rb +5 -6
- data/lib/datadog/kit/tracing/method_tracer.rb +132 -0
- data/lib/datadog/open_feature/transport.rb +8 -11
- data/lib/datadog/profiling/component.rb +0 -6
- data/lib/datadog/tracing/contrib/http/integration.rb +0 -2
- 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/rack/route_inference.rb +18 -6
- data/lib/datadog/tracing/contrib/registerable.rb +11 -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/transport/io/client.rb +5 -8
- data/lib/datadog/tracing/transport/io/traces.rb +28 -34
- data/lib/datadog/tracing/transport/traces.rb +4 -10
- data/lib/datadog/version.rb +1 -1
- metadata +10 -5
- data/lib/datadog/appsec/contrib/rails/ext.rb +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6f762ad624ddb0a21d753a524132de0f59e617e63f723fabc6c305949a77554d
|
|
4
|
+
data.tar.gz: 217159cbaea1102ba47a97deb73927919d8152264701322719aad722391d75aa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 711466d29940c6b22b5964744e7d1b6dbf5a002ca5992dbc01d61718f16cc66b33dcc7bee2604c4c2ba64a9efb8014114d015d44a109e1b5f55b1375d36c927a
|
|
7
|
+
data.tar.gz: 1490e2ab41fdf0c7ce673bbb7eaa1ccc28073cd757576a30734ce3674bef89fd58aa2876b2e9d2e26e12119e45b1d90f787e260d86cfc6e1fa5055bf14ba16b0
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [2.29.0] - 2026-02-20
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
* AppSec: Add analysis for downstream request with redirects ([#5347][])
|
|
11
|
+
* AppSec: Add downstream request body analysis. ([#5320][])
|
|
12
|
+
* SSI: Add support for Bundler vendored mode (`BUNDLE_PATH`) ([#5368][])
|
|
13
|
+
* SSI: Default to local dependency resolution ([#5368][])
|
|
14
|
+
* Dynamic Instrumentation: Added circuit breaker to automatically disable probes consuming excessive CPU time ([#5335][])
|
|
15
|
+
* Crashtracking: Add reporting of unhandled exceptions ([#5321][])
|
|
16
|
+
* Tracing: Add support for the `kicks` gem ([#5305][])
|
|
17
|
+
* Profiling: Add heap profiling for ruby 4.x ([#5201][])
|
|
18
|
+
* Tracing: Add simple method tracing API ([#5294][])
|
|
19
|
+
* Tracing: Add `trace_singleton_class_method` for tracing singleton class methods. ([#5334][])
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
* Dynamic Instrumentation: Improve error reporting when instrumentation fails or is removed due to circuit breaker ([#5371][])
|
|
24
|
+
* SSI: Reduce SSI package size ([#5352][])
|
|
25
|
+
* Core: Change default logger output from stdout to stderr ([#5342][])
|
|
26
|
+
* AppSec: Make AppSec blocking page more friendly for vulnerability scanners ([#5341][])
|
|
27
|
+
* Core: Add process tags and container id to process discovery payloads when the experimental setting `DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=true` is enabled. ([#5336][])
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
* Dynamic Instrumentation: Fix Live Debugger UI for forking web servers with more than one worker process ([#5304][])
|
|
32
|
+
|
|
5
33
|
## [2.28.0] - 2026-02-04
|
|
6
34
|
|
|
7
35
|
### Added
|
|
@@ -3486,7 +3514,8 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
|
|
|
3486
3514
|
Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
3487
3515
|
|
|
3488
3516
|
|
|
3489
|
-
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.
|
|
3517
|
+
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.29.0...master
|
|
3518
|
+
[2.29.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.28.0...v2.29.0
|
|
3490
3519
|
[2.28.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.27.0...v2.28.0
|
|
3491
3520
|
[2.27.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.26.0...v2.27.0
|
|
3492
3521
|
[2.26.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.25.0...v2.26.0
|
|
@@ -5153,6 +5182,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
|
5153
5182
|
[#5176]: https://github.com/DataDog/dd-trace-rb/issues/5176
|
|
5154
5183
|
[#5194]: https://github.com/DataDog/dd-trace-rb/issues/5194
|
|
5155
5184
|
[#5197]: https://github.com/DataDog/dd-trace-rb/issues/5197
|
|
5185
|
+
[#5201]: https://github.com/DataDog/dd-trace-rb/issues/5201
|
|
5156
5186
|
[#5206]: https://github.com/DataDog/dd-trace-rb/issues/5206
|
|
5157
5187
|
[#5210]: https://github.com/DataDog/dd-trace-rb/issues/5210
|
|
5158
5188
|
[#5215]: https://github.com/DataDog/dd-trace-rb/issues/5215
|
|
@@ -5166,6 +5196,20 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
|
5166
5196
|
[#5273]: https://github.com/DataDog/dd-trace-rb/issues/5273
|
|
5167
5197
|
[#5278]: https://github.com/DataDog/dd-trace-rb/issues/5278
|
|
5168
5198
|
[#5283]: https://github.com/DataDog/dd-trace-rb/issues/5283
|
|
5199
|
+
[#5294]: https://github.com/DataDog/dd-trace-rb/issues/5294
|
|
5200
|
+
[#5304]: https://github.com/DataDog/dd-trace-rb/issues/5304
|
|
5201
|
+
[#5305]: https://github.com/DataDog/dd-trace-rb/issues/5305
|
|
5202
|
+
[#5320]: https://github.com/DataDog/dd-trace-rb/issues/5320
|
|
5203
|
+
[#5321]: https://github.com/DataDog/dd-trace-rb/issues/5321
|
|
5204
|
+
[#5334]: https://github.com/DataDog/dd-trace-rb/issues/5334
|
|
5205
|
+
[#5335]: https://github.com/DataDog/dd-trace-rb/issues/5335
|
|
5206
|
+
[#5336]: https://github.com/DataDog/dd-trace-rb/issues/5336
|
|
5207
|
+
[#5341]: https://github.com/DataDog/dd-trace-rb/issues/5341
|
|
5208
|
+
[#5342]: https://github.com/DataDog/dd-trace-rb/issues/5342
|
|
5209
|
+
[#5347]: https://github.com/DataDog/dd-trace-rb/issues/5347
|
|
5210
|
+
[#5352]: https://github.com/DataDog/dd-trace-rb/issues/5352
|
|
5211
|
+
[#5368]: https://github.com/DataDog/dd-trace-rb/issues/5368
|
|
5212
|
+
[#5371]: https://github.com/DataDog/dd-trace-rb/issues/5371
|
|
5169
5213
|
[@AdrianLC]: https://github.com/AdrianLC
|
|
5170
5214
|
[@Azure7111]: https://github.com/Azure7111
|
|
5171
5215
|
[@BabyGroot]: https://github.com/BabyGroot
|
|
@@ -5320,4 +5364,4 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
|
5320
5364
|
[@y-yagi]: https://github.com/y-yagi
|
|
5321
5365
|
[@yujideveloper]: https://github.com/yujideveloper
|
|
5322
5366
|
[@yukimurasawa]: https://github.com/yukimurasawa
|
|
5323
|
-
[@zachmccormick]: https://github.com/zachmccormick
|
|
5367
|
+
[@zachmccormick]: https://github.com/zachmccormick
|
|
@@ -87,6 +87,7 @@ unsigned int MAX_ALLOC_WEIGHT = 10000;
|
|
|
87
87
|
static rb_postponed_job_handle_t sample_from_postponed_job_handle;
|
|
88
88
|
static rb_postponed_job_handle_t after_gc_from_postponed_job_handle;
|
|
89
89
|
static rb_postponed_job_handle_t after_gvl_running_from_postponed_job_handle;
|
|
90
|
+
static rb_postponed_job_handle_t after_allocation_from_postponed_job_handle;
|
|
90
91
|
#endif
|
|
91
92
|
|
|
92
93
|
// Contains state for a single CpuAndWallTimeWorker instance
|
|
@@ -138,6 +139,8 @@ typedef struct {
|
|
|
138
139
|
// # Generic stats
|
|
139
140
|
// How many times we tried to trigger a sample
|
|
140
141
|
unsigned int trigger_sample_attempts;
|
|
142
|
+
// How many times extra sleep was triggered by dynamic sampling rate
|
|
143
|
+
unsigned int trigger_sample_extra_sleep;
|
|
141
144
|
// How many times we tried to simulate signal delivery
|
|
142
145
|
unsigned int trigger_simulated_signal_delivery_attempts;
|
|
143
146
|
// How many times we actually simulated signal delivery
|
|
@@ -229,6 +232,7 @@ static void on_newobj_event(DDTRACE_UNUSED VALUE unused1, DDTRACE_UNUSED void *u
|
|
|
229
232
|
static void disable_tracepoints(cpu_and_wall_time_worker_state *state);
|
|
230
233
|
static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
|
|
231
234
|
static VALUE rescued_sample_allocation(VALUE tracepoint_data);
|
|
235
|
+
static VALUE rescued_after_allocation(VALUE self_instance);
|
|
232
236
|
static void delayed_error(cpu_and_wall_time_worker_state *state, const char *error);
|
|
233
237
|
static void delayed_error_clock_failure(cpu_and_wall_time_worker_state *state);
|
|
234
238
|
static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
|
|
@@ -243,9 +247,11 @@ static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE
|
|
|
243
247
|
static VALUE handle_sampling_failure_rescued_sample_from_postponed_job(VALUE self_instance, VALUE exception);
|
|
244
248
|
static VALUE handle_sampling_failure_thread_context_collector_sample_after_gc(VALUE self_instance, VALUE exception);
|
|
245
249
|
static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instance, VALUE exception);
|
|
250
|
+
static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception);
|
|
246
251
|
static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception);
|
|
247
252
|
static inline void during_sample_enter(cpu_and_wall_time_worker_state* state);
|
|
248
253
|
static inline void during_sample_exit(cpu_and_wall_time_worker_state* state);
|
|
254
|
+
static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
|
249
255
|
|
|
250
256
|
// We're using `on_newobj_event` function with `rb_add_event_hook2`, which requires in its public signature a function
|
|
251
257
|
// with signature `rb_event_hook_func_t` which doesn't match `on_newobj_event`.
|
|
@@ -293,11 +299,13 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
|
293
299
|
sample_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, sample_from_postponed_job, NULL);
|
|
294
300
|
after_gc_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gc_from_postponed_job, NULL);
|
|
295
301
|
after_gvl_running_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gvl_running_from_postponed_job, NULL);
|
|
302
|
+
after_allocation_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_allocation_from_postponed_job, NULL);
|
|
296
303
|
|
|
297
304
|
if (
|
|
298
305
|
sample_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
299
306
|
after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
300
|
-
after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
|
|
307
|
+
after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
|
308
|
+
after_allocation_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
|
|
301
309
|
) {
|
|
302
310
|
raise_error(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
|
|
303
311
|
}
|
|
@@ -713,7 +721,10 @@ static void *run_sampling_trigger_loop(void *state_ptr) {
|
|
|
713
721
|
// `dynamic_sampling_rate_get_sleep` may have changed while the above sleep was ongoing.
|
|
714
722
|
uint64_t extra_sleep =
|
|
715
723
|
dynamic_sampling_rate_get_sleep(&state->cpu_dynamic_sampling_rate, monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE));
|
|
716
|
-
if (state->dynamic_sampling_rate_enabled && extra_sleep > 0)
|
|
724
|
+
if (state->dynamic_sampling_rate_enabled && extra_sleep > 0) {
|
|
725
|
+
state->stats.trigger_sample_extra_sleep++;
|
|
726
|
+
sleep_for(extra_sleep);
|
|
727
|
+
}
|
|
717
728
|
}
|
|
718
729
|
|
|
719
730
|
return NULL; // Unused
|
|
@@ -1049,6 +1060,7 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
|
1049
1060
|
VALUE stats_as_hash = rb_hash_new();
|
|
1050
1061
|
VALUE arguments[] = {
|
|
1051
1062
|
ID2SYM(rb_intern("trigger_sample_attempts")), /* => */ UINT2NUM(state->stats.trigger_sample_attempts),
|
|
1063
|
+
ID2SYM(rb_intern("trigger_sample_extra_sleep")), /* => */ UINT2NUM(state->stats.trigger_sample_extra_sleep),
|
|
1052
1064
|
ID2SYM(rb_intern("trigger_simulated_signal_delivery_attempts")), /* => */ UINT2NUM(state->stats.trigger_simulated_signal_delivery_attempts),
|
|
1053
1065
|
ID2SYM(rb_intern("simulated_signal_delivery")), /* => */ UINT2NUM(state->stats.simulated_signal_delivery),
|
|
1054
1066
|
ID2SYM(rb_intern("signal_handler_enqueued_sample")), /* => */ UINT2NUM(state->stats.signal_handler_enqueued_sample),
|
|
@@ -1312,13 +1324,21 @@ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
|
|
|
1312
1324
|
// To control bias from sampling, we clamp the maximum weight attributed to a single allocation sample. This avoids
|
|
1313
1325
|
// assigning a very large number to a sample, if for instance the dynamic sampling mechanism chose a really big interval.
|
|
1314
1326
|
unsigned int weight = allocations_since_last_sample > MAX_ALLOC_WEIGHT ? MAX_ALLOC_WEIGHT : (unsigned int) allocations_since_last_sample;
|
|
1315
|
-
thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
|
|
1327
|
+
bool needs_after_allocation = thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
|
|
1316
1328
|
// ...but we still represent the skipped samples in the profile, thus the data will account for all allocations.
|
|
1317
1329
|
if (weight < allocations_since_last_sample) {
|
|
1318
1330
|
uint32_t skipped_samples = (uint32_t) uint64_min_of(allocations_since_last_sample - weight, UINT32_MAX);
|
|
1319
1331
|
thread_context_collector_sample_skipped_allocation_samples(state->thread_context_collector_instance, skipped_samples);
|
|
1320
1332
|
}
|
|
1321
1333
|
|
|
1334
|
+
if (needs_after_allocation) {
|
|
1335
|
+
#ifndef NO_POSTPONED_TRIGGER
|
|
1336
|
+
rb_postponed_job_trigger(after_allocation_from_postponed_job_handle);
|
|
1337
|
+
#else
|
|
1338
|
+
// Not needed on legacy rubies
|
|
1339
|
+
#endif
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1322
1342
|
// Return a dummy VALUE because we're called from rb_rescue2 which requires it
|
|
1323
1343
|
return Qnil;
|
|
1324
1344
|
}
|
|
@@ -1470,11 +1490,52 @@ static VALUE handle_sampling_failure_rescued_sample_allocation(VALUE self_instan
|
|
|
1470
1490
|
return Qnil;
|
|
1471
1491
|
}
|
|
1472
1492
|
|
|
1493
|
+
static VALUE handle_sampling_failure_rescued_after_allocation(VALUE self_instance, VALUE exception) {
|
|
1494
|
+
stop(self_instance, exception, "rescued_after_allocation");
|
|
1495
|
+
return Qnil;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1473
1498
|
static VALUE handle_sampling_failure_rescued_after_gvl_running_from_postponed_job(VALUE self_instance, VALUE exception) {
|
|
1474
1499
|
stop(self_instance, exception, "rescued_after_gvl_running_from_postponed_job");
|
|
1475
1500
|
return Qnil;
|
|
1476
1501
|
}
|
|
1477
1502
|
|
|
1503
|
+
static VALUE rescued_after_allocation(VALUE self_instance) {
|
|
1504
|
+
cpu_and_wall_time_worker_state *state;
|
|
1505
|
+
TypedData_Get_Struct(self_instance, cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
|
1506
|
+
|
|
1507
|
+
thread_context_collector_after_allocation(state->thread_context_collector_instance);
|
|
1508
|
+
|
|
1509
|
+
// Return a dummy VALUE because we're called from rb_rescue2 which requires it
|
|
1510
|
+
return Qnil;
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
// This postponed job callback is used to finalize heap allocation recordings on Ruby 4+.
|
|
1514
|
+
// During on_newobj_event, calling rb_obj_id() is unsafe because it mutates the object.
|
|
1515
|
+
// So we defer getting the object_id until after the event completes.
|
|
1516
|
+
static void after_allocation_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
1517
|
+
cpu_and_wall_time_worker_state *state = active_sampler_instance_state;
|
|
1518
|
+
|
|
1519
|
+
if (state == NULL || !ddtrace_rb_ractor_main_p()) return;
|
|
1520
|
+
|
|
1521
|
+
// Protect against nested operations
|
|
1522
|
+
if (state->during_sample) return;
|
|
1523
|
+
|
|
1524
|
+
during_sample_enter(state);
|
|
1525
|
+
|
|
1526
|
+
// NOTE: We're not updating the allocation_sampler here.
|
|
1527
|
+
// This means work done in this function isn't accounted for as profiler overhead.
|
|
1528
|
+
// This is acceptable as the amount of work done here is expected to be small.
|
|
1529
|
+
safely_call(
|
|
1530
|
+
rescued_after_allocation,
|
|
1531
|
+
state->self_instance,
|
|
1532
|
+
state->self_instance,
|
|
1533
|
+
handle_sampling_failure_rescued_after_allocation
|
|
1534
|
+
);
|
|
1535
|
+
|
|
1536
|
+
during_sample_exit(state);
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1478
1539
|
static inline void during_sample_enter(cpu_and_wall_time_worker_state* state) {
|
|
1479
1540
|
// Tell the compiler it's not allowed to reorder the `during_sample` flag with anything that happens after.
|
|
1480
1541
|
//
|
|
@@ -1339,6 +1339,16 @@ VALUE enforce_thread_context_collector_instance(VALUE object) {
|
|
|
1339
1339
|
return object;
|
|
1340
1340
|
}
|
|
1341
1341
|
|
|
1342
|
+
// Finalize any pending heap allocation recordings.
|
|
1343
|
+
// On Ruby 4+, heap allocations are recorded in two phases: during on_newobj_event we capture
|
|
1344
|
+
// the object reference, then later we safely call rb_obj_id() to get the object ID.
|
|
1345
|
+
void thread_context_collector_after_allocation(VALUE self_instance) {
|
|
1346
|
+
thread_context_collector_state *state;
|
|
1347
|
+
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1348
|
+
|
|
1349
|
+
recorder_after_sample(state->recorder_instance);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1342
1352
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
1343
1353
|
// It SHOULD NOT be used for other purposes.
|
|
1344
1354
|
static VALUE _native_stats(DDTRACE_UNUSED VALUE _self, VALUE collector_instance) {
|
|
@@ -1483,7 +1493,12 @@ bool thread_context_collector_prepare_sample_inside_signal_handler(VALUE self_in
|
|
|
1483
1493
|
return prepare_sample_thread(current_thread, &thread_context->sampling_buffer);
|
|
1484
1494
|
}
|
|
1485
1495
|
|
|
1486
|
-
|
|
1496
|
+
// This method gets called from inside the RUBY_INTERNAL_EVENT_NEWOBJ tracepoint so it should never allocate in the
|
|
1497
|
+
// Ruby heap.
|
|
1498
|
+
//
|
|
1499
|
+
// Returns true if the after_allocation needs to be called (to do work that can't be done from inside the
|
|
1500
|
+
// tracepoint, such as allocate new objects), and false if it doesn't
|
|
1501
|
+
bool thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object) {
|
|
1487
1502
|
thread_context_collector_state *state;
|
|
1488
1503
|
TypedData_Get_Struct(self_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
|
|
1489
1504
|
|
|
@@ -1553,7 +1568,7 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
|
1553
1568
|
class_name = ruby_vm_type; // For other weird internal things we just use the VM type
|
|
1554
1569
|
}
|
|
1555
1570
|
|
|
1556
|
-
track_object(state->recorder_instance, new_object, sample_weight, class_name);
|
|
1571
|
+
bool needs_after_allocation = track_object(state->recorder_instance, new_object, sample_weight, class_name);
|
|
1557
1572
|
|
|
1558
1573
|
per_thread_context *thread_context = get_or_create_context_for(current_thread, state);
|
|
1559
1574
|
|
|
@@ -1570,6 +1585,8 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
|
1570
1585
|
/* is_gvl_waiting_state: */ false,
|
|
1571
1586
|
/* is_safe_to_allocate_objects: */ false // Not safe to allocate further inside the NEWOBJ tracepoint
|
|
1572
1587
|
);
|
|
1588
|
+
|
|
1589
|
+
return needs_after_allocation;
|
|
1573
1590
|
}
|
|
1574
1591
|
|
|
1575
1592
|
// This method exists only to enable testing Datadog::Profiling::Collectors::ThreadContext behavior using RSpec.
|
|
@@ -1577,11 +1594,13 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
|
1577
1594
|
static VALUE _native_sample_allocation(DDTRACE_UNUSED VALUE self, VALUE collector_instance, VALUE sample_weight, VALUE new_object) {
|
|
1578
1595
|
debug_enter_unsafe_context();
|
|
1579
1596
|
|
|
1580
|
-
thread_context_collector_sample_allocation(collector_instance, NUM2UINT(sample_weight), new_object);
|
|
1597
|
+
bool needs_after_allocation = thread_context_collector_sample_allocation(collector_instance, NUM2UINT(sample_weight), new_object);
|
|
1581
1598
|
|
|
1582
1599
|
debug_leave_unsafe_context();
|
|
1583
1600
|
|
|
1584
|
-
|
|
1601
|
+
// We could instead choose to automatically trigger the after allocation here; yet, it seems kinda nice to keep it manual for
|
|
1602
|
+
// the tests so we can pull on each lever separately and observe "the sausage being made" in steps
|
|
1603
|
+
return needs_after_allocation ? Qtrue : Qfalse;
|
|
1585
1604
|
}
|
|
1586
1605
|
|
|
1587
1606
|
static VALUE new_empty_thread_inner(DDTRACE_UNUSED void *arg) { return Qnil; }
|
|
@@ -11,13 +11,15 @@ void thread_context_collector_sample(
|
|
|
11
11
|
VALUE profiler_overhead_stack_thread
|
|
12
12
|
);
|
|
13
13
|
__attribute__((warn_unused_result)) bool thread_context_collector_prepare_sample_inside_signal_handler(VALUE self_instance);
|
|
14
|
-
|
|
14
|
+
__attribute__((warn_unused_result)) bool thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object);
|
|
15
|
+
void thread_context_collector_after_allocation(VALUE self_instance);
|
|
15
16
|
void thread_context_collector_sample_skipped_allocation_samples(VALUE self_instance, unsigned int skipped_samples);
|
|
16
17
|
VALUE thread_context_collector_sample_after_gc(VALUE self_instance);
|
|
17
18
|
void thread_context_collector_on_gc_start(VALUE self_instance);
|
|
18
19
|
__attribute__((warn_unused_result)) bool thread_context_collector_on_gc_finish(VALUE self_instance);
|
|
19
20
|
VALUE enforce_thread_context_collector_instance(VALUE object);
|
|
20
21
|
|
|
22
|
+
|
|
21
23
|
#ifndef NO_GVL_INSTRUMENTATION
|
|
22
24
|
typedef enum {
|
|
23
25
|
ON_GVL_RUNNING_UNKNOWN, // Thread is not known, it may not even be from the current Ractor
|
|
@@ -144,6 +144,11 @@ $defs << "-DNO_PRIMITIVE_MUTEX_AND_CONDITION_VARIABLE" if RUBY_VERSION < "4"
|
|
|
144
144
|
# On Ruby 4, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347)
|
|
145
145
|
$defs << "-DNO_IMEMO_OBJECT_ID" unless RUBY_VERSION < "4"
|
|
146
146
|
|
|
147
|
+
# On Ruby 4, we need to defer calling rb_obj_id during heap allocation recording
|
|
148
|
+
# because it's not safe to mutate objects during the newobj tracepoint
|
|
149
|
+
# (see https://bugs.ruby-lang.org/issues/21710)
|
|
150
|
+
$defs << "-DUSE_DEFERRED_HEAP_ALLOCATION_RECORDING" unless RUBY_VERSION < "4"
|
|
151
|
+
|
|
147
152
|
# This symbol is exclusively visible on certain Ruby versions: 2.6 to 3.2, as well as 3.4 (but not 4.0+)
|
|
148
153
|
# It's only used to get extra information about an object when a failure happens, so it's a "very nice to have" but not
|
|
149
154
|
# actually required for correct behavior of the profiler.
|