ddtrace 1.6.1 → 1.8.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 +89 -2
- data/README.md +2 -2
- data/ext/ddtrace_profiling_loader/extconf.rb +5 -2
- data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +1 -1
- data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +3 -2
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +81 -47
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.h +1 -1
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +332 -125
- data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.c +142 -0
- data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.h +14 -0
- data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.c +241 -0
- data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.h +3 -0
- data/ext/ddtrace_profiling_native_extension/collectors_stack.c +11 -13
- data/ext/ddtrace_profiling_native_extension/extconf.rb +22 -8
- data/ext/ddtrace_profiling_native_extension/helpers.h +5 -0
- data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +8 -0
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +111 -26
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +9 -0
- data/ext/ddtrace_profiling_native_extension/profiling.c +205 -0
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +86 -0
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +28 -6
- data/ext/ddtrace_profiling_native_extension/setup_signal_handler.c +115 -0
- data/ext/ddtrace_profiling_native_extension/setup_signal_handler.h +11 -0
- data/ext/ddtrace_profiling_native_extension/stack_recorder.c +84 -35
- data/ext/ddtrace_profiling_native_extension/stack_recorder.h +1 -0
- data/ext/ddtrace_profiling_native_extension/time_helpers.c +17 -0
- data/ext/ddtrace_profiling_native_extension/time_helpers.h +10 -0
- data/lib/datadog/appsec/assets/blocked.html +98 -3
- data/lib/datadog/appsec/assets/blocked.json +1 -0
- data/lib/datadog/appsec/assets/blocked.text +5 -0
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +35 -46
- data/lib/datadog/appsec/assets/waf_rules/risky.json +1 -1
- data/lib/datadog/appsec/assets/waf_rules/strict.json +46 -1
- data/lib/datadog/appsec/assets.rb +2 -2
- data/lib/datadog/appsec/configuration/settings.rb +6 -0
- data/lib/datadog/appsec/configuration.rb +4 -0
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +4 -8
- data/lib/datadog/appsec/contrib/rack/request.rb +17 -0
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +2 -2
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +2 -2
- data/lib/datadog/appsec/contrib/rails/patcher.rb +3 -6
- data/lib/datadog/appsec/contrib/sinatra/ext.rb +1 -0
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +11 -8
- data/lib/datadog/appsec/extensions.rb +10 -0
- data/lib/datadog/appsec/processor.rb +18 -0
- data/lib/datadog/appsec/response.rb +54 -0
- data/lib/datadog/core/configuration/components.rb +27 -6
- data/lib/datadog/core/configuration/ext.rb +18 -0
- data/lib/datadog/core/configuration/settings.rb +14 -341
- data/lib/datadog/core/diagnostics/health.rb +4 -22
- data/lib/datadog/core/environment/variable_helpers.rb +58 -10
- data/lib/datadog/core/runtime/ext.rb +1 -1
- data/lib/datadog/core/utils.rb +0 -21
- data/lib/datadog/core.rb +21 -1
- data/lib/datadog/opentracer/distributed_headers.rb +7 -9
- data/lib/datadog/opentracer/rack_propagator.rb +0 -3
- data/lib/datadog/opentracer/text_map_propagator.rb +5 -7
- data/lib/datadog/profiling/collectors/cpu_and_wall_time.rb +10 -4
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +20 -5
- data/lib/datadog/profiling/collectors/dynamic_sampling_rate.rb +14 -0
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +68 -0
- data/lib/datadog/profiling/collectors/old_stack.rb +7 -0
- data/lib/datadog/profiling/exporter.rb +5 -0
- data/lib/datadog/profiling/old_recorder.rb +8 -0
- data/lib/datadog/profiling/profiler.rb +7 -0
- data/lib/datadog/profiling/scheduler.rb +4 -7
- data/lib/datadog/profiling/stack_recorder.rb +36 -0
- data/lib/datadog/profiling/tasks/setup.rb +0 -7
- data/lib/datadog/profiling.rb +2 -0
- data/lib/datadog/tracing/configuration/ext.rb +33 -3
- data/lib/datadog/tracing/configuration/settings.rb +433 -0
- data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +4 -1
- data/lib/datadog/tracing/contrib/aws/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +4 -1
- data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +4 -0
- data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +6 -1
- data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +6 -1
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +2 -1
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +6 -12
- data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +27 -0
- data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +43 -0
- data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/grpc/patcher.rb +0 -2
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +6 -1
- data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +32 -0
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +38 -0
- data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +6 -1
- data/lib/datadog/tracing/contrib/httpclient/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +6 -1
- data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -0
- data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +1 -0
- data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +1 -0
- data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +5 -1
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +4 -1
- data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +2 -2
- data/lib/datadog/tracing/contrib/patcher.rb +3 -2
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +4 -1
- data/lib/datadog/tracing/contrib/pg/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +12 -2
- data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +4 -1
- data/lib/datadog/tracing/contrib/presto/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +10 -12
- data/lib/datadog/tracing/contrib/que/tracer.rb +2 -0
- data/lib/datadog/tracing/contrib/racecar/events/batch.rb +4 -1
- data/lib/datadog/tracing/contrib/racecar/events/message.rb +4 -1
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +2 -0
- data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +4 -1
- data/lib/datadog/tracing/contrib/redis/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/redis/instrumentation.rb +30 -21
- data/lib/datadog/tracing/contrib/redis/integration.rb +34 -2
- data/lib/datadog/tracing/contrib/redis/patcher.rb +18 -14
- data/lib/datadog/tracing/contrib/redis/quantize.rb +12 -9
- data/lib/datadog/tracing/contrib/redis/tags.rb +4 -6
- data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +72 -0
- data/lib/datadog/tracing/contrib/resque/resque_job.rb +2 -0
- data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +6 -1
- data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +5 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +5 -0
- data/lib/datadog/tracing/contrib/sneakers/tracer.rb +2 -0
- data/lib/datadog/{core → tracing}/diagnostics/ext.rb +1 -6
- data/lib/datadog/tracing/diagnostics/health.rb +40 -0
- data/lib/datadog/tracing/distributed/b3_multi.rb +66 -0
- data/lib/datadog/tracing/distributed/b3_single.rb +66 -0
- data/lib/datadog/tracing/distributed/datadog.rb +153 -0
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +1 -0
- data/lib/datadog/tracing/distributed/fetcher.rb +30 -0
- data/lib/datadog/tracing/distributed/headers/ext.rb +18 -16
- data/lib/datadog/tracing/distributed/helpers.rb +9 -7
- data/lib/datadog/tracing/distributed/none.rb +19 -0
- data/lib/datadog/tracing/distributed/propagation.rb +127 -0
- data/lib/datadog/tracing/distributed/trace_context.rb +369 -0
- data/lib/datadog/tracing/metadata/ext.rb +1 -1
- data/lib/datadog/tracing/propagation/http.rb +3 -106
- data/lib/datadog/tracing/sampling/priority_sampler.rb +11 -0
- data/lib/datadog/tracing/sampling/rate_sampler.rb +3 -3
- data/lib/datadog/tracing/span.rb +3 -19
- data/lib/datadog/tracing/span_operation.rb +5 -4
- data/lib/datadog/tracing/trace_digest.rb +75 -2
- data/lib/datadog/tracing/trace_operation.rb +5 -4
- data/lib/datadog/tracing/trace_segment.rb +1 -1
- data/lib/datadog/tracing/utils.rb +50 -0
- data/lib/ddtrace/transport/trace_formatter.rb +2 -5
- data/lib/ddtrace/version.rb +2 -2
- metadata +35 -15
- data/lib/datadog/tracing/distributed/headers/b3.rb +0 -55
- data/lib/datadog/tracing/distributed/headers/b3_single.rb +0 -67
- data/lib/datadog/tracing/distributed/headers/datadog.rb +0 -144
- data/lib/datadog/tracing/distributed/headers/parser.rb +0 -37
- data/lib/datadog/tracing/distributed/metadata/b3.rb +0 -55
- data/lib/datadog/tracing/distributed/metadata/b3_single.rb +0 -66
- data/lib/datadog/tracing/distributed/metadata/datadog.rb +0 -73
- data/lib/datadog/tracing/distributed/metadata/parser.rb +0 -34
- data/lib/datadog/tracing/propagation/grpc.rb +0 -98
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <signal.h>
|
|
3
|
+
#include <errno.h>
|
|
4
|
+
#include <stdbool.h>
|
|
5
|
+
|
|
6
|
+
#include "helpers.h"
|
|
7
|
+
#include "setup_signal_handler.h"
|
|
8
|
+
#include "ruby_helpers.h"
|
|
9
|
+
|
|
10
|
+
// Used by Collectors::CpuAndWallTimeWorker to setup SIGPROF signal handlers used for cpu/wall-time profiling.
|
|
11
|
+
|
|
12
|
+
static void install_sigprof_signal_handler_internal(
|
|
13
|
+
void (*signal_handler_function)(int, siginfo_t *, void *),
|
|
14
|
+
const char *handler_pretty_name,
|
|
15
|
+
void (*signal_handler_to_replace)(int, siginfo_t *, void *)
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
void empty_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) { }
|
|
19
|
+
|
|
20
|
+
void install_sigprof_signal_handler(void (*signal_handler_function)(int, siginfo_t *, void *), const char *handler_pretty_name) {
|
|
21
|
+
install_sigprof_signal_handler_internal(signal_handler_function, handler_pretty_name, NULL);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
void replace_sigprof_signal_handler_with_empty_handler(void (*expected_existing_handler)(int, siginfo_t *, void *)) {
|
|
25
|
+
install_sigprof_signal_handler_internal(empty_signal_handler, "empty_signal_handler", expected_existing_handler);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static void install_sigprof_signal_handler_internal(
|
|
29
|
+
void (*signal_handler_function)(int, siginfo_t *, void *),
|
|
30
|
+
const char *handler_pretty_name,
|
|
31
|
+
void (*signal_handler_to_replace)(int, siginfo_t *, void *)
|
|
32
|
+
) {
|
|
33
|
+
struct sigaction existing_signal_handler_config = {.sa_sigaction = NULL};
|
|
34
|
+
struct sigaction signal_handler_config = {
|
|
35
|
+
.sa_flags = SA_RESTART | SA_SIGINFO,
|
|
36
|
+
.sa_sigaction = signal_handler_function
|
|
37
|
+
};
|
|
38
|
+
sigemptyset(&signal_handler_config.sa_mask);
|
|
39
|
+
|
|
40
|
+
if (sigaction(SIGPROF, &signal_handler_config, &existing_signal_handler_config) != 0) {
|
|
41
|
+
rb_exc_raise(rb_syserr_new_str(errno, rb_sprintf("Could not install profiling signal handler (%s)", handler_pretty_name)));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Because signal handler functions are global, let's check if we're not stepping on someone else's toes.
|
|
45
|
+
|
|
46
|
+
// If the existing signal handler was our empty one, that's ok as well
|
|
47
|
+
if (existing_signal_handler_config.sa_sigaction == empty_signal_handler ||
|
|
48
|
+
// In some corner cases (e.g. after a fork), our signal handler may still be around, and that's ok
|
|
49
|
+
existing_signal_handler_config.sa_sigaction == signal_handler_function ||
|
|
50
|
+
// Are we replacing a known handler with another one?
|
|
51
|
+
(signal_handler_to_replace != NULL && existing_signal_handler_config.sa_sigaction == signal_handler_to_replace)
|
|
52
|
+
) { return; }
|
|
53
|
+
|
|
54
|
+
if (existing_signal_handler_config.sa_handler != NULL || existing_signal_handler_config.sa_sigaction != NULL) {
|
|
55
|
+
// An unexpected/unknown signal handler already existed. Currently we don't support this situation, so let's just back out
|
|
56
|
+
// of the installation.
|
|
57
|
+
|
|
58
|
+
if (sigaction(SIGPROF, &existing_signal_handler_config, NULL) != 0) {
|
|
59
|
+
rb_exc_raise(
|
|
60
|
+
rb_syserr_new_str(
|
|
61
|
+
errno,
|
|
62
|
+
rb_sprintf(
|
|
63
|
+
"Failed to install profiling signal handler (%s): " \
|
|
64
|
+
"While installing a SIGPROF signal handler, the profiler detected that another software/library/gem had " \
|
|
65
|
+
"previously installed a different SIGPROF signal handler. " \
|
|
66
|
+
"The profiler tried to restore the previous SIGPROF signal handler, but this failed. " \
|
|
67
|
+
"The other software/library/gem may have been left in a broken state. ",
|
|
68
|
+
handler_pretty_name
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
rb_raise(
|
|
75
|
+
rb_eRuntimeError,
|
|
76
|
+
"Could not install profiling signal handler (%s): There's a pre-existing SIGPROF signal handler",
|
|
77
|
+
handler_pretty_name
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Note: Be careful when using this; you probably want to use `replace_sigprof_signal_handler_with_empty_handler` instead.
|
|
83
|
+
// (See comments on `collectors_cpu_and_wall_time_worker.c` for details)
|
|
84
|
+
void remove_sigprof_signal_handler(void) {
|
|
85
|
+
struct sigaction signal_handler_config = {
|
|
86
|
+
.sa_handler = SIG_DFL, // Reset back to default
|
|
87
|
+
.sa_flags = SA_RESTART // TODO: Unclear if this is actually needed/does anything at all
|
|
88
|
+
};
|
|
89
|
+
sigemptyset(&signal_handler_config.sa_mask);
|
|
90
|
+
|
|
91
|
+
if (sigaction(SIGPROF, &signal_handler_config, NULL) != 0) rb_sys_fail("Failure while removing the signal handler");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static void toggle_sigprof_signal_handler_for_current_thread(int action) {
|
|
95
|
+
sigset_t signals_to_toggle;
|
|
96
|
+
sigemptyset(&signals_to_toggle);
|
|
97
|
+
sigaddset(&signals_to_toggle, SIGPROF);
|
|
98
|
+
int error = pthread_sigmask(action, &signals_to_toggle, NULL);
|
|
99
|
+
if (error) rb_exc_raise(rb_syserr_new_str(error, rb_sprintf("Unexpected failure in pthread_sigmask, action=%d", action)));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
void block_sigprof_signal_handler_from_running_in_current_thread(void) {
|
|
103
|
+
toggle_sigprof_signal_handler_for_current_thread(SIG_BLOCK);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
void unblock_sigprof_signal_handler_from_running_in_current_thread(void) {
|
|
107
|
+
toggle_sigprof_signal_handler_for_current_thread(SIG_UNBLOCK);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
VALUE is_sigprof_blocked_in_current_thread(void) {
|
|
111
|
+
sigset_t current_signals;
|
|
112
|
+
sigemptyset(¤t_signals);
|
|
113
|
+
ENFORCE_SUCCESS_GVL(pthread_sigmask(0, NULL, ¤t_signals));
|
|
114
|
+
return sigismember(¤t_signals, SIGPROF) ? Qtrue : Qfalse;
|
|
115
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <signal.h>
|
|
4
|
+
|
|
5
|
+
void empty_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
|
|
6
|
+
void install_sigprof_signal_handler(void (*signal_handler_function)(int, siginfo_t *, void *), const char *handler_pretty_name);
|
|
7
|
+
void replace_sigprof_signal_handler_with_empty_handler(void (*expected_existing_handler)(int, siginfo_t *, void *));
|
|
8
|
+
void remove_sigprof_signal_handler(void);
|
|
9
|
+
void block_sigprof_signal_handler_from_running_in_current_thread(void);
|
|
10
|
+
void unblock_sigprof_signal_handler_from_running_in_current_thread(void);
|
|
11
|
+
VALUE is_sigprof_blocked_in_current_thread(void);
|
|
@@ -152,6 +152,7 @@ struct active_slot_pair {
|
|
|
152
152
|
struct call_serialize_without_gvl_arguments {
|
|
153
153
|
// Set by caller
|
|
154
154
|
struct stack_recorder_state *state;
|
|
155
|
+
ddog_Timespec finish_timestamp;
|
|
155
156
|
|
|
156
157
|
// Set by callee
|
|
157
158
|
ddog_Profile *profile;
|
|
@@ -162,18 +163,22 @@ struct call_serialize_without_gvl_arguments {
|
|
|
162
163
|
};
|
|
163
164
|
|
|
164
165
|
static VALUE _native_new(VALUE klass);
|
|
166
|
+
static void initialize_slot_concurrency_control(struct stack_recorder_state *state);
|
|
165
167
|
static void stack_recorder_typed_data_free(void *data);
|
|
166
168
|
static VALUE _native_serialize(VALUE self, VALUE recorder_instance);
|
|
167
169
|
static VALUE ruby_time_from(ddog_Timespec ddprof_time);
|
|
168
170
|
static void *call_serialize_without_gvl(void *call_args);
|
|
169
171
|
static struct active_slot_pair sampler_lock_active_profile();
|
|
170
172
|
static void sampler_unlock_active_profile(struct active_slot_pair active_slot);
|
|
171
|
-
static ddog_Profile *serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state
|
|
173
|
+
static ddog_Profile *serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state);
|
|
172
174
|
static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
|
173
175
|
static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
|
174
176
|
static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
|
175
177
|
static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot);
|
|
176
|
-
static ddog_Timespec time_now();
|
|
178
|
+
static ddog_Timespec time_now(void);
|
|
179
|
+
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance);
|
|
180
|
+
static void serializer_set_start_timestamp_for_next_profile(struct stack_recorder_state *state, ddog_Timespec timestamp);
|
|
181
|
+
static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint);
|
|
177
182
|
|
|
178
183
|
void stack_recorder_init(VALUE profiling_module) {
|
|
179
184
|
stack_recorder_class = rb_define_class_under(profiling_module, "StackRecorder", rb_cObject);
|
|
@@ -191,9 +196,11 @@ void stack_recorder_init(VALUE profiling_module) {
|
|
|
191
196
|
rb_define_alloc_func(stack_recorder_class, _native_new);
|
|
192
197
|
|
|
193
198
|
rb_define_singleton_method(stack_recorder_class, "_native_serialize", _native_serialize, 1);
|
|
199
|
+
rb_define_singleton_method(stack_recorder_class, "_native_reset_after_fork", _native_reset_after_fork, 1);
|
|
194
200
|
rb_define_singleton_method(testing_module, "_native_active_slot", _native_active_slot, 1);
|
|
195
201
|
rb_define_singleton_method(testing_module, "_native_slot_one_mutex_locked?", _native_is_slot_one_mutex_locked, 1);
|
|
196
202
|
rb_define_singleton_method(testing_module, "_native_slot_two_mutex_locked?", _native_is_slot_two_mutex_locked, 1);
|
|
203
|
+
rb_define_singleton_method(testing_module, "_native_record_endpoint", _native_record_endpoint, 3);
|
|
197
204
|
|
|
198
205
|
ok_symbol = ID2SYM(rb_intern_const("ok"));
|
|
199
206
|
error_symbol = ID2SYM(rb_intern_const("error"));
|
|
@@ -217,14 +224,7 @@ static VALUE _native_new(VALUE klass) {
|
|
|
217
224
|
|
|
218
225
|
ddog_Slice_value_type sample_types = {.ptr = enabled_value_types, .len = ENABLED_VALUE_TYPES_COUNT};
|
|
219
226
|
|
|
220
|
-
state
|
|
221
|
-
state->slot_two_mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
|
222
|
-
|
|
223
|
-
// A newly-created StackRecorder starts with slot one being active for samples, so let's lock slot two
|
|
224
|
-
int error = pthread_mutex_lock(&state->slot_two_mutex);
|
|
225
|
-
if (error) rb_syserr_fail(error, "Unexpected failure during pthread_mutex_lock");
|
|
226
|
-
|
|
227
|
-
state->active_slot = 1;
|
|
227
|
+
initialize_slot_concurrency_control(state);
|
|
228
228
|
|
|
229
229
|
// Note: Don't raise exceptions after this point, since it'll lead to libdatadog memory leaking!
|
|
230
230
|
|
|
@@ -234,6 +234,16 @@ static VALUE _native_new(VALUE klass) {
|
|
|
234
234
|
return TypedData_Wrap_Struct(klass, &stack_recorder_typed_data, state);
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
static void initialize_slot_concurrency_control(struct stack_recorder_state *state) {
|
|
238
|
+
state->slot_one_mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
|
239
|
+
state->slot_two_mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
|
240
|
+
|
|
241
|
+
// A newly-created StackRecorder starts with slot one being active for samples, so let's lock slot two
|
|
242
|
+
ENFORCE_SUCCESS_GVL(pthread_mutex_lock(&state->slot_two_mutex));
|
|
243
|
+
|
|
244
|
+
state->active_slot = 1;
|
|
245
|
+
}
|
|
246
|
+
|
|
237
247
|
static void stack_recorder_typed_data_free(void *state_ptr) {
|
|
238
248
|
struct stack_recorder_state *state = (struct stack_recorder_state *) state_ptr;
|
|
239
249
|
|
|
@@ -250,9 +260,13 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
|
|
|
250
260
|
struct stack_recorder_state *state;
|
|
251
261
|
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
|
252
262
|
|
|
263
|
+
ddog_Timespec finish_timestamp = time_now();
|
|
264
|
+
// Need to do this while still holding on to the Global VM Lock; see comments on method for why
|
|
265
|
+
serializer_set_start_timestamp_for_next_profile(state, finish_timestamp);
|
|
266
|
+
|
|
253
267
|
// We'll release the Global VM Lock while we're calling serialize, so that the Ruby VM can continue to work while this
|
|
254
268
|
// is pending
|
|
255
|
-
struct call_serialize_without_gvl_arguments args = {.state = state, .serialize_ran = false};
|
|
269
|
+
struct call_serialize_without_gvl_arguments args = {.state = state, .finish_timestamp = finish_timestamp, .serialize_ran = false};
|
|
256
270
|
|
|
257
271
|
while (!args.serialize_ran) {
|
|
258
272
|
// Give the Ruby VM an opportunity to process any pending interruptions (including raising exceptions).
|
|
@@ -315,13 +329,22 @@ void record_sample(VALUE recorder_instance, ddog_Sample sample) {
|
|
|
315
329
|
sampler_unlock_active_profile(active_slot);
|
|
316
330
|
}
|
|
317
331
|
|
|
332
|
+
void record_endpoint(VALUE recorder_instance, ddog_CharSlice local_root_span_id, ddog_CharSlice endpoint) {
|
|
333
|
+
struct stack_recorder_state *state;
|
|
334
|
+
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
|
335
|
+
|
|
336
|
+
struct active_slot_pair active_slot = sampler_lock_active_profile(state);
|
|
337
|
+
|
|
338
|
+
ddog_Profile_set_endpoint(active_slot.profile, local_root_span_id, endpoint);
|
|
339
|
+
|
|
340
|
+
sampler_unlock_active_profile(active_slot);
|
|
341
|
+
}
|
|
342
|
+
|
|
318
343
|
static void *call_serialize_without_gvl(void *call_args) {
|
|
319
344
|
struct call_serialize_without_gvl_arguments *args = (struct call_serialize_without_gvl_arguments *) call_args;
|
|
320
345
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
args->profile = serializer_flip_active_and_inactive_slots(args->state, finish_timestamp);
|
|
324
|
-
args->result = ddog_Profile_serialize(args->profile, &finish_timestamp, NULL /* duration_nanos is optional */);
|
|
346
|
+
args->profile = serializer_flip_active_and_inactive_slots(args->state);
|
|
347
|
+
args->result = ddog_Profile_serialize(args->profile, &args->finish_timestamp, NULL /* duration_nanos is optional */);
|
|
325
348
|
args->serialize_ran = true;
|
|
326
349
|
|
|
327
350
|
return NULL; // Unused
|
|
@@ -337,7 +360,7 @@ static struct active_slot_pair sampler_lock_active_profile(struct stack_recorder
|
|
|
337
360
|
|
|
338
361
|
for (int attempts = 0; attempts < 2; attempts++) {
|
|
339
362
|
error = pthread_mutex_trylock(&state->slot_one_mutex);
|
|
340
|
-
if (error && error != EBUSY)
|
|
363
|
+
if (error && error != EBUSY) ENFORCE_SUCCESS_GVL(error);
|
|
341
364
|
|
|
342
365
|
// Slot one is active
|
|
343
366
|
if (!error) return (struct active_slot_pair) {.mutex = &state->slot_one_mutex, .profile = state->slot_one_profile};
|
|
@@ -345,7 +368,7 @@ static struct active_slot_pair sampler_lock_active_profile(struct stack_recorder
|
|
|
345
368
|
// If we got here, slot one was not active, let's try slot two
|
|
346
369
|
|
|
347
370
|
error = pthread_mutex_trylock(&state->slot_two_mutex);
|
|
348
|
-
if (error && error != EBUSY)
|
|
371
|
+
if (error && error != EBUSY) ENFORCE_SUCCESS_GVL(error);
|
|
349
372
|
|
|
350
373
|
// Slot two is active
|
|
351
374
|
if (!error) return (struct active_slot_pair) {.mutex = &state->slot_two_mutex, .profile = state->slot_two_profile};
|
|
@@ -356,32 +379,24 @@ static struct active_slot_pair sampler_lock_active_profile(struct stack_recorder
|
|
|
356
379
|
}
|
|
357
380
|
|
|
358
381
|
static void sampler_unlock_active_profile(struct active_slot_pair active_slot) {
|
|
359
|
-
|
|
360
|
-
if (error != 0) rb_syserr_fail(error, "Unexpected failure in sampler_unlock_active_profile");
|
|
382
|
+
ENFORCE_SUCCESS_GVL(pthread_mutex_unlock(active_slot.mutex));
|
|
361
383
|
}
|
|
362
384
|
|
|
363
|
-
static ddog_Profile *serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state
|
|
364
|
-
int error;
|
|
385
|
+
static ddog_Profile *serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state) {
|
|
365
386
|
int previously_active_slot = state->active_slot;
|
|
366
387
|
|
|
367
388
|
if (previously_active_slot != 1 && previously_active_slot != 2) {
|
|
368
|
-
|
|
389
|
+
grab_gvl_and_raise(rb_eRuntimeError, "Unexpected active_slot state %d in serializer_flip_active_and_inactive_slots", previously_active_slot);
|
|
369
390
|
}
|
|
370
391
|
|
|
371
392
|
pthread_mutex_t *previously_active = (previously_active_slot == 1) ? &state->slot_one_mutex : &state->slot_two_mutex;
|
|
372
393
|
pthread_mutex_t *previously_inactive = (previously_active_slot == 1) ? &state->slot_two_mutex : &state->slot_one_mutex;
|
|
373
394
|
|
|
374
|
-
// Before making this profile active, we reset it so that it uses the correct timestamp for its start
|
|
375
|
-
ddog_Profile *previously_inactive_profile = (previously_active_slot == 1) ? state->slot_two_profile : state->slot_one_profile;
|
|
376
|
-
if (!ddog_Profile_reset(previously_inactive_profile, &start_timestamp_for_next_profile)) rb_raise(rb_eRuntimeError, "Failed to reset profile");
|
|
377
|
-
|
|
378
395
|
// Release the lock, thus making this slot active
|
|
379
|
-
|
|
380
|
-
if (error) rb_syserr_fail(error, "Unexpected failure during serializer_flip_active_and_inactive_slots for previously_inactive");
|
|
396
|
+
ENFORCE_SUCCESS_NO_GVL(pthread_mutex_unlock(previously_inactive));
|
|
381
397
|
|
|
382
398
|
// Grab the lock, thus making this slot inactive
|
|
383
|
-
|
|
384
|
-
if (error) rb_syserr_fail(error, "Unexpected failure during serializer_flip_active_and_inactive_slots for previously_active");
|
|
399
|
+
ENFORCE_SUCCESS_NO_GVL(pthread_mutex_lock(previously_active));
|
|
385
400
|
|
|
386
401
|
// Update active_slot
|
|
387
402
|
state->active_slot = (previously_active_slot == 1) ? 2 : 1;
|
|
@@ -418,21 +433,55 @@ static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot) {
|
|
|
418
433
|
|
|
419
434
|
if (error == 0) {
|
|
420
435
|
// Mutex was unlocked
|
|
421
|
-
pthread_mutex_unlock(slot_mutex);
|
|
436
|
+
ENFORCE_SUCCESS_GVL(pthread_mutex_unlock(slot_mutex));
|
|
422
437
|
return Qfalse;
|
|
423
438
|
} else if (error == EBUSY) {
|
|
424
439
|
// Mutex was locked
|
|
425
440
|
return Qtrue;
|
|
426
441
|
} else {
|
|
427
|
-
|
|
442
|
+
ENFORCE_SUCCESS_GVL(error);
|
|
443
|
+
rb_raise(rb_eRuntimeError, "Failed to raise exception in test_slot_mutex_state; this should never happen");
|
|
428
444
|
}
|
|
429
445
|
}
|
|
430
446
|
|
|
431
|
-
// Note that this is using CLOCK_REALTIME (e.g. actual time since unix epoch) and not the CLOCK_MONOTONIC as we use in
|
|
432
|
-
|
|
447
|
+
// Note that this is using CLOCK_REALTIME (e.g. actual time since unix epoch) and not the CLOCK_MONOTONIC as we use in
|
|
448
|
+
// monotonic_wall_time_now_ns (used in other parts of the codebase)
|
|
449
|
+
static ddog_Timespec time_now(void) {
|
|
433
450
|
struct timespec current_time;
|
|
434
451
|
|
|
435
|
-
if (clock_gettime(CLOCK_REALTIME, ¤t_time) != 0)
|
|
452
|
+
if (clock_gettime(CLOCK_REALTIME, ¤t_time) != 0) ENFORCE_SUCCESS_GVL(errno);
|
|
436
453
|
|
|
437
454
|
return (ddog_Timespec) {.seconds = current_time.tv_sec, .nanoseconds = (uint32_t) current_time.tv_nsec};
|
|
438
455
|
}
|
|
456
|
+
|
|
457
|
+
// After the Ruby VM forks, this method gets called in the child process to clean up any leftover state from the parent.
|
|
458
|
+
//
|
|
459
|
+
// Assumption: This method gets called BEFORE restarting profiling -- e.g. there are no components attempting to
|
|
460
|
+
// trigger samples at the same time.
|
|
461
|
+
static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance) {
|
|
462
|
+
struct stack_recorder_state *state;
|
|
463
|
+
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
|
464
|
+
|
|
465
|
+
// In case the fork happened halfway through `serializer_flip_active_and_inactive_slots` execution and the
|
|
466
|
+
// resulting state is inconsistent, we make sure to reset it back to the initial state.
|
|
467
|
+
initialize_slot_concurrency_control(state);
|
|
468
|
+
|
|
469
|
+
ddog_Profile_reset(state->slot_one_profile, /* start_time: */ NULL);
|
|
470
|
+
ddog_Profile_reset(state->slot_two_profile, /* start_time: */ NULL);
|
|
471
|
+
|
|
472
|
+
return Qtrue;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Assumption 1: This method is called with the GVL being held, because `ddog_Profile_reset` mutates the profile and should
|
|
476
|
+
// not be interrupted part-way through by a VM fork.
|
|
477
|
+
static void serializer_set_start_timestamp_for_next_profile(struct stack_recorder_state *state, ddog_Timespec timestamp) {
|
|
478
|
+
// Before making this profile active, we reset it so that it uses the correct timestamp for its start
|
|
479
|
+
ddog_Profile *next_profile = (state->active_slot == 1) ? state->slot_two_profile : state->slot_one_profile;
|
|
480
|
+
|
|
481
|
+
if (!ddog_Profile_reset(next_profile, ×tamp)) rb_raise(rb_eRuntimeError, "Failed to reset profile");
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint) {
|
|
485
|
+
record_endpoint(recorder_instance, char_slice_from_ruby_string(local_root_span_id), char_slice_from_ruby_string(endpoint));
|
|
486
|
+
return Qtrue;
|
|
487
|
+
}
|
|
@@ -35,4 +35,5 @@ static const ddog_ValueType enabled_value_types[] = {
|
|
|
35
35
|
#define ENABLED_VALUE_TYPES_COUNT (sizeof(enabled_value_types) / sizeof(ddog_ValueType))
|
|
36
36
|
|
|
37
37
|
void record_sample(VALUE recorder_instance, ddog_Sample sample);
|
|
38
|
+
void record_endpoint(VALUE recorder_instance, ddog_CharSlice local_root_span_id, ddog_CharSlice endpoint);
|
|
38
39
|
VALUE enforce_recorder_instance(VALUE object);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#include <errno.h>
|
|
2
|
+
#include <time.h>
|
|
3
|
+
|
|
4
|
+
#include "ruby_helpers.h"
|
|
5
|
+
#include "time_helpers.h"
|
|
6
|
+
|
|
7
|
+
// Safety: This function is assumed never to raise exceptions by callers when raise_on_failure == false
|
|
8
|
+
long monotonic_wall_time_now_ns(bool raise_on_failure) {
|
|
9
|
+
struct timespec current_monotonic;
|
|
10
|
+
|
|
11
|
+
if (clock_gettime(CLOCK_MONOTONIC, ¤t_monotonic) != 0) {
|
|
12
|
+
if (raise_on_failure) ENFORCE_SUCCESS_GVL(errno);
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return current_monotonic.tv_nsec + SECONDS_AS_NS(current_monotonic.tv_sec);
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#define SECONDS_AS_NS(value) (value * 1000 * 1000 * 1000L)
|
|
4
|
+
#define MILLIS_AS_NS(value) (value * 1000 * 1000L)
|
|
5
|
+
|
|
6
|
+
#define RAISE_ON_FAILURE true
|
|
7
|
+
#define DO_NOT_RAISE_ON_FAILURE false
|
|
8
|
+
|
|
9
|
+
// Safety: This function is assumed never to raise exceptions by callers when raise_on_failure == false
|
|
10
|
+
long monotonic_wall_time_now_ns(bool raise_on_failure);
|