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
@@ -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,11 @@ 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
|
+
void thread_context_collector_on_gvl_waiting(gvl_profiling_thread thread);
|
22
|
+
__attribute__((warn_unused_result)) bool thread_context_collector_on_gvl_running(gvl_profiling_thread thread);
|
23
|
+
VALUE thread_context_collector_sample_after_gvl_running(VALUE self_instance);
|
24
|
+
#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,7 +251,7 @@ 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
|
@@ -269,6 +265,27 @@ else
|
|
269
265
|
require "debase/ruby_core_source"
|
270
266
|
dir_config("ruby") # allow user to pass in non-standard core include directory
|
271
267
|
|
268
|
+
# This is a workaround for a weird issue...
|
269
|
+
#
|
270
|
+
# The mkmf tool defines a `with_cppflags` helper that debase-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 debase-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 debase-ruby_core_source to still include `$CPPFLAGS`.
|
285
|
+
Debase::RubyCoreSource.define_singleton_method(:with_cppflags) do |newflags, &block|
|
286
|
+
super("#{newflags} #{$CPPFLAGS}", &block)
|
287
|
+
end
|
288
|
+
|
272
289
|
Debase::RubyCoreSource
|
273
290
|
.create_makefile_with_core(
|
274
291
|
proc do
|
@@ -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
|
@@ -632,12 +632,14 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
|
|
632
632
|
ddog_prof_Location *locations = recorder->reusable_locations;
|
633
633
|
for (uint16_t i = 0; i < stack->frames_len; i++) {
|
634
634
|
const heap_frame *frame = &stack->frames[i];
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
635
|
+
locations[i] = (ddog_prof_Location) {
|
636
|
+
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C("")},
|
637
|
+
.function = {
|
638
|
+
.name = {.ptr = frame->name, .len = strlen(frame->name)},
|
639
|
+
.filename = {.ptr = frame->filename, .len = strlen(frame->filename)},
|
640
|
+
},
|
641
|
+
.line = frame->line,
|
642
|
+
};
|
641
643
|
}
|
642
644
|
|
643
645
|
heap_recorder_iteration_data iteration_data;
|
@@ -782,8 +784,20 @@ static void cleanup_heap_record_if_unused(heap_recorder *heap_recorder, heap_rec
|
|
782
784
|
}
|
783
785
|
|
784
786
|
static void on_committed_object_record_cleanup(heap_recorder *heap_recorder, object_record *record) {
|
787
|
+
// @ivoanjo: We've seen a segfault crash in the field in this function (October 2024) which we're still trying to investigate.
|
788
|
+
// (See PROF-10656 Datadog-internal for details). Just in case, I've sprinkled a bunch of NULL tests in this function for now.
|
789
|
+
// Once we figure out the issue we can get rid of them again.
|
790
|
+
|
791
|
+
if (heap_recorder == NULL) rb_raise(rb_eRuntimeError, "heap_recorder was NULL in on_committed_object_record_cleanup");
|
792
|
+
if (heap_recorder->heap_records == NULL) rb_raise(rb_eRuntimeError, "heap_recorder->heap_records was NULL in on_committed_object_record_cleanup");
|
793
|
+
if (record == NULL) rb_raise(rb_eRuntimeError, "record was NULL in on_committed_object_record_cleanup");
|
794
|
+
|
785
795
|
// Starting with the associated heap record. There will now be one less tracked object pointing to it
|
786
796
|
heap_record *heap_record = record->heap_record;
|
797
|
+
|
798
|
+
if (heap_record == NULL) rb_raise(rb_eRuntimeError, "heap_record was NULL in on_committed_object_record_cleanup");
|
799
|
+
if (heap_record->stack == NULL) rb_raise(rb_eRuntimeError, "heap_record->stack was NULL in on_committed_object_record_cleanup");
|
800
|
+
|
787
801
|
heap_record->num_tracked_objects--;
|
788
802
|
|
789
803
|
// One less object using this heap record, it may have become unused...
|
@@ -77,6 +77,32 @@ static VALUE _native_validate_exporter(DDTRACE_UNUSED VALUE _self, VALUE exporte
|
|
77
77
|
return rb_ary_new_from_args(2, ok_symbol, Qnil);
|
78
78
|
}
|
79
79
|
|
80
|
+
static ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration) {
|
81
|
+
ENFORCE_TYPE(exporter_configuration, T_ARRAY);
|
82
|
+
|
83
|
+
VALUE exporter_working_mode = rb_ary_entry(exporter_configuration, 0);
|
84
|
+
ENFORCE_TYPE(exporter_working_mode, T_SYMBOL);
|
85
|
+
ID working_mode = SYM2ID(exporter_working_mode);
|
86
|
+
|
87
|
+
ID agentless_id = rb_intern("agentless");
|
88
|
+
ID agent_id = rb_intern("agent");
|
89
|
+
|
90
|
+
if (working_mode != agentless_id && working_mode != agent_id) {
|
91
|
+
rb_raise(rb_eArgError, "Failed to initialize transport: Unexpected working mode, expected :agentless or :agent");
|
92
|
+
}
|
93
|
+
|
94
|
+
if (working_mode == agentless_id) {
|
95
|
+
VALUE site = rb_ary_entry(exporter_configuration, 1);
|
96
|
+
VALUE api_key = rb_ary_entry(exporter_configuration, 2);
|
97
|
+
|
98
|
+
return ddog_prof_Endpoint_agentless(char_slice_from_ruby_string(site), char_slice_from_ruby_string(api_key));
|
99
|
+
} else { // agent_id
|
100
|
+
VALUE base_url = rb_ary_entry(exporter_configuration, 1);
|
101
|
+
|
102
|
+
return ddog_prof_Endpoint_agent(char_slice_from_ruby_string(base_url));
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
80
106
|
static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array) {
|
81
107
|
ENFORCE_TYPE(exporter_configuration, T_ARRAY);
|
82
108
|
ENFORCE_TYPE(tags_as_array, T_ARRAY);
|
@@ -115,8 +141,7 @@ static VALUE perform_export(
|
|
115
141
|
ddog_prof_Exporter_Slice_File files_to_export_unmodified,
|
116
142
|
ddog_Vec_Tag *additional_tags,
|
117
143
|
ddog_CharSlice internal_metadata,
|
118
|
-
ddog_CharSlice info
|
119
|
-
uint64_t timeout_milliseconds
|
144
|
+
ddog_CharSlice info
|
120
145
|
) {
|
121
146
|
ddog_prof_ProfiledEndpointsStats *endpoints_stats = NULL; // Not in use yet
|
122
147
|
ddog_prof_Exporter_Request_BuildResult build_result = ddog_prof_Exporter_Request_build(
|
@@ -128,8 +153,7 @@ static VALUE perform_export(
|
|
128
153
|
additional_tags,
|
129
154
|
endpoints_stats,
|
130
155
|
&internal_metadata,
|
131
|
-
&info
|
132
|
-
timeout_milliseconds
|
156
|
+
&info
|
133
157
|
);
|
134
158
|
|
135
159
|
if (build_result.tag == DDOG_PROF_EXPORTER_REQUEST_BUILD_RESULT_ERR) {
|
@@ -254,6 +278,15 @@ static VALUE _native_do_export(
|
|
254
278
|
VALUE failure_tuple = handle_exporter_failure(exporter_result);
|
255
279
|
if (!NIL_P(failure_tuple)) return failure_tuple;
|
256
280
|
|
281
|
+
ddog_prof_MaybeError timeout_result = ddog_prof_Exporter_set_timeout(exporter_result.ok, timeout_milliseconds);
|
282
|
+
if (timeout_result.tag == DDOG_PROF_OPTION_ERROR_SOME_ERROR) {
|
283
|
+
// NOTE: Seems a bit harsh to fail the upload if we can't set a timeout. OTOH, this is only expected to fail
|
284
|
+
// if the exporter is not well built. Because such a situation should already be caught above I think it's
|
285
|
+
// preferable to leave this here as a virtually unreachable exception rather than ignoring it.
|
286
|
+
ddog_prof_Exporter_drop(exporter_result.ok);
|
287
|
+
return rb_ary_new_from_args(2, error_symbol, get_error_details_and_drop(&timeout_result.some));
|
288
|
+
}
|
289
|
+
|
257
290
|
return perform_export(
|
258
291
|
exporter_result.ok,
|
259
292
|
start,
|
@@ -262,8 +295,7 @@ static VALUE _native_do_export(
|
|
262
295
|
files_to_export_unmodified,
|
263
296
|
null_additional_tags,
|
264
297
|
internal_metadata,
|
265
|
-
info
|
266
|
-
timeout_milliseconds
|
298
|
+
info
|
267
299
|
);
|
268
300
|
}
|
269
301
|
|
@@ -182,7 +182,7 @@ uint64_t native_thread_id_for(VALUE thread) {
|
|
182
182
|
#if !defined(NO_THREAD_TID) && defined(RB_THREAD_T_HAS_NATIVE_ID)
|
183
183
|
#ifndef NO_RB_NATIVE_THREAD
|
184
184
|
struct rb_native_thread* native_thread = thread_struct_from_object(thread)->nt;
|
185
|
-
if (native_thread == NULL)
|
185
|
+
if (native_thread == NULL) return 0;
|
186
186
|
return native_thread->tid;
|
187
187
|
#else
|
188
188
|
return thread_struct_from_object(thread)->tid;
|
@@ -755,3 +755,54 @@ static inline int ddtrace_imemo_type(VALUE imemo) {
|
|
755
755
|
return GET_VM()->objspace;
|
756
756
|
}
|
757
757
|
#endif
|
758
|
+
|
759
|
+
#ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS // Ruby 3.2
|
760
|
+
#include "gvl_profiling_helper.h"
|
761
|
+
|
762
|
+
gvl_profiling_thread thread_from_thread_object(VALUE thread) {
|
763
|
+
return (gvl_profiling_thread) {.thread = thread_struct_from_object(thread)};
|
764
|
+
}
|
765
|
+
|
766
|
+
// Hack: In Ruby 3.3+ we attach gvl profiling state to Ruby threads using the
|
767
|
+
// rb_internal_thread_specific_* APIs. These APIs did not exist on Ruby 3.2. On Ruby 3.2 we instead store the
|
768
|
+
// needed data inside the `rb_thread_t` structure, specifically in `stat_insn_usage` as a Ruby FIXNUM.
|
769
|
+
//
|
770
|
+
// Why `stat_insn_usage`? We needed some per-thread storage, and while looking at the Ruby VM sources I noticed
|
771
|
+
// that `stat_insn_usage` has been in `rb_thread_t` for a long time, but is not used anywhere in the VM
|
772
|
+
// code. There's a comment attached to it "/* statistics data for profiler */" but other than marking this
|
773
|
+
// field for GC, I could not find any place in the VM commit history or on GitHub where this has ever been used.
|
774
|
+
//
|
775
|
+
// Thus, since this hack is only for 3.2, which presumably will never see this field either removed or used
|
776
|
+
// during its remaining maintenance release period we... kinda take it for our own usage. It's ugly, I know...
|
777
|
+
intptr_t gvl_profiling_state_get(gvl_profiling_thread thread) {
|
778
|
+
if (thread.thread == NULL) return 0;
|
779
|
+
|
780
|
+
VALUE current_value = ((rb_thread_t *)thread.thread)->stat_insn_usage;
|
781
|
+
intptr_t result = current_value == Qnil ? 0 : FIX2LONG(current_value);
|
782
|
+
return result;
|
783
|
+
}
|
784
|
+
|
785
|
+
void gvl_profiling_state_set(gvl_profiling_thread thread, intptr_t value) {
|
786
|
+
if (thread.thread == NULL) return;
|
787
|
+
((rb_thread_t *)thread.thread)->stat_insn_usage = LONG2FIX(value);
|
788
|
+
}
|
789
|
+
|
790
|
+
// Because Ruby 3.2 does not give us the current thread when calling the RUBY_INTERNAL_THREAD_EVENT_READY and
|
791
|
+
// RUBY_INTERNAL_THREAD_EVENT_RESUMED APIs, we need to figure out this info ourselves.
|
792
|
+
//
|
793
|
+
// Specifically, this method was created to be called from a RUBY_INTERNAL_THREAD_EVENT_RESUMED callback --
|
794
|
+
// when it's triggered, we know the thread the code gets executed on is holding the GVL, so we use this
|
795
|
+
// opportunity to initialize our thread-local value.
|
796
|
+
gvl_profiling_thread gvl_profiling_state_maybe_initialize(void) {
|
797
|
+
gvl_profiling_thread current_thread = gvl_waiting_tls;
|
798
|
+
|
799
|
+
if (current_thread.thread == NULL) {
|
800
|
+
// threads.sched.running is the thread currently holding the GVL, which when this gets executed is the
|
801
|
+
// current thread!
|
802
|
+
current_thread = (gvl_profiling_thread) {.thread = (void *) rb_current_ractor()->threads.sched.running};
|
803
|
+
gvl_waiting_tls = current_thread;
|
804
|
+
}
|
805
|
+
|
806
|
+
return current_thread;
|
807
|
+
}
|
808
|
+
#endif
|
@@ -65,3 +65,6 @@ const char *imemo_kind(VALUE imemo);
|
|
65
65
|
#ifdef NO_POSTPONED_TRIGGER
|
66
66
|
void *objspace_ptr_for_gc_finalize_deferred_workaround(void);
|
67
67
|
#endif
|
68
|
+
|
69
|
+
#define ENFORCE_THREAD(value) \
|
70
|
+
{ if (RB_UNLIKELY(!rb_typeddata_is_kind_of(value, RTYPEDDATA_TYPE(rb_thread_current())))) raise_unexpected_type(value, ADD_QUOTES(value), "Thread", __FILE__, __LINE__, __func__); }
|
@@ -253,7 +253,7 @@ static VALUE _native_enforce_success(DDTRACE_UNUSED VALUE _self, VALUE syserr_er
|
|
253
253
|
|
254
254
|
static void *trigger_enforce_success(void *trigger_args) {
|
255
255
|
intptr_t syserr_errno = (intptr_t) trigger_args;
|
256
|
-
ENFORCE_SUCCESS_NO_GVL(syserr_errno);
|
256
|
+
ENFORCE_SUCCESS_NO_GVL((int) syserr_errno);
|
257
257
|
return NULL;
|
258
258
|
}
|
259
259
|
|
@@ -19,6 +19,7 @@ typedef struct sample_labels {
|
|
19
19
|
// This is used to allow the `Collectors::Stack` to modify the existing label, if any. This MUST be NULL or point
|
20
20
|
// somewhere inside the labels slice above.
|
21
21
|
ddog_prof_Label *state_label;
|
22
|
+
bool is_gvl_waiting_state;
|
22
23
|
|
23
24
|
int64_t end_timestamp_ns;
|
24
25
|
} sample_labels;
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#include <ruby.h>
|
2
|
-
#include <datadog/
|
2
|
+
#include <datadog/crashtracker.h>
|
3
3
|
|
4
4
|
#include "datadog_ruby_common.h"
|
5
5
|
|
@@ -28,8 +28,9 @@ void crashtracker_init(VALUE crashtracking_module) {
|
|
28
28
|
static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self) {
|
29
29
|
VALUE options;
|
30
30
|
rb_scan_args(argc, argv, "0:", &options);
|
31
|
+
if (options == Qnil) options = rb_hash_new();
|
31
32
|
|
32
|
-
VALUE
|
33
|
+
VALUE agent_base_url = rb_hash_fetch(options, ID2SYM(rb_intern("agent_base_url")));
|
33
34
|
VALUE path_to_crashtracking_receiver_binary = rb_hash_fetch(options, ID2SYM(rb_intern("path_to_crashtracking_receiver_binary")));
|
34
35
|
VALUE ld_library_path = rb_hash_fetch(options, ID2SYM(rb_intern("ld_library_path")));
|
35
36
|
VALUE tags_as_array = rb_hash_fetch(options, ID2SYM(rb_intern("tags_as_array")));
|
@@ -39,7 +40,7 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
39
40
|
VALUE start_action = ID2SYM(rb_intern("start"));
|
40
41
|
VALUE update_on_fork_action = ID2SYM(rb_intern("update_on_fork"));
|
41
42
|
|
42
|
-
ENFORCE_TYPE(
|
43
|
+
ENFORCE_TYPE(agent_base_url, T_STRING);
|
43
44
|
ENFORCE_TYPE(tags_as_array, T_ARRAY);
|
44
45
|
ENFORCE_TYPE(path_to_crashtracking_receiver_binary, T_STRING);
|
45
46
|
ENFORCE_TYPE(ld_library_path, T_STRING);
|
@@ -49,13 +50,13 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
49
50
|
if (action != start_action && action != update_on_fork_action) rb_raise(rb_eArgError, "Unexpected action: %+"PRIsVALUE, action);
|
50
51
|
|
51
52
|
VALUE version = datadog_gem_version();
|
52
|
-
ddog_prof_Endpoint endpoint = endpoint_from(exporter_configuration);
|
53
53
|
|
54
|
-
// Tags are heap-allocated, so after here we can't raise exceptions otherwise we'll leak this memory
|
54
|
+
// Tags and endpoint are heap-allocated, so after here we can't raise exceptions otherwise we'll leak this memory
|
55
55
|
// Start of exception-free zone to prevent leaks {{
|
56
|
+
ddog_Endpoint *endpoint = ddog_endpoint_from_url(char_slice_from_ruby_string(agent_base_url));
|
56
57
|
ddog_Vec_Tag tags = convert_tags(tags_as_array);
|
57
58
|
|
58
|
-
|
59
|
+
ddog_crasht_Config config = {
|
59
60
|
.additional_files = {},
|
60
61
|
// The Ruby VM already uses an alt stack to detect stack overflows so the crash handler must not overwrite it.
|
61
62
|
//
|
@@ -67,26 +68,26 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
67
68
|
// "Process.kill('SEGV', Process.pid)" gets run.
|
68
69
|
.create_alt_stack = false,
|
69
70
|
.endpoint = endpoint,
|
70
|
-
.resolve_frames =
|
71
|
+
.resolve_frames = DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER,
|
71
72
|
.timeout_secs = FIX2INT(upload_timeout_seconds),
|
72
73
|
// Waits for crash tracker to finish reporting the issue before letting the Ruby process die; see
|
73
74
|
// https://github.com/DataDog/libdatadog/pull/477 for details
|
74
75
|
.wait_for_receiver = true,
|
75
76
|
};
|
76
77
|
|
77
|
-
|
78
|
-
.
|
79
|
-
.
|
78
|
+
ddog_crasht_Metadata metadata = {
|
79
|
+
.library_name = DDOG_CHARSLICE_C("dd-trace-rb"),
|
80
|
+
.library_version = char_slice_from_ruby_string(version),
|
80
81
|
.family = DDOG_CHARSLICE_C("ruby"),
|
81
82
|
.tags = &tags,
|
82
83
|
};
|
83
84
|
|
84
|
-
|
85
|
+
ddog_crasht_EnvVar ld_library_path_env = {
|
85
86
|
.key = DDOG_CHARSLICE_C("LD_LIBRARY_PATH"),
|
86
87
|
.val = char_slice_from_ruby_string(ld_library_path),
|
87
88
|
};
|
88
89
|
|
89
|
-
|
90
|
+
ddog_crasht_ReceiverConfig receiver_config = {
|
90
91
|
.args = {},
|
91
92
|
.env = {.ptr = &ld_library_path_env, .len = 1},
|
92
93
|
.path_to_receiver_binary = char_slice_from_ruby_string(path_to_crashtracking_receiver_binary),
|
@@ -94,16 +95,17 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
94
95
|
.optional_stdout_filename = {},
|
95
96
|
};
|
96
97
|
|
97
|
-
|
98
|
+
ddog_crasht_Result result =
|
98
99
|
action == start_action ?
|
99
|
-
|
100
|
-
|
100
|
+
ddog_crasht_init_with_receiver(config, receiver_config, metadata) :
|
101
|
+
ddog_crasht_update_on_fork(config, receiver_config, metadata);
|
101
102
|
|
102
103
|
// Clean up before potentially raising any exceptions
|
103
104
|
ddog_Vec_Tag_drop(tags);
|
105
|
+
ddog_endpoint_drop(endpoint);
|
104
106
|
// }} End of exception-free zone to prevent leaks
|
105
107
|
|
106
|
-
if (result.tag ==
|
108
|
+
if (result.tag == DDOG_CRASHT_RESULT_ERR) {
|
107
109
|
rb_raise(rb_eRuntimeError, "Failed to start/update the crash tracker: %"PRIsVALUE, get_error_details_and_drop(&result.err));
|
108
110
|
}
|
109
111
|
|
@@ -111,9 +113,9 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
111
113
|
}
|
112
114
|
|
113
115
|
static VALUE _native_stop(DDTRACE_UNUSED VALUE _self) {
|
114
|
-
|
116
|
+
ddog_crasht_Result result = ddog_crasht_shutdown();
|
115
117
|
|
116
|
-
if (result.tag ==
|
118
|
+
if (result.tag == DDOG_CRASHT_RESULT_ERR) {
|
117
119
|
rb_raise(rb_eRuntimeError, "Failed to stop the crash tracker: %"PRIsVALUE, get_error_details_and_drop(&result.err));
|
118
120
|
}
|
119
121
|
|