datadog 2.3.0 → 2.5.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 +64 -2
- 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/NativeExtensionDesign.md +3 -3
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +198 -41
- 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 +645 -107
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +15 -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 +42 -25
- 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 +194 -34
- data/ext/datadog_profiling_native_extension/heap_recorder.h +11 -0
- data/ext/datadog_profiling_native_extension/http_transport.c +38 -6
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +1 -1
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +53 -2
- 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/ruby_helpers.c +14 -11
- data/ext/datadog_profiling_native_extension/stack_recorder.c +58 -22
- data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -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 +10 -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 +14 -15
- data/lib/datadog/appsec/contrib/graphql/integration.rb +14 -1
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +7 -20
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +2 -5
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -15
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +6 -18
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +7 -20
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +5 -18
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +3 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +3 -5
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +5 -18
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +6 -10
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +7 -20
- data/lib/datadog/appsec/event.rb +25 -1
- data/lib/datadog/appsec/ext.rb +4 -0
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +3 -5
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +7 -20
- data/lib/datadog/appsec/processor/context.rb +109 -0
- 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 +42 -107
- data/lib/datadog/appsec/rate_limiter.rb +25 -40
- data/lib/datadog/appsec/remote.rb +7 -3
- data/lib/datadog/appsec/scope.rb +1 -4
- data/lib/datadog/appsec/utils/trace_operation.rb +15 -0
- data/lib/datadog/appsec/utils.rb +2 -0
- data/lib/datadog/appsec.rb +3 -2
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +26 -25
- data/lib/datadog/core/configuration/components.rb +4 -3
- data/lib/datadog/core/configuration/settings.rb +96 -5
- data/lib/datadog/core/configuration.rb +1 -3
- data/lib/datadog/core/crashtracking/component.rb +9 -6
- data/lib/datadog/core/environment/execution.rb +5 -5
- data/lib/datadog/core/environment/yjit.rb +5 -0
- 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/remote/transport/http.rb +5 -0
- data/lib/datadog/core/remote/worker.rb +1 -1
- data/lib/datadog/core/runtime/ext.rb +1 -0
- data/lib/datadog/core/runtime/metrics.rb +5 -1
- data/lib/datadog/core/semaphore.rb +35 -0
- 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/transport/ext.rb +1 -0
- data/lib/datadog/core/utils/time.rb +12 -0
- data/lib/datadog/core/workers/async.rb +1 -1
- data/lib/datadog/di/code_tracker.rb +166 -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/instrumenter.rb +301 -0
- data/lib/datadog/di/probe.rb +162 -0
- data/lib/datadog/di/probe_builder.rb +47 -0
- data/lib/datadog/di/probe_notification_builder.rb +207 -0
- data/lib/datadog/di/probe_notifier_worker.rb +244 -0
- data/lib/datadog/di/redactor.rb +188 -0
- data/lib/datadog/di/serializer.rb +215 -0
- data/lib/datadog/di/transport.rb +67 -0
- data/lib/datadog/di/utils.rb +39 -0
- data/lib/datadog/di.rb +57 -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 +32 -8
- data/lib/datadog/profiling/component.rb +21 -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 +40 -9
- data/lib/datadog/single_step_instrument.rb +12 -0
- data/lib/datadog/tracing/component.rb +13 -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/ethon/easy_patch.rb +4 -0
- data/lib/datadog/tracing/contrib/excon/middleware.rb +3 -0
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +12 -0
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +24 -2
- 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/circuit_breaker.rb +9 -0
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +22 -15
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +10 -5
- data/lib/datadog/tracing/contrib/httpclient/patcher.rb +1 -14
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +9 -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/rails/runner.rb +1 -1
- data/lib/datadog/tracing/contrib/redis/tags.rb +4 -0
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +3 -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 +15 -9
- 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 +29 -22
- data/lib/datadog/tracing/transport/http/client.rb +1 -0
- data/lib/datadog/tracing/transport/http.rb +4 -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 +2 -2
- data/lib/datadog/tracing/writer.rb +26 -28
- data/lib/datadog/version.rb +1 -1
- metadata +40 -15
- data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
@@ -3,6 +3,8 @@
|
|
3
3
|
#include <ruby.h>
|
4
4
|
#include <stdbool.h>
|
5
5
|
|
6
|
+
#include "gvl_profiling_helper.h"
|
7
|
+
|
6
8
|
void thread_context_collector_sample(
|
7
9
|
VALUE self_instance,
|
8
10
|
long current_monotonic_wall_time_ns,
|
@@ -12,5 +14,17 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
12
14
|
void thread_context_collector_sample_skipped_allocation_samples(VALUE self_instance, unsigned int skipped_samples);
|
13
15
|
VALUE thread_context_collector_sample_after_gc(VALUE self_instance);
|
14
16
|
void thread_context_collector_on_gc_start(VALUE self_instance);
|
15
|
-
bool thread_context_collector_on_gc_finish(VALUE self_instance);
|
17
|
+
__attribute__((warn_unused_result)) bool thread_context_collector_on_gc_finish(VALUE self_instance);
|
16
18
|
VALUE enforce_thread_context_collector_instance(VALUE object);
|
19
|
+
|
20
|
+
#ifndef NO_GVL_INSTRUMENTATION
|
21
|
+
typedef enum {
|
22
|
+
ON_GVL_RUNNING_UNKNOWN, // Thread is not known, it may not even be from the current Ractor
|
23
|
+
ON_GVL_RUNNING_DONT_SAMPLE, // Thread is known, but "Waiting for GVL" period was too small to be sampled
|
24
|
+
ON_GVL_RUNNING_SAMPLE, // Thread is known, and "Waiting for GVL" period should be sampled
|
25
|
+
} on_gvl_running_result;
|
26
|
+
|
27
|
+
void thread_context_collector_on_gvl_waiting(gvl_profiling_thread thread);
|
28
|
+
__attribute__((warn_unused_result)) on_gvl_running_result thread_context_collector_on_gvl_running(gvl_profiling_thread thread);
|
29
|
+
VALUE thread_context_collector_sample_after_gvl_running(VALUE self_instance, VALUE current_thread, long current_monotonic_wall_time_ns);
|
30
|
+
#endif
|
@@ -28,33 +28,6 @@ VALUE datadog_gem_version(void) {
|
|
28
28
|
return version_string;
|
29
29
|
}
|
30
30
|
|
31
|
-
__attribute__((warn_unused_result))
|
32
|
-
ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration) {
|
33
|
-
ENFORCE_TYPE(exporter_configuration, T_ARRAY);
|
34
|
-
|
35
|
-
VALUE exporter_working_mode = rb_ary_entry(exporter_configuration, 0);
|
36
|
-
ENFORCE_TYPE(exporter_working_mode, T_SYMBOL);
|
37
|
-
ID working_mode = SYM2ID(exporter_working_mode);
|
38
|
-
|
39
|
-
ID agentless_id = rb_intern("agentless");
|
40
|
-
ID agent_id = rb_intern("agent");
|
41
|
-
|
42
|
-
if (working_mode != agentless_id && working_mode != agent_id) {
|
43
|
-
rb_raise(rb_eArgError, "Failed to initialize transport: Unexpected working mode, expected :agentless or :agent");
|
44
|
-
}
|
45
|
-
|
46
|
-
if (working_mode == agentless_id) {
|
47
|
-
VALUE site = rb_ary_entry(exporter_configuration, 1);
|
48
|
-
VALUE api_key = rb_ary_entry(exporter_configuration, 2);
|
49
|
-
|
50
|
-
return ddog_prof_Endpoint_agentless(char_slice_from_ruby_string(site), char_slice_from_ruby_string(api_key));
|
51
|
-
} else { // agent_id
|
52
|
-
VALUE base_url = rb_ary_entry(exporter_configuration, 1);
|
53
|
-
|
54
|
-
return ddog_prof_Endpoint_agent(char_slice_from_ruby_string(base_url));
|
55
|
-
}
|
56
|
-
}
|
57
|
-
|
58
31
|
static VALUE log_failure_to_process_tag(VALUE err_details) {
|
59
32
|
VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
|
60
33
|
VALUE logger = rb_funcall(datadog_module, rb_intern("logger"), 0);
|
@@ -27,7 +27,6 @@
|
|
27
27
|
#define ENFORCE_BOOLEAN(value) \
|
28
28
|
{ if (RB_UNLIKELY(value != Qtrue && value != Qfalse)) raise_unexpected_type(value, ADD_QUOTES(value), "true or false", __FILE__, __LINE__, __func__); }
|
29
29
|
|
30
|
-
// Called by ENFORCE_TYPE; should not be used directly
|
31
30
|
NORETURN(void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name));
|
32
31
|
|
33
32
|
// Helper to retrieve Datadog::VERSION::STRING
|
@@ -39,9 +38,6 @@ static inline ddog_CharSlice char_slice_from_ruby_string(VALUE string) {
|
|
39
38
|
return char_slice;
|
40
39
|
}
|
41
40
|
|
42
|
-
__attribute__((warn_unused_result))
|
43
|
-
ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration);
|
44
|
-
|
45
41
|
__attribute__((warn_unused_result))
|
46
42
|
ddog_Vec_Tag convert_tags(VALUE tags_as_array);
|
47
43
|
|
@@ -74,35 +74,25 @@ Logging.message("[datadog] Using compiler:\n")
|
|
74
74
|
xsystem("#{CONFIG["CC"]} -v")
|
75
75
|
Logging.message("[datadog] End of compiler information\n")
|
76
76
|
|
77
|
-
# mkmf on modern Rubies actually has an append_cflags that does something similar
|
78
|
-
# (see https://github.com/ruby/ruby/pull/5760), but as usual we need a bit more boilerplate to deal with legacy Rubies
|
79
|
-
def add_compiler_flag(flag)
|
80
|
-
if try_cflags(flag)
|
81
|
-
$CFLAGS << " " << flag
|
82
|
-
else
|
83
|
-
$stderr.puts("WARNING: '#{flag}' not accepted by compiler, skipping it")
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
77
|
# Because we can't control what compiler versions our customers use, shipping with -Werror by default is a no-go.
|
88
78
|
# But we can enable it in CI, so that we quickly spot any new warnings that just got introduced.
|
89
|
-
|
79
|
+
append_cflags "-Werror" if ENV["DATADOG_GEM_CI"] == "true"
|
90
80
|
|
91
81
|
# Older gcc releases may not default to C99 and we need to ask for this. This is also used:
|
92
82
|
# * by upstream Ruby -- search for gnu99 in the codebase
|
93
83
|
# * by msgpack, another datadog gem dependency
|
94
84
|
# (https://github.com/msgpack/msgpack-ruby/blob/18ce08f6d612fe973843c366ac9a0b74c4e50599/ext/msgpack/extconf.rb#L8)
|
95
|
-
|
85
|
+
append_cflags "-std=gnu99"
|
96
86
|
|
97
87
|
# Gets really noisy when we include the MJIT header, let's omit it (TODO: Use #pragma GCC diagnostic instead?)
|
98
|
-
|
88
|
+
append_cflags "-Wno-unused-function"
|
99
89
|
|
100
90
|
# Allow defining variables at any point in a function
|
101
|
-
|
91
|
+
append_cflags "-Wno-declaration-after-statement"
|
102
92
|
|
103
93
|
# If we forget to include a Ruby header, the function call may still appear to work, but then
|
104
94
|
# cause a segfault later. Let's ensure that never happens.
|
105
|
-
|
95
|
+
append_cflags "-Werror-implicit-function-declaration"
|
106
96
|
|
107
97
|
# The native extension is not intended to expose any symbols/functions for other native libraries to use;
|
108
98
|
# the sole exception being `Init_datadog_profiling_native_extension` which needs to be visible for Ruby to call it when
|
@@ -110,14 +100,14 @@ add_compiler_flag "-Werror-implicit-function-declaration"
|
|
110
100
|
#
|
111
101
|
# By setting this compiler flag, we tell it to assume that everything is private unless explicitly stated.
|
112
102
|
# For more details see https://gcc.gnu.org/wiki/Visibility
|
113
|
-
|
103
|
+
append_cflags "-fvisibility=hidden"
|
114
104
|
|
115
105
|
# Avoid legacy C definitions
|
116
|
-
|
106
|
+
append_cflags "-Wold-style-definition"
|
117
107
|
|
118
108
|
# Enable all other compiler warnings
|
119
|
-
|
120
|
-
|
109
|
+
append_cflags "-Wall"
|
110
|
+
append_cflags "-Wextra"
|
121
111
|
|
122
112
|
if ENV["DDTRACE_DEBUG"] == "true"
|
123
113
|
$defs << "-DDD_DEBUG"
|
@@ -153,6 +143,12 @@ $defs << "-DNO_RACTOR_HEADER_INCLUDE" if RUBY_VERSION < "3.3"
|
|
153
143
|
# On older Rubies, some of the Ractor internal APIs were directly accessible
|
154
144
|
$defs << "-DUSE_RACTOR_INTERNAL_APIS_DIRECTLY" if RUBY_VERSION < "3.3"
|
155
145
|
|
146
|
+
# On older Rubies, there was no GVL instrumentation API and APIs created to support it
|
147
|
+
$defs << "-DNO_GVL_INSTRUMENTATION" if RUBY_VERSION < "3.2"
|
148
|
+
|
149
|
+
# Supporting GVL instrumentation on 3.2 needs some workarounds
|
150
|
+
$defs << "-DUSE_GVL_PROFILING_3_2_WORKAROUNDS" if RUBY_VERSION.start_with?("3.2")
|
151
|
+
|
156
152
|
# On older Rubies, there was no struct rb_native_thread. See private_vm_api_acccess.c for details.
|
157
153
|
$defs << "-DNO_RB_NATIVE_THREAD" if RUBY_VERSION < "3.2"
|
158
154
|
|
@@ -255,21 +251,42 @@ if Datadog::Profiling::NativeExtensionHelpers::CAN_USE_MJIT_HEADER
|
|
255
251
|
|
256
252
|
# Warn on unused parameters to functions. Use `DDTRACE_UNUSED` to mark things as known-to-not-be-used.
|
257
253
|
# See the comment on the same flag below for why this is done last.
|
258
|
-
|
254
|
+
append_cflags "-Wunused-parameter"
|
259
255
|
|
260
256
|
create_makefile EXTENSION_NAME
|
261
257
|
else
|
262
258
|
# The MJIT header was introduced on 2.6 and removed on 3.3; for other Rubies we rely on
|
263
|
-
# the
|
259
|
+
# the datadog-ruby_core_source gem to get access to private VM headers.
|
264
260
|
# This gem ships source code copies of these VM headers for the different Ruby VM versions;
|
265
|
-
# see https://github.com/
|
261
|
+
# see https://github.com/DataDog/datadog-ruby_core_source for details
|
266
262
|
|
267
263
|
create_header
|
268
264
|
|
269
|
-
require "
|
265
|
+
require "datadog/ruby_core_source"
|
270
266
|
dir_config("ruby") # allow user to pass in non-standard core include directory
|
271
267
|
|
272
|
-
|
268
|
+
# This is a workaround for a weird issue...
|
269
|
+
#
|
270
|
+
# The mkmf tool defines a `with_cppflags` helper that datadog-ruby_core_source uses. This helper temporarily
|
271
|
+
# replaces `$CPPFLAGS` (aka the C pre-processor [not c++!] flags) with a different set when doing something.
|
272
|
+
#
|
273
|
+
# The datadog-ruby_core_source gem uses `with_cppflags` during makefile generation to inject extra headers into the
|
274
|
+
# path. But because `with_cppflags` replaces `$CPPFLAGS`, well, the default `$CPPFLAGS` are not included in the
|
275
|
+
# makefile.
|
276
|
+
#
|
277
|
+
# This is a problem because the default `$CPPFLAGS` carries configuration that was set when Ruby was being built.
|
278
|
+
# Thus, if we ignore it, we don't compile the profiler with the exact same configuration as Ruby.
|
279
|
+
# In practice, this can generate crashes and weird bugs if the Ruby configuration is tweaked in a manner that
|
280
|
+
# changes some of the internal structures that the profiler relies on. Concretely, setting for instance
|
281
|
+
# `VM_CHECK_MODE=1` when building Ruby will trigger this issue (because somethings in structures the profiler reads
|
282
|
+
# are ifdef'd out using this setting).
|
283
|
+
#
|
284
|
+
# To workaround this issue, we override `with_cppflags` for datadog-ruby_core_source to still include `$CPPFLAGS`.
|
285
|
+
Datadog::RubyCoreSource.define_singleton_method(:with_cppflags) do |newflags, &block|
|
286
|
+
super("#{newflags} #{$CPPFLAGS}", &block)
|
287
|
+
end
|
288
|
+
|
289
|
+
Datadog::RubyCoreSource
|
273
290
|
.create_makefile_with_core(
|
274
291
|
proc do
|
275
292
|
headers_available =
|
@@ -282,7 +299,7 @@ else
|
|
282
299
|
# This is added as late as possible because in some Rubies we support (e.g. 3.3), adding this flag before
|
283
300
|
# checking if internal VM headers are available causes those checks to fail because of this warning (and not
|
284
301
|
# because the headers are not available.)
|
285
|
-
|
302
|
+
append_cflags "-Wunused-parameter"
|
286
303
|
end
|
287
304
|
|
288
305
|
headers_available
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <ruby/thread.h>
|
3
|
+
#include "gvl_profiling_helper.h"
|
4
|
+
|
5
|
+
#if !defined(NO_GVL_INSTRUMENTATION) && !defined(USE_GVL_PROFILING_3_2_WORKAROUNDS) // Ruby 3.3+
|
6
|
+
rb_internal_thread_specific_key_t gvl_waiting_tls_key;
|
7
|
+
|
8
|
+
void gvl_profiling_init(void) {
|
9
|
+
gvl_waiting_tls_key = rb_internal_thread_specific_key_create();
|
10
|
+
}
|
11
|
+
|
12
|
+
#endif
|
13
|
+
|
14
|
+
#ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS // Ruby 3.2
|
15
|
+
__thread gvl_profiling_thread gvl_waiting_tls;
|
16
|
+
static bool gvl_profiling_state_thread_tracking_workaround_installed = false;
|
17
|
+
|
18
|
+
static void on_thread_start(
|
19
|
+
DDTRACE_UNUSED rb_event_flag_t _unused1,
|
20
|
+
DDTRACE_UNUSED const rb_internal_thread_event_data_t *_unused2,
|
21
|
+
DDTRACE_UNUSED void *_unused3
|
22
|
+
) {
|
23
|
+
gvl_waiting_tls = (gvl_profiling_thread) {.thread = NULL};
|
24
|
+
}
|
25
|
+
|
26
|
+
// Hack: We're using the gvl_waiting_tls native thread-local to store per-thread information. Unfortunately, Ruby puts a big hole
|
27
|
+
// in our plan because it reuses native threads -- specifically, in Ruby 3.2, native threads are still 1:1 to Ruby
|
28
|
+
// threads (M:N wasn't a thing yet) BUT once a Ruby thread dies, the VM will keep the native thread around for a
|
29
|
+
// bit, and if another Ruby thread starts right after, Ruby will reuse the native thread, rather than create a new one.
|
30
|
+
//
|
31
|
+
// This will mean that the new Ruby thread will still have the same native thread-local data that we set on the
|
32
|
+
// old thread. For the purposes of our tracking, where we're keeping a pointer to the current thread object in
|
33
|
+
// thread-local storage **this is disastrous** since it means we'll be pointing at the wrong thread (and its
|
34
|
+
// memory may have been freed or reused since!)
|
35
|
+
//
|
36
|
+
// To work around this issue, once GVL profiling is enabled, we install an event hook on thread start
|
37
|
+
// events that clears the thread-local data. This guarantees that there will be no stale data -- any existing
|
38
|
+
// data will be cleared at thread start.
|
39
|
+
//
|
40
|
+
// Note that once installed, this event hook becomes permanent -- stopping the profiler does not stop this event
|
41
|
+
// hook, unlike all others. This is because we can't afford to miss any thread start events while the
|
42
|
+
// profiler is stopped (e.g. during reconfiguration) as that would mean stale data once the profiler starts again.
|
43
|
+
void gvl_profiling_state_thread_tracking_workaround(void) {
|
44
|
+
if (gvl_profiling_state_thread_tracking_workaround_installed) return;
|
45
|
+
|
46
|
+
rb_internal_thread_add_event_hook(on_thread_start, RUBY_INTERNAL_THREAD_EVENT_STARTED, NULL);
|
47
|
+
|
48
|
+
gvl_profiling_state_thread_tracking_workaround_installed = true;
|
49
|
+
}
|
50
|
+
#endif
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
// This helper is used by the Datadog::Profiling::Collectors::ThreadContext to store data used when profiling the GVL.
|
4
|
+
// It's tested through that class' interfaces.
|
5
|
+
// ---
|
6
|
+
|
7
|
+
#include "extconf.h"
|
8
|
+
|
9
|
+
#if !defined(NO_GVL_INSTRUMENTATION) && !defined(USE_GVL_PROFILING_3_2_WORKAROUNDS) // Ruby 3.3+
|
10
|
+
#include <ruby.h>
|
11
|
+
#include <ruby/thread.h>
|
12
|
+
#include "datadog_ruby_common.h"
|
13
|
+
|
14
|
+
typedef struct { VALUE thread; } gvl_profiling_thread;
|
15
|
+
extern rb_internal_thread_specific_key_t gvl_waiting_tls_key;
|
16
|
+
|
17
|
+
void gvl_profiling_init(void);
|
18
|
+
|
19
|
+
static inline gvl_profiling_thread thread_from_thread_object(VALUE thread) {
|
20
|
+
return (gvl_profiling_thread) {.thread = thread};
|
21
|
+
}
|
22
|
+
|
23
|
+
static inline gvl_profiling_thread thread_from_event(const rb_internal_thread_event_data_t *event_data) {
|
24
|
+
return thread_from_thread_object(event_data->thread);
|
25
|
+
}
|
26
|
+
|
27
|
+
static inline intptr_t gvl_profiling_state_get(gvl_profiling_thread thread) {
|
28
|
+
return (intptr_t) rb_internal_thread_specific_get(thread.thread, gvl_waiting_tls_key);
|
29
|
+
}
|
30
|
+
|
31
|
+
static inline void gvl_profiling_state_set(gvl_profiling_thread thread, intptr_t value) {
|
32
|
+
rb_internal_thread_specific_set(thread.thread, gvl_waiting_tls_key, (void *) value);
|
33
|
+
}
|
34
|
+
#endif
|
35
|
+
|
36
|
+
#ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS // Ruby 3.2
|
37
|
+
typedef struct { void *thread; } gvl_profiling_thread;
|
38
|
+
extern __thread gvl_profiling_thread gvl_waiting_tls;
|
39
|
+
|
40
|
+
static inline void gvl_profiling_init(void) { }
|
41
|
+
|
42
|
+
// This header gets included in private_vm_access.c which can't include datadog_ruby_common.h so we replicate this
|
43
|
+
// helper here
|
44
|
+
#ifdef __GNUC__
|
45
|
+
#define DDTRACE_UNUSED __attribute__((unused))
|
46
|
+
#else
|
47
|
+
#define DDTRACE_UNUSED
|
48
|
+
#endif
|
49
|
+
|
50
|
+
// NOTE: This is a hack that relies on the knowledge that on Ruby 3.2 the
|
51
|
+
// RUBY_INTERNAL_THREAD_EVENT_READY and RUBY_INTERNAL_THREAD_EVENT_RESUMED events always get called on the thread they
|
52
|
+
// are about. Thus, we can use our thread local storage hack to get this data, even though the event doesn't include it.
|
53
|
+
static inline gvl_profiling_thread thread_from_event(DDTRACE_UNUSED const void *event_data) {
|
54
|
+
return gvl_waiting_tls;
|
55
|
+
}
|
56
|
+
|
57
|
+
void gvl_profiling_state_thread_tracking_workaround(void);
|
58
|
+
gvl_profiling_thread gvl_profiling_state_maybe_initialize(void);
|
59
|
+
|
60
|
+
// Implementing these on Ruby 3.2 requires access to private VM things, so the following methods are
|
61
|
+
// implemented in `private_vm_api_access.c`
|
62
|
+
gvl_profiling_thread thread_from_thread_object(VALUE thread);
|
63
|
+
intptr_t gvl_profiling_state_get(gvl_profiling_thread thread);
|
64
|
+
void gvl_profiling_state_set(gvl_profiling_thread thread, intptr_t value);
|
65
|
+
#endif
|
66
|
+
|
67
|
+
#ifndef NO_GVL_INSTRUMENTATION // For all Rubies supporting GVL profiling (3.2+)
|
68
|
+
static inline intptr_t gvl_profiling_state_thread_object_get(VALUE thread) {
|
69
|
+
return gvl_profiling_state_get(thread_from_thread_object(thread));
|
70
|
+
}
|
71
|
+
|
72
|
+
static inline void gvl_profiling_state_thread_object_set(VALUE thread, intptr_t value) {
|
73
|
+
gvl_profiling_state_set(thread_from_thread_object(thread), value);
|
74
|
+
}
|
75
|
+
#endif
|