datadog 2.3.0 → 2.4.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 +37 -1
- data/ext/datadog_profiling_loader/datadog_profiling_loader.c +9 -1
- data/ext/datadog_profiling_loader/extconf.rb +10 -22
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +148 -30
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +4 -2
- data/ext/datadog_profiling_native_extension/collectors_stack.c +89 -46
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +580 -29
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +9 -1
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +0 -27
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +0 -4
- data/ext/datadog_profiling_native_extension/extconf.rb +38 -21
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +50 -0
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +75 -0
- data/ext/datadog_profiling_native_extension/heap_recorder.c +20 -6
- data/ext/datadog_profiling_native_extension/http_transport.c +38 -6
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +52 -1
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -0
- data/ext/datadog_profiling_native_extension/profiling.c +1 -1
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
- data/ext/libdatadog_api/crashtracker.c +20 -18
- data/ext/libdatadog_api/datadog_ruby_common.c +0 -27
- data/ext/libdatadog_api/datadog_ruby_common.h +0 -4
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +2184 -108
- data/lib/datadog/appsec/assets/waf_rules/strict.json +1430 -2
- data/lib/datadog/appsec/component.rb +29 -8
- data/lib/datadog/appsec/configuration/settings.rb +2 -2
- data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +1 -0
- data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +21 -0
- data/lib/datadog/appsec/contrib/devise/patcher.rb +12 -2
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +0 -14
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +67 -31
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +18 -15
- data/lib/datadog/appsec/contrib/graphql/integration.rb +14 -1
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +2 -5
- data/lib/datadog/appsec/event.rb +1 -1
- data/lib/datadog/appsec/processor/rule_loader.rb +3 -1
- data/lib/datadog/appsec/processor/rule_merger.rb +33 -15
- data/lib/datadog/appsec/processor.rb +36 -37
- data/lib/datadog/appsec/rate_limiter.rb +25 -40
- data/lib/datadog/appsec/remote.rb +7 -3
- data/lib/datadog/appsec.rb +2 -2
- data/lib/datadog/core/configuration/components.rb +4 -3
- data/lib/datadog/core/configuration/settings.rb +84 -5
- data/lib/datadog/core/crashtracking/component.rb +1 -1
- data/lib/datadog/core/environment/execution.rb +5 -5
- data/lib/datadog/core/metrics/client.rb +7 -0
- data/lib/datadog/core/rate_limiter.rb +183 -0
- data/lib/datadog/core/remote/client/capabilities.rb +4 -3
- data/lib/datadog/core/remote/component.rb +4 -2
- data/lib/datadog/core/remote/negotiation.rb +4 -4
- data/lib/datadog/core/remote/tie.rb +2 -0
- data/lib/datadog/core/runtime/metrics.rb +1 -1
- data/lib/datadog/core/telemetry/component.rb +2 -0
- data/lib/datadog/core/telemetry/event.rb +12 -7
- data/lib/datadog/core/telemetry/logger.rb +51 -0
- data/lib/datadog/core/telemetry/logging.rb +50 -14
- data/lib/datadog/core/telemetry/request.rb +13 -1
- data/lib/datadog/core/utils/time.rb +12 -0
- data/lib/datadog/di/code_tracker.rb +168 -0
- data/lib/datadog/di/configuration/settings.rb +163 -0
- data/lib/datadog/di/configuration.rb +11 -0
- data/lib/datadog/di/error.rb +31 -0
- data/lib/datadog/di/extensions.rb +16 -0
- data/lib/datadog/di/probe.rb +133 -0
- data/lib/datadog/di/probe_builder.rb +41 -0
- data/lib/datadog/di/redactor.rb +188 -0
- data/lib/datadog/di/serializer.rb +193 -0
- data/lib/datadog/di.rb +14 -0
- data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -0
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +12 -10
- data/lib/datadog/profiling/collectors/info.rb +12 -3
- data/lib/datadog/profiling/collectors/thread_context.rb +26 -0
- data/lib/datadog/profiling/component.rb +20 -4
- data/lib/datadog/profiling/http_transport.rb +6 -1
- data/lib/datadog/profiling/scheduler.rb +2 -0
- data/lib/datadog/profiling/stack_recorder.rb +3 -0
- data/lib/datadog/single_step_instrument.rb +12 -0
- data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -12
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +78 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +33 -0
- data/lib/datadog/tracing/contrib/action_pack/patcher.rb +2 -0
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +4 -0
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +3 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +3 -1
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +5 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +9 -0
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +19 -0
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +9 -12
- data/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +13 -9
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +6 -3
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +18 -15
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -5
- data/lib/datadog/tracing/contrib/httpclient/patcher.rb +1 -14
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/httprb/patcher.rb +1 -14
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +1 -2
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +13 -6
- data/lib/datadog/tracing/contrib/patcher.rb +2 -1
- data/lib/datadog/tracing/contrib/presto/patcher.rb +1 -13
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +27 -0
- data/lib/datadog/tracing/contrib/redis/tags.rb +4 -0
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +4 -0
- data/lib/datadog/tracing/contrib/stripe/request.rb +3 -2
- data/lib/datadog/tracing/distributed/propagation.rb +7 -0
- data/lib/datadog/tracing/metadata/ext.rb +2 -0
- data/lib/datadog/tracing/remote.rb +5 -2
- data/lib/datadog/tracing/sampling/matcher.rb +6 -1
- data/lib/datadog/tracing/sampling/rate_sampler.rb +1 -1
- data/lib/datadog/tracing/sampling/rule.rb +2 -0
- data/lib/datadog/tracing/sampling/rule_sampler.rb +9 -5
- data/lib/datadog/tracing/sampling/span/ext.rb +1 -1
- data/lib/datadog/tracing/sampling/span/rule.rb +2 -2
- data/lib/datadog/tracing/trace_operation.rb +26 -2
- data/lib/datadog/tracing/tracer.rb +14 -12
- data/lib/datadog/tracing/transport/http/client.rb +1 -0
- data/lib/datadog/tracing/transport/io/client.rb +1 -0
- data/lib/datadog/tracing/workers/trace_writer.rb +1 -1
- data/lib/datadog/tracing/workers.rb +1 -1
- data/lib/datadog/version.rb +1 -1
- metadata +25 -8
- data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba1dc00d9afe70c54ea0918673e832793bb7c7a629618dc3d36e066fd121ea56
|
4
|
+
data.tar.gz: 9818bf0ba8dbd989451b7b0492efe848a9cdf97239acac3637aaa9d17937b541
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 419ac80497f45159d199d2b9d3b16c70c46903d65d828c6e5ab52029758c9b38e11b243e112d52e3e9d45f05d5e1acf814bcb3f7582496f1e5be120c13f0f685
|
7
|
+
data.tar.gz: ce8f36e974b194fbdedd40fdf634e49115c0bd6befb350092dfc4cb94185a173a7d3325b6adc9cf50e060b05a4968ca7858a52082748a4979090a7eb32aa96ba
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,29 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
+
## [2.4.0] - 2024-10-11
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
* Core: Allow changing sampling rate for customer defined tags and resources ([#3956][])
|
10
|
+
* Profiling: Add GVL profiling for Ruby 3.2+ as a preview feature ([#3929][])
|
11
|
+
* Profiling: Otel: Add preview support for correlating profiling with otel ruby gem ([#3984][])
|
12
|
+
* Tracing: AppSec: Add http.route tag to Rails, Grape, and Sinatra integrations ([#3849][])
|
13
|
+
* Tracing: Add capabilities to remote config: tracing sample rate, tracing logs injection, tracing http header tags ([#3888][])
|
14
|
+
* AppSec: Add a force disable of AppSec feature when using Ruby >= 3.3 with old FFI gem version ([#3969][])
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
|
18
|
+
* AppSec: Improve PII compliance ([#3857][])
|
19
|
+
* AppSec: Integrations: Improve accuracy of login tracking for Devise ([#3867][])
|
20
|
+
* Crashtracking feature is now disabled by default ([#3970][])
|
21
|
+
|
22
|
+
### Fixed
|
23
|
+
|
24
|
+
* AppSec: Integrations: Fix GraphQL instrumentation for query fragments ([#3887][])
|
25
|
+
* Bug: Profiling: Fix (small) memory leak in profiler when forking ([#3852][])
|
26
|
+
* Tracing: Integrations: Fix GraphQL integration reconfiguration ([#3859][])
|
27
|
+
|
5
28
|
## [2.3.0] - 2024-08-22
|
6
29
|
|
7
30
|
### Added
|
@@ -2962,7 +2985,8 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
|
|
2962
2985
|
Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
2963
2986
|
|
2964
2987
|
|
2965
|
-
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.
|
2988
|
+
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.4.0...master
|
2989
|
+
[2.4.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.3.0...v2.4.0
|
2966
2990
|
[2.3.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.2.0...v2.3.0
|
2967
2991
|
[2.2.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.1.0...v2.2.0
|
2968
2992
|
[2.1.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0...v2.1.0
|
@@ -4383,6 +4407,18 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4383
4407
|
[#3837]: https://github.com/DataDog/dd-trace-rb/issues/3837
|
4384
4408
|
[#3839]: https://github.com/DataDog/dd-trace-rb/issues/3839
|
4385
4409
|
[#3841]: https://github.com/DataDog/dd-trace-rb/issues/3841
|
4410
|
+
[#3849]: https://github.com/DataDog/dd-trace-rb/issues/3849
|
4411
|
+
[#3852]: https://github.com/DataDog/dd-trace-rb/issues/3852
|
4412
|
+
[#3857]: https://github.com/DataDog/dd-trace-rb/issues/3857
|
4413
|
+
[#3859]: https://github.com/DataDog/dd-trace-rb/issues/3859
|
4414
|
+
[#3867]: https://github.com/DataDog/dd-trace-rb/issues/3867
|
4415
|
+
[#3887]: https://github.com/DataDog/dd-trace-rb/issues/3887
|
4416
|
+
[#3888]: https://github.com/DataDog/dd-trace-rb/issues/3888
|
4417
|
+
[#3929]: https://github.com/DataDog/dd-trace-rb/issues/3929
|
4418
|
+
[#3956]: https://github.com/DataDog/dd-trace-rb/issues/3956
|
4419
|
+
[#3969]: https://github.com/DataDog/dd-trace-rb/issues/3969
|
4420
|
+
[#3970]: https://github.com/DataDog/dd-trace-rb/issues/3970
|
4421
|
+
[#3984]: https://github.com/DataDog/dd-trace-rb/issues/3984
|
4386
4422
|
[@AdrianLC]: https://github.com/AdrianLC
|
4387
4423
|
[@Azure7111]: https://github.com/Azure7111
|
4388
4424
|
[@BabyGroot]: https://github.com/BabyGroot
|
@@ -65,7 +65,15 @@ static VALUE _native_load(DDTRACE_UNUSED VALUE self, VALUE ruby_path, VALUE ruby
|
|
65
65
|
char *path = StringValueCStr(ruby_path);
|
66
66
|
char *init_name = StringValueCStr(ruby_init_name);
|
67
67
|
|
68
|
-
|
68
|
+
int dlopen_flags = RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND;
|
69
|
+
|
70
|
+
#if defined(__has_feature)
|
71
|
+
#if __has_feature(address_sanitizer)
|
72
|
+
dlopen_flags &= ~RTLD_DEEPBIND; // Not supported by ASAN
|
73
|
+
#endif
|
74
|
+
#endif
|
75
|
+
|
76
|
+
void *handle = dlopen(path, dlopen_flags);
|
69
77
|
|
70
78
|
VALUE failure_details = Qnil;
|
71
79
|
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# rubocop:disable Style/StderrPuts
|
2
|
-
# rubocop:disable Style/GlobalVars
|
3
2
|
|
4
3
|
if RUBY_ENGINE != "ruby" || Gem.win_platform?
|
5
4
|
$stderr.puts(
|
@@ -12,38 +11,28 @@ end
|
|
12
11
|
|
13
12
|
require "mkmf"
|
14
13
|
|
15
|
-
# mkmf on modern Rubies actually has an append_cflags that does something similar
|
16
|
-
# (see https://github.com/ruby/ruby/pull/5760), but as usual we need a bit more boilerplate to deal with legacy Rubies
|
17
|
-
def add_compiler_flag(flag)
|
18
|
-
if try_cflags(flag)
|
19
|
-
$CFLAGS << " " << flag
|
20
|
-
else
|
21
|
-
$stderr.puts("WARNING: '#{flag}' not accepted by compiler, skipping it")
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
14
|
# Because we can't control what compiler versions our customers use, shipping with -Werror by default is a no-go.
|
26
15
|
# But we can enable it in CI, so that we quickly spot any new warnings that just got introduced.
|
27
|
-
|
16
|
+
append_cflags "-Werror" if ENV["DATADOG_GEM_CI"] == "true"
|
28
17
|
|
29
18
|
# Older gcc releases may not default to C99 and we need to ask for this. This is also used:
|
30
19
|
# * by upstream Ruby -- search for gnu99 in the codebase
|
31
20
|
# * by msgpack, another datadog gem dependency
|
32
21
|
# (https://github.com/msgpack/msgpack-ruby/blob/18ce08f6d612fe973843c366ac9a0b74c4e50599/ext/msgpack/extconf.rb#L8)
|
33
|
-
|
22
|
+
append_cflags "-std=gnu99"
|
34
23
|
|
35
24
|
# Gets really noisy when we include the MJIT header, let's omit it (TODO: Use #pragma GCC diagnostic instead?)
|
36
|
-
|
25
|
+
append_cflags "-Wno-unused-function"
|
37
26
|
|
38
27
|
# Allow defining variables at any point in a function
|
39
|
-
|
28
|
+
append_cflags "-Wno-declaration-after-statement"
|
40
29
|
|
41
30
|
# If we forget to include a Ruby header, the function call may still appear to work, but then
|
42
31
|
# cause a segfault later. Let's ensure that never happens.
|
43
|
-
|
32
|
+
append_cflags "-Werror-implicit-function-declaration"
|
44
33
|
|
45
34
|
# Warn on unused parameters to functions. Use `DDTRACE_UNUSED` to mark things as known-to-not-be-used.
|
46
|
-
|
35
|
+
append_cflags "-Wunused-parameter"
|
47
36
|
|
48
37
|
# The native extension is not intended to expose any symbols/functions for other native libraries to use;
|
49
38
|
# the sole exception being `Init_datadog_profiling_loader` which needs to be visible for Ruby to call it when
|
@@ -51,14 +40,14 @@ add_compiler_flag "-Wunused-parameter"
|
|
51
40
|
#
|
52
41
|
# By setting this compiler flag, we tell it to assume that everything is private unless explicitly stated.
|
53
42
|
# For more details see https://gcc.gnu.org/wiki/Visibility
|
54
|
-
|
43
|
+
append_cflags "-fvisibility=hidden"
|
55
44
|
|
56
45
|
# Avoid legacy C definitions
|
57
|
-
|
46
|
+
append_cflags "-Wold-style-definition"
|
58
47
|
|
59
48
|
# Enable all other compiler warnings
|
60
|
-
|
61
|
-
|
49
|
+
append_cflags "-Wall"
|
50
|
+
append_cflags "-Wextra"
|
62
51
|
|
63
52
|
# Tag the native extension library with the Ruby version and Ruby platform.
|
64
53
|
# This makes it easier for development (avoids "oops I forgot to rebuild when I switched my Ruby") and ensures that
|
@@ -68,5 +57,4 @@ EXTENSION_NAME = "datadog_profiling_loader.#{RUBY_VERSION}_#{RUBY_PLATFORM}".fre
|
|
68
57
|
|
69
58
|
create_makefile(EXTENSION_NAME)
|
70
59
|
|
71
|
-
# rubocop:enable Style/GlobalVars
|
72
60
|
# rubocop:enable Style/StderrPuts
|
@@ -88,6 +88,7 @@ unsigned int MAX_ALLOC_WEIGHT = 10000;
|
|
88
88
|
// `collectors_cpu_and_wall_time_worker_init` below and always get reused after that.
|
89
89
|
static rb_postponed_job_handle_t sample_from_postponed_job_handle;
|
90
90
|
static rb_postponed_job_handle_t after_gc_from_postponed_job_handle;
|
91
|
+
static rb_postponed_job_handle_t after_gvl_running_from_postponed_job_handle;
|
91
92
|
#endif
|
92
93
|
|
93
94
|
// Contains state for a single CpuAndWallTimeWorker instance
|
@@ -99,6 +100,7 @@ struct cpu_and_wall_time_worker_state {
|
|
99
100
|
bool dynamic_sampling_rate_enabled;
|
100
101
|
bool allocation_profiling_enabled;
|
101
102
|
bool allocation_counting_enabled;
|
103
|
+
bool gvl_profiling_enabled;
|
102
104
|
bool skip_idle_samples_for_testing;
|
103
105
|
VALUE self_instance;
|
104
106
|
VALUE thread_context_collector_instance;
|
@@ -123,6 +125,11 @@ struct cpu_and_wall_time_worker_state {
|
|
123
125
|
// that happens during another sample.
|
124
126
|
bool during_sample;
|
125
127
|
|
128
|
+
#ifndef NO_GVL_INSTRUMENTATION
|
129
|
+
// Only set when sampling is active (gets created at start and cleaned on stop)
|
130
|
+
rb_internal_thread_event_hook_t *gvl_profiling_hook;
|
131
|
+
#endif
|
132
|
+
|
126
133
|
struct stats {
|
127
134
|
// # Generic stats
|
128
135
|
// How many times we tried to trigger a sample
|
@@ -169,23 +176,15 @@ struct cpu_and_wall_time_worker_state {
|
|
169
176
|
uint64_t allocation_sampling_time_ns_total;
|
170
177
|
// How many times we saw allocations being done inside a sample
|
171
178
|
unsigned int allocations_during_sample;
|
179
|
+
|
180
|
+
// # GVL profiling stats
|
181
|
+
// How many times we triggered the after_gvl_running sampling
|
182
|
+
unsigned int after_gvl_running;
|
172
183
|
} stats;
|
173
184
|
};
|
174
185
|
|
175
186
|
static VALUE _native_new(VALUE klass);
|
176
|
-
static VALUE _native_initialize(
|
177
|
-
DDTRACE_UNUSED VALUE _self,
|
178
|
-
VALUE self_instance,
|
179
|
-
VALUE thread_context_collector_instance,
|
180
|
-
VALUE gc_profiling_enabled,
|
181
|
-
VALUE idle_sampling_helper_instance,
|
182
|
-
VALUE no_signals_workaround_enabled,
|
183
|
-
VALUE dynamic_sampling_rate_enabled,
|
184
|
-
VALUE dynamic_sampling_rate_overhead_target_percentage,
|
185
|
-
VALUE allocation_profiling_enabled,
|
186
|
-
VALUE allocation_counting_enabled,
|
187
|
-
VALUE skip_idle_samples_for_testing
|
188
|
-
);
|
187
|
+
static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
|
189
188
|
static void cpu_and_wall_time_worker_typed_data_mark(void *state_ptr);
|
190
189
|
static VALUE _native_sampling_loop(VALUE self, VALUE instance);
|
191
190
|
static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread);
|
@@ -227,6 +226,11 @@ static void delayed_error(struct cpu_and_wall_time_worker_state *state, const ch
|
|
227
226
|
static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
|
228
227
|
static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);
|
229
228
|
static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
|
229
|
+
#ifndef NO_GVL_INSTRUMENTATION
|
230
|
+
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused);
|
231
|
+
static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused);
|
232
|
+
#endif
|
233
|
+
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance);
|
230
234
|
|
231
235
|
// We're using `on_newobj_event` function with `rb_add_event_hook2`, which requires in its public signature a function
|
232
236
|
// with signature `rb_event_hook_func_t` which doesn't match `on_newobj_event`.
|
@@ -272,8 +276,13 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
272
276
|
int unused_flags = 0;
|
273
277
|
sample_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, sample_from_postponed_job, NULL);
|
274
278
|
after_gc_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gc_from_postponed_job, NULL);
|
279
|
+
after_gvl_running_from_postponed_job_handle = rb_postponed_job_preregister(unused_flags, after_gvl_running_from_postponed_job, NULL);
|
275
280
|
|
276
|
-
if (
|
281
|
+
if (
|
282
|
+
sample_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
283
|
+
after_gc_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID ||
|
284
|
+
after_gvl_running_from_postponed_job_handle == POSTPONED_JOB_HANDLE_INVALID
|
285
|
+
) {
|
277
286
|
rb_raise(rb_eRuntimeError, "Failed to register profiler postponed jobs (got POSTPONED_JOB_HANDLE_INVALID)");
|
278
287
|
}
|
279
288
|
#else
|
@@ -295,7 +304,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
295
304
|
// https://bugs.ruby-lang.org/issues/18007 for a discussion around this.
|
296
305
|
rb_define_alloc_func(collectors_cpu_and_wall_time_worker_class, _native_new);
|
297
306
|
|
298
|
-
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_initialize", _native_initialize,
|
307
|
+
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_initialize", _native_initialize, -1);
|
299
308
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_sampling_loop", _native_sampling_loop, 1);
|
300
309
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_stop", _native_stop, 2);
|
301
310
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_reset_after_fork", _native_reset_after_fork, 1);
|
@@ -317,6 +326,7 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
317
326
|
rb_define_singleton_method(testing_module, "_native_is_sigprof_blocked_in_current_thread", _native_is_sigprof_blocked_in_current_thread, 0);
|
318
327
|
rb_define_singleton_method(testing_module, "_native_with_blocked_sigprof", _native_with_blocked_sigprof, 0);
|
319
328
|
rb_define_singleton_method(testing_module, "_native_delayed_error", _native_delayed_error, 2);
|
329
|
+
rb_define_singleton_method(testing_module, "_native_gvl_profiling_hook_active", _native_gvl_profiling_hook_active, 1);
|
320
330
|
}
|
321
331
|
|
322
332
|
// This structure is used to define a Ruby object that stores a pointer to a struct cpu_and_wall_time_worker_state
|
@@ -345,6 +355,7 @@ static VALUE _native_new(VALUE klass) {
|
|
345
355
|
state->dynamic_sampling_rate_enabled = true;
|
346
356
|
state->allocation_profiling_enabled = false;
|
347
357
|
state->allocation_counting_enabled = false;
|
358
|
+
state->gvl_profiling_enabled = false;
|
348
359
|
state->skip_idle_samples_for_testing = false;
|
349
360
|
state->thread_context_collector_instance = Qnil;
|
350
361
|
state->idle_sampling_helper_instance = Qnil;
|
@@ -358,6 +369,10 @@ static VALUE _native_new(VALUE klass) {
|
|
358
369
|
|
359
370
|
state->during_sample = false;
|
360
371
|
|
372
|
+
#ifndef NO_GVL_INSTRUMENTATION
|
373
|
+
state->gvl_profiling_hook = NULL;
|
374
|
+
#endif
|
375
|
+
|
361
376
|
reset_stats_not_thread_safe(state);
|
362
377
|
discrete_dynamic_sampler_init(&state->allocation_sampler, "allocation", now);
|
363
378
|
|
@@ -368,25 +383,30 @@ static VALUE _native_new(VALUE klass) {
|
|
368
383
|
return state->self_instance = TypedData_Wrap_Struct(klass, &cpu_and_wall_time_worker_typed_data, state);
|
369
384
|
}
|
370
385
|
|
371
|
-
static VALUE _native_initialize(
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
VALUE
|
377
|
-
VALUE
|
378
|
-
VALUE
|
379
|
-
VALUE
|
380
|
-
VALUE
|
381
|
-
VALUE
|
382
|
-
VALUE
|
383
|
-
)
|
386
|
+
static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self) {
|
387
|
+
VALUE options;
|
388
|
+
rb_scan_args(argc, argv, "0:", &options);
|
389
|
+
if (options == Qnil) options = rb_hash_new();
|
390
|
+
|
391
|
+
VALUE self_instance = rb_hash_fetch(options, ID2SYM(rb_intern("self_instance")));
|
392
|
+
VALUE thread_context_collector_instance = rb_hash_fetch(options, ID2SYM(rb_intern("thread_context_collector")));
|
393
|
+
VALUE gc_profiling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("gc_profiling_enabled")));
|
394
|
+
VALUE idle_sampling_helper_instance = rb_hash_fetch(options, ID2SYM(rb_intern("idle_sampling_helper")));
|
395
|
+
VALUE no_signals_workaround_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("no_signals_workaround_enabled")));
|
396
|
+
VALUE dynamic_sampling_rate_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("dynamic_sampling_rate_enabled")));
|
397
|
+
VALUE dynamic_sampling_rate_overhead_target_percentage = rb_hash_fetch(options, ID2SYM(rb_intern("dynamic_sampling_rate_overhead_target_percentage")));
|
398
|
+
VALUE allocation_profiling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("allocation_profiling_enabled")));
|
399
|
+
VALUE allocation_counting_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("allocation_counting_enabled")));
|
400
|
+
VALUE gvl_profiling_enabled = rb_hash_fetch(options, ID2SYM(rb_intern("gvl_profiling_enabled")));
|
401
|
+
VALUE skip_idle_samples_for_testing = rb_hash_fetch(options, ID2SYM(rb_intern("skip_idle_samples_for_testing")));
|
402
|
+
|
384
403
|
ENFORCE_BOOLEAN(gc_profiling_enabled);
|
385
404
|
ENFORCE_BOOLEAN(no_signals_workaround_enabled);
|
386
405
|
ENFORCE_BOOLEAN(dynamic_sampling_rate_enabled);
|
387
406
|
ENFORCE_TYPE(dynamic_sampling_rate_overhead_target_percentage, T_FLOAT);
|
388
407
|
ENFORCE_BOOLEAN(allocation_profiling_enabled);
|
389
408
|
ENFORCE_BOOLEAN(allocation_counting_enabled);
|
409
|
+
ENFORCE_BOOLEAN(gvl_profiling_enabled);
|
390
410
|
ENFORCE_BOOLEAN(skip_idle_samples_for_testing)
|
391
411
|
|
392
412
|
struct cpu_and_wall_time_worker_state *state;
|
@@ -397,6 +417,7 @@ static VALUE _native_initialize(
|
|
397
417
|
state->dynamic_sampling_rate_enabled = (dynamic_sampling_rate_enabled == Qtrue);
|
398
418
|
state->allocation_profiling_enabled = (allocation_profiling_enabled == Qtrue);
|
399
419
|
state->allocation_counting_enabled = (allocation_counting_enabled == Qtrue);
|
420
|
+
state->gvl_profiling_enabled = (gvl_profiling_enabled == Qtrue);
|
400
421
|
state->skip_idle_samples_for_testing = (skip_idle_samples_for_testing == Qtrue);
|
401
422
|
|
402
423
|
double total_overhead_target_percentage = NUM2DBL(dynamic_sampling_rate_overhead_target_percentage);
|
@@ -781,6 +802,27 @@ static VALUE release_gvl_and_run_sampling_trigger_loop(VALUE instance) {
|
|
781
802
|
;
|
782
803
|
}
|
783
804
|
|
805
|
+
if (state->gvl_profiling_enabled) {
|
806
|
+
#ifndef NO_GVL_INSTRUMENTATION
|
807
|
+
#ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS
|
808
|
+
gvl_profiling_state_thread_tracking_workaround();
|
809
|
+
#endif
|
810
|
+
|
811
|
+
state->gvl_profiling_hook = rb_internal_thread_add_event_hook(
|
812
|
+
on_gvl_event,
|
813
|
+
(
|
814
|
+
// For now we're only asking for these events, even though there's more
|
815
|
+
// (e.g. check docs or gvl-tracing gem)
|
816
|
+
RUBY_INTERNAL_THREAD_EVENT_READY /* waiting for gvl */ |
|
817
|
+
RUBY_INTERNAL_THREAD_EVENT_RESUMED /* running/runnable */
|
818
|
+
),
|
819
|
+
NULL
|
820
|
+
);
|
821
|
+
#else
|
822
|
+
rb_raise(rb_eArgError, "GVL profiling is not supported in this Ruby version");
|
823
|
+
#endif
|
824
|
+
}
|
825
|
+
|
784
826
|
// Flag the profiler as running before we release the GVL, in case anyone's waiting to know about it
|
785
827
|
rb_funcall(instance, rb_intern("signal_running"), 0);
|
786
828
|
|
@@ -892,7 +934,6 @@ static void after_gc_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
|
892
934
|
|
893
935
|
state->during_sample = true;
|
894
936
|
|
895
|
-
// Trigger sampling using the Collectors::ThreadState; rescue against any exceptions that happen during sampling
|
896
937
|
safely_call(thread_context_collector_sample_after_gc, state->thread_context_collector_instance, state->self_instance);
|
897
938
|
|
898
939
|
state->during_sample = false;
|
@@ -999,6 +1040,9 @@ static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
|
999
1040
|
ID2SYM(rb_intern("allocation_sampling_time_ns_avg")), /* => */ RUBY_AVG_OR_NIL(state->stats.allocation_sampling_time_ns_total, state->stats.allocation_sampled),
|
1000
1041
|
ID2SYM(rb_intern("allocation_sampler_snapshot")), /* => */ allocation_sampler_snapshot,
|
1001
1042
|
ID2SYM(rb_intern("allocations_during_sample")), /* => */ state->allocation_profiling_enabled ? UINT2NUM(state->stats.allocations_during_sample) : Qnil,
|
1043
|
+
|
1044
|
+
// GVL profiling stats
|
1045
|
+
ID2SYM(rb_intern("after_gvl_running")), /* => */ UINT2NUM(state->stats.after_gvl_running),
|
1002
1046
|
};
|
1003
1047
|
for (long unsigned int i = 0; i < VALUE_COUNT(arguments); i += 2) rb_hash_aset(stats_as_hash, arguments[i], arguments[i+1]);
|
1004
1048
|
return stats_as_hash;
|
@@ -1173,7 +1217,15 @@ static void disable_tracepoints(struct cpu_and_wall_time_worker_state *state) {
|
|
1173
1217
|
if (state->gc_tracepoint != Qnil) {
|
1174
1218
|
rb_tracepoint_disable(state->gc_tracepoint);
|
1175
1219
|
}
|
1220
|
+
|
1176
1221
|
rb_remove_event_hook_with_data(on_newobj_event_as_hook, state->self_instance);
|
1222
|
+
|
1223
|
+
#ifndef NO_GVL_INSTRUMENTATION
|
1224
|
+
if (state->gvl_profiling_hook) {
|
1225
|
+
rb_internal_thread_remove_event_hook(state->gvl_profiling_hook);
|
1226
|
+
state->gvl_profiling_hook = NULL;
|
1227
|
+
}
|
1228
|
+
#endif
|
1177
1229
|
}
|
1178
1230
|
|
1179
1231
|
static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self) {
|
@@ -1211,7 +1263,8 @@ static VALUE rescued_sample_allocation(DDTRACE_UNUSED VALUE unused) {
|
|
1211
1263
|
thread_context_collector_sample_allocation(state->thread_context_collector_instance, weight, new_object);
|
1212
1264
|
// ...but we still represent the skipped samples in the profile, thus the data will account for all allocations.
|
1213
1265
|
if (weight < allocations_since_last_sample) {
|
1214
|
-
|
1266
|
+
uint32_t skipped_samples = (uint32_t) uint64_min_of(allocations_since_last_sample - weight, UINT32_MAX);
|
1267
|
+
thread_context_collector_sample_skipped_allocation_samples(state->thread_context_collector_instance, skipped_samples);
|
1215
1268
|
}
|
1216
1269
|
|
1217
1270
|
// Return a dummy VALUE because we're called from rb_rescue2 which requires it
|
@@ -1247,3 +1300,68 @@ static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
|
|
1247
1300
|
unblock_sigprof_signal_handler_from_running_in_current_thread();
|
1248
1301
|
return Qtrue;
|
1249
1302
|
}
|
1303
|
+
|
1304
|
+
#ifndef NO_GVL_INSTRUMENTATION
|
1305
|
+
static void on_gvl_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *event_data, DDTRACE_UNUSED void *_unused) {
|
1306
|
+
// Be very careful about touching the `state` here or doing anything at all:
|
1307
|
+
// This function gets called without the GVL, and potentially from background Ractors!
|
1308
|
+
//
|
1309
|
+
// In fact, the `target_thread` that this event is about may not even be the current thread. (So be careful with thread locals that
|
1310
|
+
// are not directly tied to the `target_thread` object and the like)
|
1311
|
+
gvl_profiling_thread target_thread = thread_from_event(event_data);
|
1312
|
+
|
1313
|
+
if (event_id == RUBY_INTERNAL_THREAD_EVENT_READY) { /* waiting for gvl */
|
1314
|
+
thread_context_collector_on_gvl_waiting(target_thread);
|
1315
|
+
} else if (event_id == RUBY_INTERNAL_THREAD_EVENT_RESUMED) { /* running/runnable */
|
1316
|
+
// Interesting note: A RUBY_INTERNAL_THREAD_EVENT_RESUMED is guaranteed to be called with the GVL being acquired.
|
1317
|
+
// (And... I think target_thread will be == rb_thread_current()?)
|
1318
|
+
// But we're not sure if we're on the main Ractor yet. The thread context collector actually can actually help here:
|
1319
|
+
// it tags threads it's tracking, so if a thread is tagged then by definition we know that thread belongs to the main
|
1320
|
+
// Ractor. Thus, if we really really wanted to access the state, we could do it after making sure we're on the correct Ractor.
|
1321
|
+
|
1322
|
+
#ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS
|
1323
|
+
target_thread = gvl_profiling_state_maybe_initialize();
|
1324
|
+
#endif
|
1325
|
+
|
1326
|
+
bool should_sample = thread_context_collector_on_gvl_running(target_thread);
|
1327
|
+
|
1328
|
+
if (should_sample) {
|
1329
|
+
// should_sample is only true if a thread belongs to the main Ractor, so we're good to go
|
1330
|
+
#ifndef NO_POSTPONED_TRIGGER
|
1331
|
+
rb_postponed_job_trigger(after_gvl_running_from_postponed_job_handle);
|
1332
|
+
#else
|
1333
|
+
rb_postponed_job_register_one(0, after_gvl_running_from_postponed_job, NULL);
|
1334
|
+
#endif
|
1335
|
+
}
|
1336
|
+
} else {
|
1337
|
+
// This is a very delicate time and it's hard for us to raise an exception so let's at least complain to stderr
|
1338
|
+
fprintf(stderr, "[ddtrace] Unexpected value in on_gvl_event (%d)\n", event_id);
|
1339
|
+
}
|
1340
|
+
}
|
1341
|
+
|
1342
|
+
static void after_gvl_running_from_postponed_job(DDTRACE_UNUSED void *_unused) {
|
1343
|
+
struct cpu_and_wall_time_worker_state *state = active_sampler_instance_state; // Read from global variable, see "sampler global state safety" note above
|
1344
|
+
|
1345
|
+
// This can potentially happen if the CpuAndWallTimeWorker was stopped while the postponed job was waiting to be executed; nothing to do
|
1346
|
+
if (state == NULL) return;
|
1347
|
+
|
1348
|
+
state->during_sample = true;
|
1349
|
+
|
1350
|
+
safely_call(thread_context_collector_sample_after_gvl_running, state->thread_context_collector_instance, state->self_instance);
|
1351
|
+
|
1352
|
+
state->stats.after_gvl_running++;
|
1353
|
+
|
1354
|
+
state->during_sample = false;
|
1355
|
+
}
|
1356
|
+
|
1357
|
+
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, VALUE instance) {
|
1358
|
+
struct cpu_and_wall_time_worker_state *state;
|
1359
|
+
TypedData_Get_Struct(instance, struct cpu_and_wall_time_worker_state, &cpu_and_wall_time_worker_typed_data, state);
|
1360
|
+
|
1361
|
+
return state->gvl_profiling_hook != NULL ? Qtrue : Qfalse;
|
1362
|
+
}
|
1363
|
+
#else
|
1364
|
+
static VALUE _native_gvl_profiling_hook_active(DDTRACE_UNUSED VALUE self, DDTRACE_UNUSED VALUE instance) {
|
1365
|
+
return Qfalse;
|
1366
|
+
}
|
1367
|
+
#endif
|
@@ -92,7 +92,7 @@ double discrete_dynamic_sampler_probability(discrete_dynamic_sampler *sampler) {
|
|
92
92
|
return sampler->sampling_probability * 100.;
|
93
93
|
}
|
94
94
|
|
95
|
-
|
95
|
+
unsigned long discrete_dynamic_sampler_events_since_last_sample(discrete_dynamic_sampler *sampler) {
|
96
96
|
return sampler->events_since_last_sample;
|
97
97
|
}
|
98
98
|
|
@@ -259,7 +259,9 @@ void discrete_dynamic_sampler_readjust(discrete_dynamic_sampler *sampler, long n
|
|
259
259
|
// are so big they don't fit into the sampling_interval. In both cases lets just disable sampling until next readjustment
|
260
260
|
// by setting interval to 0.
|
261
261
|
double sampling_interval = sampler->sampling_probability == 0 ? 0 : ceil(1.0 / sampler->sampling_probability);
|
262
|
-
|
262
|
+
// NOTE: We use UINT32_MAX instead of ULONG_MAX here to avoid clang warnings; in practice, we shouldn't ever hit
|
263
|
+
// such high sampling intervals.
|
264
|
+
sampler->sampling_interval = sampling_interval > UINT32_MAX ? 0 : sampling_interval;
|
263
265
|
|
264
266
|
#ifdef DD_DEBUG
|
265
267
|
double allocs_in_60s = sampler->events_per_ns * 1e9 * 60;
|