datadog 2.2.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 +87 -2
- data/ext/datadog_profiling_loader/datadog_profiling_loader.c +9 -1
- data/ext/datadog_profiling_loader/extconf.rb +14 -26
- data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
- data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
- data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +257 -69
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +53 -28
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
- data/ext/datadog_profiling_native_extension/collectors_stack.c +136 -81
- data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +661 -48
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +10 -1
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +83 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +53 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +91 -69
- 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 +54 -12
- data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
- data/ext/datadog_profiling_native_extension/helpers.h +6 -17
- data/ext/datadog_profiling_native_extension/http_transport.c +41 -9
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -172
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +116 -139
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +20 -11
- data/ext/datadog_profiling_native_extension/profiling.c +1 -3
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +14 -2
- data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
- data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
- data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
- data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +37 -22
- data/ext/libdatadog_api/datadog_ruby_common.c +83 -0
- data/ext/libdatadog_api/datadog_ruby_common.h +53 -0
- data/ext/libdatadog_api/extconf.rb +108 -0
- data/ext/libdatadog_api/macos_development.md +26 -0
- data/ext/libdatadog_extconf_helpers.rb +130 -0
- 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 +35 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +109 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +71 -0
- data/lib/datadog/appsec/contrib/graphql/integration.rb +54 -0
- data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +3 -6
- data/lib/datadog/appsec/event.rb +1 -1
- data/lib/datadog/appsec/processor/actions.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/response.rb +15 -1
- data/lib/datadog/appsec.rb +3 -2
- data/lib/datadog/core/configuration/components.rb +18 -15
- data/lib/datadog/core/configuration/settings.rb +135 -9
- data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
- data/lib/datadog/core/crashtracking/component.rb +111 -0
- data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
- data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
- 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 +51 -2
- data/lib/datadog/core/telemetry/emitter.rb +9 -11
- data/lib/datadog/core/telemetry/event.rb +37 -1
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- data/lib/datadog/core/telemetry/http/adapters/net.rb +10 -12
- data/lib/datadog/core/telemetry/http/ext.rb +3 -0
- data/lib/datadog/core/telemetry/http/transport.rb +38 -9
- data/lib/datadog/core/telemetry/logger.rb +51 -0
- data/lib/datadog/core/telemetry/logging.rb +71 -0
- data/lib/datadog/core/telemetry/request.rb +13 -1
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
- 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/kit/appsec/events.rb +2 -4
- data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -0
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +10 -0
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +7 -7
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +28 -26
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
- data/lib/datadog/profiling/collectors/info.rb +15 -6
- data/lib/datadog/profiling/collectors/thread_context.rb +30 -2
- data/lib/datadog/profiling/component.rb +89 -95
- data/lib/datadog/profiling/exporter.rb +3 -3
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
- data/lib/datadog/profiling/ext.rb +21 -21
- data/lib/datadog/profiling/flush.rb +1 -1
- data/lib/datadog/profiling/http_transport.rb +14 -7
- data/lib/datadog/profiling/load_native_extension.rb +5 -5
- data/lib/datadog/profiling/preload.rb +1 -1
- data/lib/datadog/profiling/profiler.rb +5 -8
- data/lib/datadog/profiling/scheduler.rb +33 -25
- data/lib/datadog/profiling/stack_recorder.rb +3 -0
- data/lib/datadog/profiling/tag_builder.rb +2 -2
- data/lib/datadog/profiling/tasks/exec.rb +5 -5
- data/lib/datadog/profiling/tasks/setup.rb +16 -35
- data/lib/datadog/profiling.rb +4 -5
- 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 +4 -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/ext.rb +14 -0
- 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 +14 -10
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +10 -4
- 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 +15 -0
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
- data/lib/datadog/tracing/contrib/opensearch/patcher.rb +13 -6
- data/lib/datadog/tracing/contrib/patcher.rb +2 -1
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
- data/lib/datadog/tracing/contrib/presto/patcher.rb +1 -13
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
- 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/contrib/trilogy/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
- data/lib/datadog/tracing/distributed/propagation.rb +7 -0
- data/lib/datadog/tracing/metadata/errors.rb +9 -1
- data/lib/datadog/tracing/metadata/ext.rb +6 -0
- data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
- 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/span.rb +9 -2
- data/lib/datadog/tracing/span_event.rb +41 -0
- data/lib/datadog/tracing/span_operation.rb +6 -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/transport/serializable_trace.rb +3 -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 +46 -11
- data/lib/datadog/profiling/crashtracker.rb +0 -91
- data/lib/datadog/profiling/ext/forking.rb +0 -98
- data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
@@ -20,6 +20,9 @@
|
|
20
20
|
|
21
21
|
#define EMA_SMOOTHING_FACTOR 0.6
|
22
22
|
|
23
|
+
static void maybe_readjust(discrete_dynamic_sampler *sampler, long now_ns);
|
24
|
+
static inline bool should_readjust(discrete_dynamic_sampler *sampler, coarse_instant now);
|
25
|
+
|
23
26
|
void discrete_dynamic_sampler_init(discrete_dynamic_sampler *sampler, const char *debug_name, long now_ns) {
|
24
27
|
sampler->debug_name = debug_name;
|
25
28
|
discrete_dynamic_sampler_set_overhead_target_percentage(sampler, BASE_OVERHEAD_PCT, now_ns);
|
@@ -54,27 +57,25 @@ void discrete_dynamic_sampler_set_overhead_target_percentage(discrete_dynamic_sa
|
|
54
57
|
return discrete_dynamic_sampler_reset(sampler, now_ns);
|
55
58
|
}
|
56
59
|
|
57
|
-
|
58
|
-
|
59
|
-
bool discrete_dynamic_sampler_should_sample(discrete_dynamic_sampler *sampler
|
60
|
+
// NOTE: See header for an explanation of when this should get used
|
61
|
+
__attribute__((warn_unused_result))
|
62
|
+
bool discrete_dynamic_sampler_should_sample(discrete_dynamic_sampler *sampler) {
|
60
63
|
// For efficiency reasons we don't do true random sampling but rather systematic
|
61
64
|
// sampling following a sample interval/skip. This can be biased and hide patterns
|
62
|
-
// but the dynamic interval and rather
|
65
|
+
// but the dynamic interval and rather nondeterministic pattern of allocations in
|
63
66
|
// most real applications should help reduce the bias impact.
|
64
67
|
sampler->events_since_last_sample++;
|
65
68
|
sampler->events_since_last_readjustment++;
|
66
|
-
bool should_sample = sampler->sampling_interval > 0 && sampler->events_since_last_sample >= sampler->sampling_interval;
|
67
69
|
|
68
|
-
|
69
|
-
|
70
|
-
} else {
|
71
|
-
// check if we should readjust our sampler after this event, even if we didn't sample it
|
72
|
-
maybe_readjust(sampler, now_ns);
|
73
|
-
}
|
70
|
+
return sampler->sampling_interval > 0 && sampler->events_since_last_sample >= sampler->sampling_interval;
|
71
|
+
}
|
74
72
|
|
75
|
-
|
73
|
+
// NOTE: See header for an explanation of when this should get used
|
74
|
+
void discrete_dynamic_sampler_before_sample(discrete_dynamic_sampler *sampler, long now_ns) {
|
75
|
+
sampler->sample_start_time_ns = now_ns;
|
76
76
|
}
|
77
77
|
|
78
|
+
// NOTE: See header for an explanation of when this should get used
|
78
79
|
long discrete_dynamic_sampler_after_sample(discrete_dynamic_sampler *sampler, long now_ns) {
|
79
80
|
long last_sampling_time_ns = sampler->sample_start_time_ns == 0 ? 0 : long_max_of(0, now_ns - sampler->sample_start_time_ns);
|
80
81
|
sampler->samples_since_last_readjustment++;
|
@@ -91,10 +92,15 @@ double discrete_dynamic_sampler_probability(discrete_dynamic_sampler *sampler) {
|
|
91
92
|
return sampler->sampling_probability * 100.;
|
92
93
|
}
|
93
94
|
|
94
|
-
|
95
|
+
unsigned long discrete_dynamic_sampler_events_since_last_sample(discrete_dynamic_sampler *sampler) {
|
95
96
|
return sampler->events_since_last_sample;
|
96
97
|
}
|
97
98
|
|
99
|
+
// NOTE: See header for an explanation of when this should get used
|
100
|
+
bool discrete_dynamic_sampler_skipped_sample(discrete_dynamic_sampler *sampler, coarse_instant now) {
|
101
|
+
return should_readjust(sampler, now);
|
102
|
+
}
|
103
|
+
|
98
104
|
static double ewma_adj_window(double latest_value, double avg, long current_window_time_ns, bool is_first) {
|
99
105
|
if (is_first) {
|
100
106
|
return latest_value;
|
@@ -109,19 +115,27 @@ static double ewma_adj_window(double latest_value, double avg, long current_wind
|
|
109
115
|
return (1-alpha) * avg + alpha * latest_value;
|
110
116
|
}
|
111
117
|
|
112
|
-
static void maybe_readjust(discrete_dynamic_sampler *sampler, long
|
113
|
-
|
118
|
+
static void maybe_readjust(discrete_dynamic_sampler *sampler, long now_ns) {
|
119
|
+
if (should_readjust(sampler, to_coarse_instant(now_ns))) discrete_dynamic_sampler_readjust(sampler, now_ns);
|
120
|
+
}
|
121
|
+
|
122
|
+
static inline bool should_readjust(discrete_dynamic_sampler *sampler, coarse_instant now) {
|
123
|
+
long this_window_time_ns =
|
124
|
+
sampler->last_readjust_time_ns == 0 ? ADJUSTMENT_WINDOW_NS : now.timestamp_ns - sampler->last_readjust_time_ns;
|
114
125
|
|
115
126
|
bool should_readjust_based_on_time = this_window_time_ns >= ADJUSTMENT_WINDOW_NS;
|
116
127
|
bool should_readjust_based_on_samples = sampler->samples_since_last_readjustment >= ADJUSTMENT_WINDOW_SAMPLES;
|
117
128
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
129
|
+
return should_readjust_based_on_time || should_readjust_based_on_samples;
|
130
|
+
}
|
131
|
+
|
132
|
+
// NOTE: This method ASSUMES should_readjust == true
|
133
|
+
// NOTE: See header for an explanation of when this should get used
|
134
|
+
void discrete_dynamic_sampler_readjust(discrete_dynamic_sampler *sampler, long now_ns) {
|
135
|
+
long this_window_time_ns = sampler->last_readjust_time_ns == 0 ? ADJUSTMENT_WINDOW_NS : now_ns - sampler->last_readjust_time_ns;
|
122
136
|
|
123
|
-
if (this_window_time_ns
|
124
|
-
// should not be possible given
|
137
|
+
if (this_window_time_ns <= 0) {
|
138
|
+
// should not be possible given should_readjust but lets protect against div by 0 below.
|
125
139
|
return;
|
126
140
|
}
|
127
141
|
|
@@ -143,7 +157,7 @@ static void maybe_readjust(discrete_dynamic_sampler *sampler, long now) {
|
|
143
157
|
// Lets update our average sampling time per event
|
144
158
|
long avg_sampling_time_in_window_ns = sampler->samples_since_last_readjustment == 0 ? 0 : sampler->sampling_time_since_last_readjustment_ns / sampler->samples_since_last_readjustment;
|
145
159
|
if (avg_sampling_time_in_window_ns > sampler->max_sampling_time_ns) {
|
146
|
-
// If the average sampling time in the previous window was deemed
|
160
|
+
// If the average sampling time in the previous window was deemed unacceptable, clamp it to the
|
147
161
|
// maximum acceptable value and register this operation in our counter.
|
148
162
|
// NOTE: This is important so that events like suspensions or system overloads do not lead us to
|
149
163
|
// learn arbitrarily big sampling times which may then result in us not sampling anything
|
@@ -245,7 +259,9 @@ static void maybe_readjust(discrete_dynamic_sampler *sampler, long now) {
|
|
245
259
|
// are so big they don't fit into the sampling_interval. In both cases lets just disable sampling until next readjustment
|
246
260
|
// by setting interval to 0.
|
247
261
|
double sampling_interval = sampler->sampling_probability == 0 ? 0 : ceil(1.0 / sampler->sampling_probability);
|
248
|
-
|
262
|
+
// NOTE: We use UINT32_MAX instead of ULONG_MAX here to avoid clang warnings; in practice, we shouldn't ever hit
|
263
|
+
// such high sampling intervals.
|
264
|
+
sampler->sampling_interval = sampling_interval > UINT32_MAX ? 0 : sampling_interval;
|
249
265
|
|
250
266
|
#ifdef DD_DEBUG
|
251
267
|
double allocs_in_60s = sampler->events_per_ns * 1e9 * 60;
|
@@ -255,9 +271,7 @@ static void maybe_readjust(discrete_dynamic_sampler *sampler, long now) {
|
|
255
271
|
double num_this_windows_in_60s = 60 * 1e9 / this_window_time_ns;
|
256
272
|
double real_total_sampling_time_in_60s = sampler->sampling_time_since_last_readjustment_ns * num_this_windows_in_60s / 1e9;
|
257
273
|
|
258
|
-
|
259
|
-
|
260
|
-
fprintf(stderr, "[dds.%s] readjusting due to %s...\n", sampler->debug_name, readjustment_reason);
|
274
|
+
fprintf(stderr, "[dds.%s] readjusting...\n", sampler->debug_name);
|
261
275
|
fprintf(stderr, "events_since_last_readjustment=%ld\n", sampler->events_since_last_readjustment);
|
262
276
|
fprintf(stderr, "samples_since_last_readjustment=%ld\n", sampler->samples_since_last_readjustment);
|
263
277
|
fprintf(stderr, "this_window_time=%ld\n", this_window_time_ns);
|
@@ -286,7 +300,7 @@ static void maybe_readjust(discrete_dynamic_sampler *sampler, long now) {
|
|
286
300
|
sampler->events_since_last_readjustment = 0;
|
287
301
|
sampler->samples_since_last_readjustment = 0;
|
288
302
|
sampler->sampling_time_since_last_readjustment_ns = 0;
|
289
|
-
sampler->last_readjust_time_ns =
|
303
|
+
sampler->last_readjust_time_ns = now_ns;
|
290
304
|
sampler->has_completed_full_adjustment_window = true;
|
291
305
|
}
|
292
306
|
|
@@ -359,6 +373,10 @@ static VALUE _native_new(VALUE klass) {
|
|
359
373
|
}
|
360
374
|
discrete_dynamic_sampler_init(&state->sampler, "test sampler", now_ns);
|
361
375
|
|
376
|
+
// Note: As of this writing, no new Ruby objects get created and stored in the state. If that ever changes, remember
|
377
|
+
// to keep them on the stack and mark them with RB_GC_GUARD -- otherwise it's possible for a GC to run and
|
378
|
+
// since the instance representing the state does not yet exist, such objects will not get marked.
|
379
|
+
|
362
380
|
return TypedData_Wrap_Struct(klass, &sampler_typed_data, state);
|
363
381
|
}
|
364
382
|
|
@@ -402,7 +420,14 @@ VALUE _native_should_sample(VALUE self, VALUE now_ns) {
|
|
402
420
|
sampler_state *state;
|
403
421
|
TypedData_Get_Struct(self, sampler_state, &sampler_typed_data, state);
|
404
422
|
|
405
|
-
|
423
|
+
if (discrete_dynamic_sampler_should_sample(&state->sampler)) {
|
424
|
+
discrete_dynamic_sampler_before_sample(&state->sampler, NUM2LONG(now_ns));
|
425
|
+
return Qtrue;
|
426
|
+
} else {
|
427
|
+
bool needs_readjust = discrete_dynamic_sampler_skipped_sample(&state->sampler, to_coarse_instant(NUM2LONG(now_ns)));
|
428
|
+
if (needs_readjust) discrete_dynamic_sampler_readjust(&state->sampler, NUM2LONG(now_ns));
|
429
|
+
return Qfalse;
|
430
|
+
}
|
406
431
|
}
|
407
432
|
|
408
433
|
VALUE _native_after_sample(VALUE self, VALUE now_ns) {
|
@@ -5,6 +5,8 @@
|
|
5
5
|
|
6
6
|
#include <ruby.h>
|
7
7
|
|
8
|
+
#include "time_helpers.h"
|
9
|
+
|
8
10
|
// A sampler that will sample discrete events based on the overhead of their
|
9
11
|
// sampling.
|
10
12
|
//
|
@@ -62,7 +64,6 @@ typedef struct discrete_dynamic_sampler {
|
|
62
64
|
unsigned long sampling_time_clamps;
|
63
65
|
} discrete_dynamic_sampler;
|
64
66
|
|
65
|
-
|
66
67
|
// Init a new sampler with sane defaults.
|
67
68
|
void discrete_dynamic_sampler_init(discrete_dynamic_sampler *sampler, const char *debug_name, long now_ns);
|
68
69
|
|
@@ -80,9 +81,38 @@ void discrete_dynamic_sampler_set_overhead_target_percentage(discrete_dynamic_sa
|
|
80
81
|
// @return True if the event associated with this decision should be sampled, false
|
81
82
|
// otherwise.
|
82
83
|
//
|
83
|
-
//
|
84
|
-
//
|
85
|
-
|
84
|
+
// IMPORTANT: A call to this method MUST be followed by a call to either
|
85
|
+
// `discrete_dynamic_sampler_before_sample` if return is `true` or
|
86
|
+
// `discrete_dynamic_sampler_skipped_sample` if return is `false`.
|
87
|
+
//
|
88
|
+
// In the past, this method took care of before_sample/skipped_sample/readjust as well.
|
89
|
+
// We've had to split it up because we wanted to both use coarse time and regular monotonic time depending on the
|
90
|
+
// situation, but also wanted time to come as an argument from the outside.
|
91
|
+
//
|
92
|
+
// TL;DR here's how they should be used as Ruby code:
|
93
|
+
// if discrete_dynamic_sampler_should_sample
|
94
|
+
// discrete_dynamic_sampler_before_sample(monotonic_wall_time_now_ns())
|
95
|
+
// else
|
96
|
+
// needs_readjust = discrete_dynamic_sampler_skipped_sample(monotonic_coarse_wall_time_now_ns())
|
97
|
+
// discrete_dynamic_sampler_readjust(monotonic_wall_time_now_ns()) if needs_readjust
|
98
|
+
// end
|
99
|
+
__attribute__((warn_unused_result))
|
100
|
+
bool discrete_dynamic_sampler_should_sample(discrete_dynamic_sampler *sampler);
|
101
|
+
|
102
|
+
// Signal the start of a sampling operation.
|
103
|
+
// MUST be called after `discrete_dynamic_sampler_should_sample` returns `true`.
|
104
|
+
void discrete_dynamic_sampler_before_sample(discrete_dynamic_sampler *sampler, long now_ns);
|
105
|
+
|
106
|
+
// Signals that sampling did not happen
|
107
|
+
// MUST be called after `discrete_dynamic_sampler_skipped_sample` returns `false`.
|
108
|
+
//
|
109
|
+
// @return True if the sampler needs to be readjusted.
|
110
|
+
//
|
111
|
+
// IMPORTANT: A call to this method MUST be followed by a call to `discrete_dynamic_sampler_readjust` if return is `true`.
|
112
|
+
__attribute__((warn_unused_result))
|
113
|
+
bool discrete_dynamic_sampler_skipped_sample(discrete_dynamic_sampler *sampler, coarse_instant now);
|
114
|
+
|
115
|
+
void discrete_dynamic_sampler_readjust(discrete_dynamic_sampler *sampler, long now_ns);
|
86
116
|
|
87
117
|
// Signal the end of a sampling operation.
|
88
118
|
//
|
@@ -83,6 +83,10 @@ static VALUE _native_new(VALUE klass) {
|
|
83
83
|
|
84
84
|
reset_state(state);
|
85
85
|
|
86
|
+
// Note: As of this writing, no new Ruby objects get created and stored in the state. If that ever changes, remember
|
87
|
+
// to keep them on the stack and mark them with RB_GC_GUARD -- otherwise it's possible for a GC to run and
|
88
|
+
// since the instance representing the state does not yet exist, such objects will not get marked.
|
89
|
+
|
86
90
|
return TypedData_Wrap_Struct(klass, &idle_sampling_helper_typed_data, state);
|
87
91
|
}
|
88
92
|
|
@@ -15,56 +15,72 @@ static VALUE missing_string = Qnil;
|
|
15
15
|
|
16
16
|
// Used as scratch space during sampling
|
17
17
|
struct sampling_buffer {
|
18
|
-
|
19
|
-
VALUE *stack_buffer;
|
20
|
-
int *lines_buffer;
|
21
|
-
bool *is_ruby_frame;
|
18
|
+
uint16_t max_frames;
|
22
19
|
ddog_prof_Location *locations;
|
20
|
+
frame_info *stack_buffer;
|
23
21
|
}; // Note: typedef'd in the header to sampling_buffer
|
24
22
|
|
25
|
-
static VALUE _native_sample(
|
26
|
-
|
27
|
-
|
28
|
-
VALUE recorder_instance,
|
29
|
-
VALUE metric_values_hash,
|
30
|
-
VALUE labels_array,
|
31
|
-
VALUE numeric_labels_array,
|
32
|
-
VALUE max_frames,
|
33
|
-
VALUE in_gc
|
34
|
-
);
|
23
|
+
static VALUE _native_sample(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
|
24
|
+
static VALUE native_sample_do(VALUE args);
|
25
|
+
static VALUE native_sample_ensure(VALUE args);
|
35
26
|
static void maybe_add_placeholder_frames_omitted(VALUE thread, sampling_buffer* buffer, char *frames_omitted_message, int frames_omitted_message_size);
|
36
|
-
static void record_placeholder_stack_in_native_code(
|
27
|
+
static void record_placeholder_stack_in_native_code(VALUE recorder_instance, sample_values values, sample_labels labels);
|
37
28
|
static void maybe_trim_template_random_ids(ddog_CharSlice *name_slice, ddog_CharSlice *filename_slice);
|
38
29
|
|
30
|
+
// These two functions are exposed as symbols by the VM but are not in any header.
|
31
|
+
// Their signatures actually take a `const rb_iseq_t *iseq` but it gets casted back and forth between VALUE.
|
32
|
+
extern VALUE rb_iseq_path(const VALUE);
|
33
|
+
extern VALUE rb_iseq_base_label(const VALUE);
|
34
|
+
|
39
35
|
void collectors_stack_init(VALUE profiling_module) {
|
40
36
|
VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
|
41
37
|
VALUE collectors_stack_class = rb_define_class_under(collectors_module, "Stack", rb_cObject);
|
42
38
|
// Hosts methods used for testing the native code using RSpec
|
43
39
|
VALUE testing_module = rb_define_module_under(collectors_stack_class, "Testing");
|
44
40
|
|
45
|
-
rb_define_singleton_method(testing_module, "_native_sample", _native_sample,
|
41
|
+
rb_define_singleton_method(testing_module, "_native_sample", _native_sample, -1);
|
46
42
|
|
47
43
|
missing_string = rb_str_new2("");
|
48
44
|
rb_global_variable(&missing_string);
|
49
45
|
}
|
50
46
|
|
47
|
+
struct native_sample_args {
|
48
|
+
VALUE in_gc;
|
49
|
+
VALUE recorder_instance;
|
50
|
+
sample_values values;
|
51
|
+
sample_labels labels;
|
52
|
+
VALUE thread;
|
53
|
+
ddog_prof_Location *locations;
|
54
|
+
sampling_buffer *buffer;
|
55
|
+
};
|
56
|
+
|
51
57
|
// This method exists only to enable testing Datadog::Profiling::Collectors::Stack behavior using RSpec.
|
52
58
|
// It SHOULD NOT be used for other purposes.
|
53
|
-
static VALUE _native_sample(
|
54
|
-
|
55
|
-
VALUE thread
|
56
|
-
VALUE recorder_instance
|
57
|
-
VALUE metric_values_hash
|
58
|
-
VALUE labels_array
|
59
|
-
VALUE numeric_labels_array
|
60
|
-
VALUE
|
61
|
-
|
62
|
-
)
|
59
|
+
static VALUE _native_sample(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self) {
|
60
|
+
// Positional args
|
61
|
+
VALUE thread;
|
62
|
+
VALUE recorder_instance;
|
63
|
+
VALUE metric_values_hash;
|
64
|
+
VALUE labels_array;
|
65
|
+
VALUE numeric_labels_array;
|
66
|
+
VALUE options;
|
67
|
+
|
68
|
+
rb_scan_args(argc, argv, "5:", &thread, &recorder_instance, &metric_values_hash, &labels_array, &numeric_labels_array, &options);
|
69
|
+
|
70
|
+
if (options == Qnil) options = rb_hash_new();
|
71
|
+
|
72
|
+
// Optional keyword args
|
73
|
+
VALUE max_frames = rb_hash_lookup2(options, ID2SYM(rb_intern("max_frames")), INT2NUM(400));
|
74
|
+
VALUE in_gc = rb_hash_lookup2(options, ID2SYM(rb_intern("in_gc")), Qfalse);
|
75
|
+
VALUE is_gvl_waiting_state = rb_hash_lookup2(options, ID2SYM(rb_intern("is_gvl_waiting_state")), Qfalse);
|
76
|
+
|
63
77
|
ENFORCE_TYPE(metric_values_hash, T_HASH);
|
64
78
|
ENFORCE_TYPE(labels_array, T_ARRAY);
|
65
79
|
ENFORCE_TYPE(numeric_labels_array, T_ARRAY);
|
66
80
|
|
67
81
|
VALUE zero = INT2NUM(0);
|
82
|
+
VALUE heap_sample = rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("heap_sample"), Qfalse);
|
83
|
+
ENFORCE_BOOLEAN(heap_sample);
|
68
84
|
sample_values values = {
|
69
85
|
.cpu_time_ns = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("cpu-time"), zero)),
|
70
86
|
.cpu_or_wall_samples = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("cpu-samples"), zero)),
|
@@ -72,6 +88,7 @@ static VALUE _native_sample(
|
|
72
88
|
.alloc_samples = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("alloc-samples"), zero)),
|
73
89
|
.alloc_samples_unscaled = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("alloc-samples-unscaled"), zero)),
|
74
90
|
.timeline_wall_time_ns = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("timeline"), zero)),
|
91
|
+
.heap_sample = heap_sample == Qtrue,
|
75
92
|
};
|
76
93
|
|
77
94
|
long labels_count = RARRAY_LEN(labels_array) + RARRAY_LEN(numeric_labels_array);
|
@@ -99,32 +116,54 @@ static VALUE _native_sample(
|
|
99
116
|
};
|
100
117
|
}
|
101
118
|
|
102
|
-
int max_frames_requested = NUM2INT(max_frames);
|
103
|
-
if (max_frames_requested < 0) rb_raise(rb_eArgError, "Invalid max_frames: value must not be negative");
|
119
|
+
int max_frames_requested = sampling_buffer_check_max_frames(NUM2INT(max_frames));
|
104
120
|
|
105
|
-
|
121
|
+
ddog_prof_Location *locations = ruby_xcalloc(max_frames_requested, sizeof(ddog_prof_Location));
|
122
|
+
sampling_buffer *buffer = sampling_buffer_new(max_frames_requested, locations);
|
106
123
|
|
107
124
|
ddog_prof_Slice_Label slice_labels = {.ptr = labels, .len = labels_count};
|
108
125
|
|
109
|
-
|
126
|
+
struct native_sample_args args_struct = {
|
127
|
+
.in_gc = in_gc,
|
128
|
+
.recorder_instance = recorder_instance,
|
129
|
+
.values = values,
|
130
|
+
.labels = (sample_labels) {.labels = slice_labels, .state_label = state_label, .is_gvl_waiting_state = is_gvl_waiting_state == Qtrue},
|
131
|
+
.thread = thread,
|
132
|
+
.locations = locations,
|
133
|
+
.buffer = buffer,
|
134
|
+
};
|
135
|
+
|
136
|
+
return rb_ensure(native_sample_do, (VALUE) &args_struct, native_sample_ensure, (VALUE) &args_struct);
|
137
|
+
}
|
138
|
+
|
139
|
+
static VALUE native_sample_do(VALUE args) {
|
140
|
+
struct native_sample_args *args_struct = (struct native_sample_args *) args;
|
141
|
+
|
142
|
+
if (args_struct->in_gc == Qtrue) {
|
110
143
|
record_placeholder_stack(
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
(sample_labels) {.labels = slice_labels, .state_label = state_label},
|
144
|
+
args_struct->recorder_instance,
|
145
|
+
args_struct->values,
|
146
|
+
args_struct->labels,
|
115
147
|
DDOG_CHARSLICE_C("Garbage Collection")
|
116
148
|
);
|
117
149
|
} else {
|
118
150
|
sample_thread(
|
119
|
-
thread,
|
120
|
-
buffer,
|
121
|
-
recorder_instance,
|
122
|
-
values,
|
123
|
-
|
151
|
+
args_struct->thread,
|
152
|
+
args_struct->buffer,
|
153
|
+
args_struct->recorder_instance,
|
154
|
+
args_struct->values,
|
155
|
+
args_struct->labels
|
124
156
|
);
|
125
157
|
}
|
126
158
|
|
127
|
-
|
159
|
+
return Qtrue;
|
160
|
+
}
|
161
|
+
|
162
|
+
static VALUE native_sample_ensure(VALUE args) {
|
163
|
+
struct native_sample_args *args_struct = (struct native_sample_args *) args;
|
164
|
+
|
165
|
+
ruby_xfree(args_struct->locations);
|
166
|
+
sampling_buffer_free(args_struct->buffer);
|
128
167
|
|
129
168
|
return Qtrue;
|
130
169
|
}
|
@@ -151,47 +190,59 @@ void sample_thread(
|
|
151
190
|
thread,
|
152
191
|
0 /* stack starting depth */,
|
153
192
|
buffer->max_frames,
|
154
|
-
buffer->stack_buffer
|
155
|
-
buffer->lines_buffer,
|
156
|
-
buffer->is_ruby_frame
|
193
|
+
buffer->stack_buffer
|
157
194
|
);
|
158
195
|
|
159
196
|
if (captured_frames == PLACEHOLDER_STACK_IN_NATIVE_CODE) {
|
160
|
-
record_placeholder_stack_in_native_code(
|
197
|
+
record_placeholder_stack_in_native_code(recorder_instance, values, labels);
|
161
198
|
return;
|
162
199
|
}
|
163
200
|
|
201
|
+
// if (captured_frames > 0) {
|
202
|
+
// int cache_hits = 0;
|
203
|
+
// for (int i = 0; i < captured_frames; i++) {
|
204
|
+
// if (buffer->stack_buffer[i].same_frame) cache_hits++;
|
205
|
+
// }
|
206
|
+
// fprintf(stderr, "Sampling cache hits: %f\n", ((double) cache_hits / captured_frames) * 100);
|
207
|
+
// }
|
208
|
+
|
164
209
|
// Ruby does not give us path and line number for methods implemented using native code.
|
165
210
|
// The convention in Kernel#caller_locations is to instead use the path and line number of the first Ruby frame
|
166
211
|
// on the stack that is below (e.g. directly or indirectly has called) the native method.
|
167
212
|
// Thus, we keep that frame here to able to replicate that behavior.
|
168
|
-
// (This is why we also iterate the sampling buffers backwards below -- so that it's easier to keep the
|
169
|
-
VALUE
|
213
|
+
// (This is why we also iterate the sampling buffers backwards below -- so that it's easier to keep the last_ruby_frame_filename)
|
214
|
+
VALUE last_ruby_frame_filename = Qnil;
|
170
215
|
int last_ruby_line = 0;
|
171
216
|
|
172
217
|
ddog_prof_Label *state_label = labels.state_label;
|
173
218
|
bool cpu_or_wall_sample = values.cpu_or_wall_samples > 0;
|
174
219
|
bool has_cpu_time = cpu_or_wall_sample && values.cpu_time_ns > 0;
|
175
|
-
|
220
|
+
// Note: In theory, a cpu_or_wall_sample should always have some wall-time. In practice, the first sample for a thread
|
221
|
+
// will be zero, as well as if the system clock does something weird. Thus, at some point we had values.wall_time_ns > 0
|
222
|
+
// here, but >= 0 makes this easier to understand/debug.
|
223
|
+
bool only_wall_time = cpu_or_wall_sample && values.cpu_time_ns == 0 && values.wall_time_ns >= 0;
|
176
224
|
|
177
225
|
if (cpu_or_wall_sample && state_label == NULL) rb_raise(rb_eRuntimeError, "BUG: Unexpected missing state_label");
|
178
226
|
|
179
|
-
if (has_cpu_time)
|
227
|
+
if (has_cpu_time) {
|
228
|
+
state_label->str = DDOG_CHARSLICE_C("had cpu");
|
229
|
+
if (labels.is_gvl_waiting_state) rb_raise(rb_eRuntimeError, "BUG: Unexpected combination of cpu-time with is_gvl_waiting");
|
230
|
+
}
|
180
231
|
|
181
232
|
for (int i = captured_frames - 1; i >= 0; i--) {
|
182
233
|
VALUE name, filename;
|
183
234
|
int line;
|
184
235
|
|
185
|
-
if (buffer->
|
186
|
-
|
187
|
-
|
236
|
+
if (buffer->stack_buffer[i].is_ruby_frame) {
|
237
|
+
name = rb_iseq_base_label(buffer->stack_buffer[i].as.ruby_frame.iseq);
|
238
|
+
filename = rb_iseq_path(buffer->stack_buffer[i].as.ruby_frame.iseq);
|
239
|
+
line = buffer->stack_buffer[i].as.ruby_frame.line;
|
188
240
|
|
189
|
-
|
190
|
-
|
191
|
-
line = buffer->lines_buffer[i];
|
241
|
+
last_ruby_frame_filename = filename;
|
242
|
+
last_ruby_line = line;
|
192
243
|
} else {
|
193
|
-
name =
|
194
|
-
filename =
|
244
|
+
name = rb_id2str(buffer->stack_buffer[i].as.native_frame.method_id);
|
245
|
+
filename = last_ruby_frame_filename;
|
195
246
|
line = last_ruby_line;
|
196
247
|
}
|
197
248
|
|
@@ -206,12 +257,15 @@ void sample_thread(
|
|
206
257
|
bool top_of_the_stack = i == 0;
|
207
258
|
|
208
259
|
// When there's only wall-time in a sample, this means that the thread was not active in the sampled period.
|
209
|
-
//
|
210
|
-
// We try to categorize what it was doing based on what we observe at the top of the stack. This is a very rough
|
211
|
-
// approximation, and in the future we hope to replace this with a more accurate approach (such as using the
|
212
|
-
// GVL instrumentation API.)
|
213
260
|
if (top_of_the_stack && only_wall_time) {
|
214
|
-
|
261
|
+
// Did the caller already provide the state?
|
262
|
+
if (labels.is_gvl_waiting_state) {
|
263
|
+
state_label->str = DDOG_CHARSLICE_C("waiting for gvl");
|
264
|
+
|
265
|
+
// Otherwise, we try to categorize what the thread was doing based on what we observe at the top of the stack. This is a very rough
|
266
|
+
// approximation, and in the future we hope to replace this with a more accurate approach (such as using the
|
267
|
+
// GVL instrumentation API.)
|
268
|
+
} else if (!buffer->stack_buffer[i].is_ruby_frame) {
|
215
269
|
// We know that known versions of Ruby implement these using native code; thus if we find a method with the
|
216
270
|
// same name that is not native code, we ignore it, as it's probably a user method that coincidentally
|
217
271
|
// has the same name. Thus, even though "matching just by method name" is kinda weak,
|
@@ -246,10 +300,8 @@ void sample_thread(
|
|
246
300
|
}
|
247
301
|
|
248
302
|
buffer->locations[i] = (ddog_prof_Location) {
|
249
|
-
.
|
250
|
-
|
251
|
-
.filename = filename_slice,
|
252
|
-
},
|
303
|
+
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C("")},
|
304
|
+
.function = (ddog_prof_Function) {.name = name_slice, .filename = filename_slice},
|
253
305
|
.line = line,
|
254
306
|
};
|
255
307
|
}
|
@@ -287,7 +339,9 @@ static void maybe_trim_template_random_ids(ddog_CharSlice *name_slice, ddog_Char
|
|
287
339
|
// Check filename doesn't end with ".rb"; templates are usually along the lines of .html.erb/.html.haml/...
|
288
340
|
if (filename_slice->len < 3 || memcmp(filename_slice->ptr + filename_slice->len - 3, ".rb", 3) == 0) return;
|
289
341
|
|
290
|
-
|
342
|
+
if (name_slice->len > 1024) return;
|
343
|
+
|
344
|
+
int pos = ((int) name_slice->len) - 1;
|
291
345
|
|
292
346
|
// Let's match on something__number_number:
|
293
347
|
// Find start of id suffix from the end...
|
@@ -325,6 +379,7 @@ static void maybe_add_placeholder_frames_omitted(VALUE thread, sampling_buffer*
|
|
325
379
|
ddog_CharSlice function_name = DDOG_CHARSLICE_C("");
|
326
380
|
ddog_CharSlice function_filename = {.ptr = frames_omitted_message, .len = strlen(frames_omitted_message)};
|
327
381
|
buffer->locations[buffer->max_frames - 1] = (ddog_prof_Location) {
|
382
|
+
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C("")},
|
328
383
|
.function = (ddog_prof_Function) {.name = function_name, .filename = function_filename},
|
329
384
|
.line = 0,
|
330
385
|
};
|
@@ -352,13 +407,11 @@ static void maybe_add_placeholder_frames_omitted(VALUE thread, sampling_buffer*
|
|
352
407
|
// with one containing a placeholder frame, so that these threads are properly represented in the UX.
|
353
408
|
|
354
409
|
static void record_placeholder_stack_in_native_code(
|
355
|
-
sampling_buffer* buffer,
|
356
410
|
VALUE recorder_instance,
|
357
411
|
sample_values values,
|
358
412
|
sample_labels labels
|
359
413
|
) {
|
360
414
|
record_placeholder_stack(
|
361
|
-
buffer,
|
362
415
|
recorder_instance,
|
363
416
|
values,
|
364
417
|
labels,
|
@@ -367,36 +420,40 @@ static void record_placeholder_stack_in_native_code(
|
|
367
420
|
}
|
368
421
|
|
369
422
|
void record_placeholder_stack(
|
370
|
-
sampling_buffer* buffer,
|
371
423
|
VALUE recorder_instance,
|
372
424
|
sample_values values,
|
373
425
|
sample_labels labels,
|
374
426
|
ddog_CharSlice placeholder_stack
|
375
427
|
) {
|
376
|
-
|
377
|
-
|
428
|
+
ddog_prof_Location placeholder_location = {
|
429
|
+
.mapping = {.filename = DDOG_CHARSLICE_C(""), .build_id = DDOG_CHARSLICE_C("")},
|
430
|
+
.function = {.name = DDOG_CHARSLICE_C(""), .filename = placeholder_stack},
|
431
|
+
.line = 0,
|
432
|
+
};
|
378
433
|
|
379
434
|
record_sample(
|
380
435
|
recorder_instance,
|
381
|
-
(ddog_prof_Slice_Location) {.ptr =
|
436
|
+
(ddog_prof_Slice_Location) {.ptr = &placeholder_location, .len = 1},
|
382
437
|
values,
|
383
438
|
labels
|
384
439
|
);
|
385
440
|
}
|
386
441
|
|
387
|
-
|
442
|
+
uint16_t sampling_buffer_check_max_frames(int max_frames) {
|
388
443
|
if (max_frames < 5) rb_raise(rb_eArgError, "Invalid max_frames: value must be >= 5");
|
389
444
|
if (max_frames > MAX_FRAMES_LIMIT) rb_raise(rb_eArgError, "Invalid max_frames: value must be <= " MAX_FRAMES_LIMIT_AS_STRING);
|
445
|
+
return max_frames;
|
446
|
+
}
|
447
|
+
|
448
|
+
sampling_buffer *sampling_buffer_new(uint16_t max_frames, ddog_prof_Location *locations) {
|
449
|
+
sampling_buffer_check_max_frames(max_frames);
|
390
450
|
|
391
451
|
// Note: never returns NULL; if out of memory, it calls the Ruby out-of-memory handlers
|
392
452
|
sampling_buffer* buffer = ruby_xcalloc(1, sizeof(sampling_buffer));
|
393
453
|
|
394
454
|
buffer->max_frames = max_frames;
|
395
|
-
|
396
|
-
buffer->stack_buffer
|
397
|
-
buffer->lines_buffer = ruby_xcalloc(max_frames, sizeof(int));
|
398
|
-
buffer->is_ruby_frame = ruby_xcalloc(max_frames, sizeof(bool));
|
399
|
-
buffer->locations = ruby_xcalloc(max_frames, sizeof(ddog_prof_Location));
|
455
|
+
buffer->locations = locations;
|
456
|
+
buffer->stack_buffer = ruby_xcalloc(max_frames, sizeof(frame_info));
|
400
457
|
|
401
458
|
return buffer;
|
402
459
|
}
|
@@ -404,10 +461,8 @@ sampling_buffer *sampling_buffer_new(unsigned int max_frames) {
|
|
404
461
|
void sampling_buffer_free(sampling_buffer *buffer) {
|
405
462
|
if (buffer == NULL) rb_raise(rb_eArgError, "sampling_buffer_free called with NULL buffer");
|
406
463
|
|
464
|
+
// buffer->locations are owned by whoever called sampling_buffer_new, not us
|
407
465
|
ruby_xfree(buffer->stack_buffer);
|
408
|
-
ruby_xfree(buffer->lines_buffer);
|
409
|
-
ruby_xfree(buffer->is_ruby_frame);
|
410
|
-
ruby_xfree(buffer->locations);
|
411
466
|
|
412
467
|
ruby_xfree(buffer);
|
413
468
|
}
|
@@ -17,11 +17,11 @@ void sample_thread(
|
|
17
17
|
sample_labels labels
|
18
18
|
);
|
19
19
|
void record_placeholder_stack(
|
20
|
-
sampling_buffer* buffer,
|
21
20
|
VALUE recorder_instance,
|
22
21
|
sample_values values,
|
23
22
|
sample_labels labels,
|
24
23
|
ddog_CharSlice placeholder_stack
|
25
24
|
);
|
26
|
-
|
25
|
+
uint16_t sampling_buffer_check_max_frames(int max_frames);
|
26
|
+
sampling_buffer *sampling_buffer_new(uint16_t max_frames, ddog_prof_Location *locations);
|
27
27
|
void sampling_buffer_free(sampling_buffer *buffer);
|