ddtrace 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +63 -1
- data/ext/ddtrace_profiling_loader/ddtrace_profiling_loader.c +9 -2
- data/ext/ddtrace_profiling_loader/extconf.rb +17 -0
- data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +38 -2
- data/ext/ddtrace_profiling_native_extension/clock_id.h +1 -0
- data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +1 -0
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +517 -42
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.h +3 -0
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +208 -30
- data/ext/ddtrace_profiling_native_extension/collectors_stack.c +156 -46
- data/ext/ddtrace_profiling_native_extension/collectors_stack.h +11 -2
- data/ext/ddtrace_profiling_native_extension/extconf.rb +11 -1
- data/ext/ddtrace_profiling_native_extension/http_transport.c +83 -64
- data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +4 -4
- data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +3 -2
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +59 -0
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +3 -0
- data/ext/ddtrace_profiling_native_extension/profiling.c +10 -0
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +0 -1
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +4 -2
- data/ext/ddtrace_profiling_native_extension/stack_recorder.c +45 -29
- data/ext/ddtrace_profiling_native_extension/stack_recorder.h +7 -7
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +44 -18
- data/lib/datadog/appsec/event.rb +8 -4
- data/lib/datadog/core/configuration/components.rb +20 -14
- data/lib/datadog/core/configuration/settings.rb +59 -7
- data/lib/datadog/core/diagnostics/environment_logger.rb +5 -1
- data/lib/datadog/core/utils/compression.rb +5 -1
- data/lib/datadog/core.rb +0 -54
- data/lib/datadog/profiling/collectors/cpu_and_wall_time.rb +12 -2
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +5 -3
- data/lib/datadog/profiling/exporter.rb +2 -4
- data/lib/datadog/profiling/http_transport.rb +1 -1
- data/lib/datadog/tracing/client_ip.rb +11 -0
- data/lib/datadog/tracing/configuration/ext.rb +3 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +4 -0
- data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +3 -0
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +2 -0
- data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +2 -0
- data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -0
- data/lib/datadog/tracing/contrib/ext.rb +6 -0
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -0
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +5 -0
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +7 -1
- data/lib/datadog/tracing/contrib/grpc/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +47 -0
- data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +22 -0
- data/lib/datadog/tracing/contrib/hanami/ext.rb +24 -0
- data/lib/datadog/tracing/contrib/hanami/integration.rb +44 -0
- data/lib/datadog/tracing/contrib/hanami/patcher.rb +33 -0
- data/lib/datadog/tracing/contrib/hanami/plugin.rb +23 -0
- data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +41 -0
- data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +44 -0
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +7 -0
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +4 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +12 -0
- data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +16 -0
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +12 -0
- data/lib/datadog/tracing/contrib/pg/ext.rb +2 -1
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +34 -18
- data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +43 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +32 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +28 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +49 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +15 -7
- data/lib/datadog/tracing/contrib/redis/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/redis/instrumentation.rb +4 -2
- data/lib/datadog/tracing/contrib/redis/patcher.rb +41 -0
- data/lib/datadog/tracing/contrib/redis/tags.rb +5 -0
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +2 -0
- data/lib/datadog/tracing/contrib/sinatra/env.rb +12 -23
- data/lib/datadog/tracing/contrib/sinatra/ext.rb +7 -3
- data/lib/datadog/tracing/contrib/sinatra/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +8 -80
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +14 -9
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +84 -0
- data/lib/datadog/tracing/distributed/headers/datadog.rb +122 -30
- data/lib/datadog/tracing/distributed/headers/ext.rb +2 -0
- data/lib/datadog/tracing/flush.rb +1 -1
- data/lib/datadog/tracing/metadata/ext.rb +8 -0
- data/lib/datadog/tracing/propagation/http.rb +9 -1
- data/lib/datadog/tracing/sampling/ext.rb +31 -0
- data/lib/datadog/tracing/sampling/priority_sampler.rb +46 -4
- data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +8 -9
- data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +29 -5
- data/lib/datadog/tracing/sampling/rate_sampler.rb +10 -3
- data/lib/datadog/tracing/sampling/rule_sampler.rb +4 -3
- data/lib/datadog/tracing/sampling/span/ext.rb +0 -4
- data/lib/datadog/tracing/sampling/span/rule.rb +1 -1
- data/lib/datadog/tracing/sampling/span/sampler.rb +14 -3
- data/lib/datadog/tracing/trace_digest.rb +3 -0
- data/lib/datadog/tracing/trace_operation.rb +10 -0
- data/lib/datadog/tracing/trace_segment.rb +6 -0
- data/lib/datadog/tracing/tracer.rb +3 -1
- data/lib/datadog/tracing/writer.rb +7 -0
- data/lib/ddtrace/transport/trace_formatter.rb +7 -0
- data/lib/ddtrace/transport/traces.rb +1 -1
- data/lib/ddtrace/version.rb +2 -2
- metadata +18 -14
- data/lib/datadog/profiling/old_ext.rb +0 -42
- data/lib/datadog/profiling/transport/http/api/endpoint.rb +0 -85
- data/lib/datadog/profiling/transport/http/api/instance.rb +0 -38
- data/lib/datadog/profiling/transport/http/api/spec.rb +0 -42
- data/lib/datadog/profiling/transport/http/api.rb +0 -45
- data/lib/datadog/profiling/transport/http/builder.rb +0 -30
- data/lib/datadog/profiling/transport/http/client.rb +0 -37
- data/lib/datadog/profiling/transport/http/response.rb +0 -21
- data/lib/datadog/profiling/transport/http.rb +0 -118
@@ -7,7 +7,7 @@
|
|
7
7
|
#include "libdatadog_helpers.h"
|
8
8
|
#include "ruby_helpers.h"
|
9
9
|
|
10
|
-
// Used to wrap a
|
10
|
+
// Used to wrap a ddog_Profile in a Ruby object and expose Ruby-level serialization APIs
|
11
11
|
// This file implements the native bits of the Datadog::Profiling::StackRecorder class
|
12
12
|
|
13
13
|
// ---
|
@@ -24,7 +24,7 @@
|
|
24
24
|
// 2. The thread that serializes and reports profiles, let's call it the **serializer thread**. We enforce that there
|
25
25
|
// cannot be more than one thread attempting to serialize profiles at a time.
|
26
26
|
//
|
27
|
-
// If both the sampler and serializer threads are trying to access the same `
|
27
|
+
// If both the sampler and serializer threads are trying to access the same `ddog_Profile` in parallel, we will
|
28
28
|
// have a concurrency issue. Thus, the StackRecorder has an added mechanism to avoid this.
|
29
29
|
//
|
30
30
|
// As an additional constraint, the **sampler thread** has absolute priority and must never block while
|
@@ -32,7 +32,7 @@
|
|
32
32
|
//
|
33
33
|
// ### The solution: Keep two profiles at the same time
|
34
34
|
//
|
35
|
-
// To solve for the constraints above, the StackRecorder keeps two `
|
35
|
+
// To solve for the constraints above, the StackRecorder keeps two `ddog_Profile` profile instances inside itself.
|
36
36
|
// They are called the `slot_one_profile` and `slot_two_profile`.
|
37
37
|
//
|
38
38
|
// Each profile is paired with its own mutex. `slot_one_profile` is protected by `slot_one_mutex` and `slot_two_profile`
|
@@ -135,10 +135,10 @@ static VALUE stack_recorder_class = Qnil;
|
|
135
135
|
// Contains native state for each instance
|
136
136
|
struct stack_recorder_state {
|
137
137
|
pthread_mutex_t slot_one_mutex;
|
138
|
-
|
138
|
+
ddog_Profile *slot_one_profile;
|
139
139
|
|
140
140
|
pthread_mutex_t slot_two_mutex;
|
141
|
-
|
141
|
+
ddog_Profile *slot_two_profile;
|
142
142
|
|
143
143
|
short active_slot; // MUST NEVER BE ACCESSED FROM record_sample; this is NOT for the sampler thread to use.
|
144
144
|
};
|
@@ -146,7 +146,7 @@ struct stack_recorder_state {
|
|
146
146
|
// Used to return a pair of values from sampler_lock_active_profile()
|
147
147
|
struct active_slot_pair {
|
148
148
|
pthread_mutex_t *mutex;
|
149
|
-
|
149
|
+
ddog_Profile *profile;
|
150
150
|
};
|
151
151
|
|
152
152
|
struct call_serialize_without_gvl_arguments {
|
@@ -154,8 +154,8 @@ struct call_serialize_without_gvl_arguments {
|
|
154
154
|
struct stack_recorder_state *state;
|
155
155
|
|
156
156
|
// Set by callee
|
157
|
-
|
158
|
-
|
157
|
+
ddog_Profile *profile;
|
158
|
+
ddog_SerializeResult result;
|
159
159
|
|
160
160
|
// Set by both
|
161
161
|
bool serialize_ran;
|
@@ -164,15 +164,16 @@ struct call_serialize_without_gvl_arguments {
|
|
164
164
|
static VALUE _native_new(VALUE klass);
|
165
165
|
static void stack_recorder_typed_data_free(void *data);
|
166
166
|
static VALUE _native_serialize(VALUE self, VALUE recorder_instance);
|
167
|
-
static VALUE ruby_time_from(
|
167
|
+
static VALUE ruby_time_from(ddog_Timespec ddprof_time);
|
168
168
|
static void *call_serialize_without_gvl(void *call_args);
|
169
169
|
static struct active_slot_pair sampler_lock_active_profile();
|
170
170
|
static void sampler_unlock_active_profile(struct active_slot_pair active_slot);
|
171
|
-
static
|
171
|
+
static ddog_Profile *serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state, ddog_Timespec start_timestamp_for_next_profile);
|
172
172
|
static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
173
173
|
static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
174
174
|
static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
|
175
175
|
static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot);
|
176
|
+
static ddog_Timespec time_now();
|
176
177
|
|
177
178
|
void stack_recorder_init(VALUE profiling_module) {
|
178
179
|
stack_recorder_class = rb_define_class_under(profiling_module, "StackRecorder", rb_cObject);
|
@@ -199,7 +200,7 @@ void stack_recorder_init(VALUE profiling_module) {
|
|
199
200
|
ruby_time_from_id = rb_intern_const("ruby_time_from");
|
200
201
|
}
|
201
202
|
|
202
|
-
// This structure is used to define a Ruby object that stores a pointer to a
|
203
|
+
// This structure is used to define a Ruby object that stores a pointer to a ddog_Profile instance
|
203
204
|
// See also https://github.com/ruby/ruby/blob/master/doc/extension.rdoc for how this works
|
204
205
|
static const rb_data_type_t stack_recorder_typed_data = {
|
205
206
|
.wrap_struct_name = "Datadog::Profiling::StackRecorder",
|
@@ -214,7 +215,7 @@ static const rb_data_type_t stack_recorder_typed_data = {
|
|
214
215
|
static VALUE _native_new(VALUE klass) {
|
215
216
|
struct stack_recorder_state *state = ruby_xcalloc(1, sizeof(struct stack_recorder_state));
|
216
217
|
|
217
|
-
|
218
|
+
ddog_Slice_value_type sample_types = {.ptr = enabled_value_types, .len = ENABLED_VALUE_TYPES_COUNT};
|
218
219
|
|
219
220
|
state->slot_one_mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
220
221
|
state->slot_two_mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
@@ -227,8 +228,8 @@ static VALUE _native_new(VALUE klass) {
|
|
227
228
|
|
228
229
|
// Note: Don't raise exceptions after this point, since it'll lead to libdatadog memory leaking!
|
229
230
|
|
230
|
-
state->slot_one_profile =
|
231
|
-
state->slot_two_profile =
|
231
|
+
state->slot_one_profile = ddog_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
|
232
|
+
state->slot_two_profile = ddog_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
|
232
233
|
|
233
234
|
return TypedData_Wrap_Struct(klass, &stack_recorder_typed_data, state);
|
234
235
|
}
|
@@ -237,10 +238,10 @@ static void stack_recorder_typed_data_free(void *state_ptr) {
|
|
237
238
|
struct stack_recorder_state *state = (struct stack_recorder_state *) state_ptr;
|
238
239
|
|
239
240
|
pthread_mutex_destroy(&state->slot_one_mutex);
|
240
|
-
|
241
|
+
ddog_Profile_free(state->slot_one_profile);
|
241
242
|
|
242
243
|
pthread_mutex_destroy(&state->slot_two_mutex);
|
243
|
-
|
244
|
+
ddog_Profile_free(state->slot_two_profile);
|
244
245
|
|
245
246
|
ruby_xfree(state);
|
246
247
|
}
|
@@ -267,33 +268,33 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
|
|
267
268
|
rb_thread_call_without_gvl2(call_serialize_without_gvl, &args, NULL /* No interruption function needed in this case */, NULL /* Not needed */);
|
268
269
|
}
|
269
270
|
|
270
|
-
|
271
|
+
ddog_SerializeResult serialized_profile = args.result;
|
271
272
|
|
272
|
-
if (serialized_profile.tag ==
|
273
|
+
if (serialized_profile.tag == DDOG_SERIALIZE_RESULT_ERR) {
|
273
274
|
VALUE err_details = ruby_string_from_vec_u8(serialized_profile.err);
|
274
|
-
|
275
|
+
ddog_SerializeResult_drop(serialized_profile);
|
275
276
|
return rb_ary_new_from_args(2, error_symbol, err_details);
|
276
277
|
}
|
277
278
|
|
278
279
|
VALUE encoded_pprof = ruby_string_from_vec_u8(serialized_profile.ok.buffer);
|
279
280
|
|
280
|
-
|
281
|
-
|
281
|
+
ddog_Timespec ddprof_start = serialized_profile.ok.start;
|
282
|
+
ddog_Timespec ddprof_finish = serialized_profile.ok.end;
|
282
283
|
|
283
284
|
// Clean up libdatadog object to avoid leaking in case ruby_time_from raises an exception
|
284
|
-
|
285
|
+
ddog_SerializeResult_drop(serialized_profile);
|
285
286
|
|
286
287
|
VALUE start = ruby_time_from(ddprof_start);
|
287
288
|
VALUE finish = ruby_time_from(ddprof_finish);
|
288
289
|
|
289
|
-
if (!
|
290
|
+
if (!ddog_Profile_reset(args.profile, NULL /* start_time is optional */ )) {
|
290
291
|
return rb_ary_new_from_args(2, error_symbol, rb_str_new_cstr("Failed to reset profile"));
|
291
292
|
}
|
292
293
|
|
293
294
|
return rb_ary_new_from_args(2, ok_symbol, rb_ary_new_from_args(3, start, finish, encoded_pprof));
|
294
295
|
}
|
295
296
|
|
296
|
-
static VALUE ruby_time_from(
|
297
|
+
static VALUE ruby_time_from(ddog_Timespec ddprof_time) {
|
297
298
|
#ifndef NO_RB_TIME_TIMESPEC_NEW // Modern Rubies
|
298
299
|
const int utc = INT_MAX - 1; // From Ruby sources
|
299
300
|
struct timespec time = {.tv_sec = ddprof_time.seconds, .tv_nsec = ddprof_time.nanoseconds};
|
@@ -303,13 +304,13 @@ static VALUE ruby_time_from(ddprof_ffi_Timespec ddprof_time) {
|
|
303
304
|
#endif
|
304
305
|
}
|
305
306
|
|
306
|
-
void record_sample(VALUE recorder_instance,
|
307
|
+
void record_sample(VALUE recorder_instance, ddog_Sample sample) {
|
307
308
|
struct stack_recorder_state *state;
|
308
309
|
TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
|
309
310
|
|
310
311
|
struct active_slot_pair active_slot = sampler_lock_active_profile(state);
|
311
312
|
|
312
|
-
|
313
|
+
ddog_Profile_add(active_slot.profile, sample);
|
313
314
|
|
314
315
|
sampler_unlock_active_profile(active_slot);
|
315
316
|
}
|
@@ -317,8 +318,10 @@ void record_sample(VALUE recorder_instance, ddprof_ffi_Sample sample) {
|
|
317
318
|
static void *call_serialize_without_gvl(void *call_args) {
|
318
319
|
struct call_serialize_without_gvl_arguments *args = (struct call_serialize_without_gvl_arguments *) call_args;
|
319
320
|
|
320
|
-
|
321
|
-
|
321
|
+
ddog_Timespec finish_timestamp = time_now();
|
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 */);
|
322
325
|
args->serialize_ran = true;
|
323
326
|
|
324
327
|
return NULL; // Unused
|
@@ -357,7 +360,7 @@ static void sampler_unlock_active_profile(struct active_slot_pair active_slot) {
|
|
357
360
|
if (error != 0) rb_syserr_fail(error, "Unexpected failure in sampler_unlock_active_profile");
|
358
361
|
}
|
359
362
|
|
360
|
-
static
|
363
|
+
static ddog_Profile *serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state, ddog_Timespec start_timestamp_for_next_profile) {
|
361
364
|
int error;
|
362
365
|
int previously_active_slot = state->active_slot;
|
363
366
|
|
@@ -368,6 +371,10 @@ static ddprof_ffi_Profile *serializer_flip_active_and_inactive_slots(struct stac
|
|
368
371
|
pthread_mutex_t *previously_active = (previously_active_slot == 1) ? &state->slot_one_mutex : &state->slot_two_mutex;
|
369
372
|
pthread_mutex_t *previously_inactive = (previously_active_slot == 1) ? &state->slot_two_mutex : &state->slot_one_mutex;
|
370
373
|
|
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
|
+
|
371
378
|
// Release the lock, thus making this slot active
|
372
379
|
error = pthread_mutex_unlock(previously_inactive);
|
373
380
|
if (error) rb_syserr_fail(error, "Unexpected failure during serializer_flip_active_and_inactive_slots for previously_inactive");
|
@@ -420,3 +427,12 @@ static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot) {
|
|
420
427
|
rb_syserr_fail(error, "Unexpected failure when checking mutex state");
|
421
428
|
}
|
422
429
|
}
|
430
|
+
|
431
|
+
// Note that this is using CLOCK_REALTIME (e.g. actual time since unix epoch) and not the CLOCK_MONOTONIC as we use in other parts of the codebase
|
432
|
+
static ddog_Timespec time_now() {
|
433
|
+
struct timespec current_time;
|
434
|
+
|
435
|
+
if (clock_gettime(CLOCK_REALTIME, ¤t_time) != 0) rb_sys_fail("Failed to read CLOCK_REALTIME");
|
436
|
+
|
437
|
+
return (ddog_Timespec) {.seconds = current_time.tv_sec, .nanoseconds = (uint32_t) current_time.tv_nsec};
|
438
|
+
}
|
@@ -1,16 +1,16 @@
|
|
1
1
|
#pragma once
|
2
2
|
|
3
|
-
#include <
|
3
|
+
#include <datadog/profiling.h>
|
4
4
|
|
5
|
-
// Note: Please DO NOT use `VALUE_STRING` anywhere else, instead use `
|
5
|
+
// Note: Please DO NOT use `VALUE_STRING` anywhere else, instead use `DDOG_CHARSLICE_C`.
|
6
6
|
// `VALUE_STRING` is only needed because older versions of gcc (4.9.2, used in our Ruby 2.2 CI test images)
|
7
7
|
// tripped when compiling `enabled_value_types` using `-std=gnu99` due to the extra cast that is included in
|
8
|
-
// `
|
8
|
+
// `DDOG_CHARSLICE_C` with the following error:
|
9
9
|
//
|
10
10
|
// ```
|
11
11
|
// compiling ../../../../ext/ddtrace_profiling_native_extension/stack_recorder.c
|
12
12
|
// ../../../../ext/ddtrace_profiling_native_extension/stack_recorder.c:23:1: error: initializer element is not constant
|
13
|
-
// static const
|
13
|
+
// static const ddog_ValueType enabled_value_types[] = {CPU_TIME_VALUE, CPU_SAMPLES_VALUE, WALL_TIME_VALUE};
|
14
14
|
// ^
|
15
15
|
// ```
|
16
16
|
#define VALUE_STRING(string) {.ptr = "" string, .len = sizeof(string) - 1}
|
@@ -23,7 +23,7 @@
|
|
23
23
|
#define HEAP_LIVE_SIZE_VALUE {.type_ = VALUE_STRING("heap-live-size"), .unit = VALUE_STRING("bytes")}
|
24
24
|
#define HEAP_LIVE_SAMPLES_VALUE {.type_ = VALUE_STRING("heap-live-samples"), .unit = VALUE_STRING("count")}
|
25
25
|
|
26
|
-
static const
|
26
|
+
static const ddog_ValueType enabled_value_types[] = {
|
27
27
|
#define CPU_TIME_VALUE_POS 0
|
28
28
|
CPU_TIME_VALUE,
|
29
29
|
#define CPU_SAMPLES_VALUE_POS 1
|
@@ -32,7 +32,7 @@ static const ddprof_ffi_ValueType enabled_value_types[] = {
|
|
32
32
|
WALL_TIME_VALUE
|
33
33
|
};
|
34
34
|
|
35
|
-
#define ENABLED_VALUE_TYPES_COUNT (sizeof(enabled_value_types) / sizeof(
|
35
|
+
#define ENABLED_VALUE_TYPES_COUNT (sizeof(enabled_value_types) / sizeof(ddog_ValueType))
|
36
36
|
|
37
|
-
void record_sample(VALUE recorder_instance,
|
37
|
+
void record_sample(VALUE recorder_instance, ddog_Sample sample);
|
38
38
|
VALUE enforce_recorder_instance(VALUE object);
|
@@ -6,6 +6,9 @@ require_relative '../../instrumentation/gateway'
|
|
6
6
|
require_relative '../../processor'
|
7
7
|
require_relative '../../assets'
|
8
8
|
|
9
|
+
require_relative '../../../tracing/client_ip'
|
10
|
+
require_relative '../../../tracing/contrib/rack/header_collection'
|
11
|
+
|
9
12
|
module Datadog
|
10
13
|
module AppSec
|
11
14
|
module Contrib
|
@@ -30,7 +33,7 @@ module Datadog
|
|
30
33
|
env['datadog.waf.context'] = context
|
31
34
|
request = ::Rack::Request.new(env)
|
32
35
|
|
33
|
-
add_appsec_tags
|
36
|
+
add_appsec_tags(active_trace, active_span, env)
|
34
37
|
|
35
38
|
request_return, request_response = Instrumentation.gateway.push('rack.request', request) do
|
36
39
|
@app.call(env)
|
@@ -56,7 +59,7 @@ module Datadog
|
|
56
59
|
|
57
60
|
request_return
|
58
61
|
ensure
|
59
|
-
add_waf_runtime_tags(context) if context
|
62
|
+
add_waf_runtime_tags(active_trace, context) if context
|
60
63
|
context.finalize if context
|
61
64
|
end
|
62
65
|
|
@@ -70,41 +73,64 @@ module Datadog
|
|
70
73
|
Datadog::Tracing.active_trace
|
71
74
|
end
|
72
75
|
|
73
|
-
def
|
74
|
-
|
76
|
+
def active_span
|
77
|
+
# TODO: factor out tracing availability detection
|
78
|
+
|
79
|
+
return unless defined?(Datadog::Tracing)
|
80
|
+
|
81
|
+
Datadog::Tracing.active_span
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_appsec_tags(trace, span, env)
|
85
|
+
return unless trace
|
86
|
+
|
87
|
+
trace.set_tag('_dd.appsec.enabled', 1)
|
88
|
+
trace.set_tag('_dd.runtime_family', 'ruby')
|
89
|
+
trace.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
|
75
90
|
|
76
|
-
|
77
|
-
|
78
|
-
|
91
|
+
if span && span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).nil?
|
92
|
+
request_header_collection = Datadog::Tracing::Contrib::Rack::Header::RequestHeaderCollection.new(env)
|
93
|
+
|
94
|
+
# always collect client ip, as this is part of AppSec provided functionality
|
95
|
+
Datadog::Tracing::ClientIp.set_client_ip_tag!(
|
96
|
+
span,
|
97
|
+
headers: request_header_collection,
|
98
|
+
remote_ip: env['REMOTE_ADDR']
|
99
|
+
)
|
100
|
+
end
|
79
101
|
|
80
102
|
if @processor.ruleset_info
|
81
|
-
|
103
|
+
trace.set_tag('_dd.appsec.event_rules.version', @processor.ruleset_info[:version])
|
82
104
|
|
83
105
|
unless @oneshot_tags_sent
|
84
106
|
# Small race condition, but it's inoccuous: worst case the tags
|
85
107
|
# are sent a couple of times more than expected
|
86
108
|
@oneshot_tags_sent = true
|
87
109
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
110
|
+
trace.set_tag('_dd.appsec.event_rules.loaded', @processor.ruleset_info[:loaded].to_f)
|
111
|
+
trace.set_tag('_dd.appsec.event_rules.error_count', @processor.ruleset_info[:failed].to_f)
|
112
|
+
trace.set_tag('_dd.appsec.event_rules.errors', JSON.dump(@processor.ruleset_info[:errors]))
|
113
|
+
trace.set_tag('_dd.appsec.event_rules.addresses', JSON.dump(@processor.addresses))
|
92
114
|
|
93
115
|
# Ensure these tags reach the backend
|
94
|
-
|
116
|
+
trace.keep!
|
117
|
+
trace.set_tag(
|
118
|
+
Datadog::Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER,
|
119
|
+
Datadog::Tracing::Sampling::Ext::Decision::ASM
|
120
|
+
)
|
95
121
|
end
|
96
122
|
end
|
97
123
|
end
|
98
124
|
|
99
|
-
def add_waf_runtime_tags(context)
|
100
|
-
return unless
|
125
|
+
def add_waf_runtime_tags(trace, context)
|
126
|
+
return unless trace
|
101
127
|
return unless context
|
102
128
|
|
103
|
-
|
129
|
+
trace.set_tag('_dd.appsec.waf.timeouts', context.timeouts)
|
104
130
|
|
105
131
|
# these tags expect time in us
|
106
|
-
|
107
|
-
|
132
|
+
trace.set_tag('_dd.appsec.waf.duration', context.time_ns / 1000.0)
|
133
|
+
trace.set_tag('_dd.appsec.waf.duration_ext', context.time_ext_ns / 1000.0)
|
108
134
|
end
|
109
135
|
end
|
110
136
|
end
|
data/lib/datadog/appsec/event.rb
CHANGED
@@ -51,7 +51,8 @@ module Datadog
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
|
54
|
+
# rubocop:disable Metrics/MethodLength
|
55
|
+
def self.record_via_span(*events) # rubocop:disable Metrics/AbcSize
|
55
56
|
events.group_by { |e| e[:trace] }.each do |trace, event_group|
|
56
57
|
unless trace
|
57
58
|
Datadog.logger.debug { "{ error: 'no trace: cannot record', event_group: #{event_group.inspect}}" }
|
@@ -59,6 +60,10 @@ module Datadog
|
|
59
60
|
end
|
60
61
|
|
61
62
|
trace.keep!
|
63
|
+
trace.set_tag(
|
64
|
+
Datadog::Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER,
|
65
|
+
Datadog::Tracing::Sampling::Ext::Decision::ASM
|
66
|
+
)
|
62
67
|
|
63
68
|
# prepare and gather tags to apply
|
64
69
|
trace_tags = event_group.each_with_object({}) do |event, tags|
|
@@ -75,9 +80,7 @@ module Datadog
|
|
75
80
|
|
76
81
|
tags['http.host'] = request.host
|
77
82
|
tags['http.useragent'] = request.user_agent
|
78
|
-
tags['network.client.ip'] = request.
|
79
|
-
|
80
|
-
# tags['actor.ip'] = request.ip # TODO: uses client IP resolution algorithm
|
83
|
+
tags['network.client.ip'] = request.env['REMOTE_ADDR'] if request.env['REMOTE_ADDR']
|
81
84
|
end
|
82
85
|
|
83
86
|
if (response = event[:response])
|
@@ -108,6 +111,7 @@ module Datadog
|
|
108
111
|
end
|
109
112
|
end
|
110
113
|
end
|
114
|
+
# rubocop:enable Metrics/MethodLength
|
111
115
|
end
|
112
116
|
end
|
113
117
|
end
|
@@ -181,7 +181,7 @@ module Datadog
|
|
181
181
|
|
182
182
|
next unless response && !response.internal_error? && response.service_rates
|
183
183
|
|
184
|
-
sampler.update(response.service_rates)
|
184
|
+
sampler.update(response.service_rates, decision: Tracing::Sampling::Ext::Decision::AGENT_RATE)
|
185
185
|
end
|
186
186
|
end
|
187
187
|
|
@@ -251,7 +251,9 @@ module Datadog
|
|
251
251
|
recorder = Datadog::Profiling::StackRecorder.new
|
252
252
|
collector = Datadog::Profiling::Collectors::CpuAndWallTimeWorker.new(
|
253
253
|
recorder: recorder,
|
254
|
-
max_frames: settings.profiling.advanced.max_frames
|
254
|
+
max_frames: settings.profiling.advanced.max_frames,
|
255
|
+
tracer: tracer,
|
256
|
+
gc_profiling_enabled: should_enable_gc_profiling?(settings)
|
255
257
|
)
|
256
258
|
else
|
257
259
|
trace_identifiers_helper = Profiling::TraceIdentifiers::Helper.new(
|
@@ -320,18 +322,6 @@ module Datadog
|
|
320
322
|
|
321
323
|
def build_profiler_transport(settings, agent_settings)
|
322
324
|
settings.profiling.exporter.transport ||
|
323
|
-
if settings.profiling.advanced.legacy_transport_enabled
|
324
|
-
require_relative '../../profiling/transport/http'
|
325
|
-
|
326
|
-
Datadog.logger.warn('Using legacy profiling transport. Do not use unless instructed to by support.')
|
327
|
-
|
328
|
-
Profiling::Transport::HTTP.default(
|
329
|
-
agent_settings: agent_settings,
|
330
|
-
site: settings.site,
|
331
|
-
api_key: settings.api_key,
|
332
|
-
profiling_upload_timeout_seconds: settings.profiling.upload.timeout_seconds
|
333
|
-
)
|
334
|
-
end ||
|
335
325
|
Profiling::HttpTransport.new(
|
336
326
|
agent_settings: agent_settings,
|
337
327
|
site: settings.site,
|
@@ -339,6 +329,22 @@ module Datadog
|
|
339
329
|
upload_timeout_seconds: settings.profiling.upload.timeout_seconds,
|
340
330
|
)
|
341
331
|
end
|
332
|
+
|
333
|
+
def should_enable_gc_profiling?(settings)
|
334
|
+
return true if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3')
|
335
|
+
|
336
|
+
# See comments on the setting definition for more context on why it exists.
|
337
|
+
if settings.profiling.advanced.force_enable_gc_profiling
|
338
|
+
Datadog.logger.debug(
|
339
|
+
'Profiling time/resources spent in Garbage Collection force enabled. Do not use Ractors in combination ' \
|
340
|
+
'with this option as profiles will be incomplete.'
|
341
|
+
)
|
342
|
+
|
343
|
+
true
|
344
|
+
else
|
345
|
+
false
|
346
|
+
end
|
347
|
+
end
|
342
348
|
end
|
343
349
|
|
344
350
|
attr_reader \
|
@@ -234,11 +234,17 @@ module Datadog
|
|
234
234
|
# categorization of stack traces.
|
235
235
|
option :code_provenance_enabled, default: true
|
236
236
|
|
237
|
-
#
|
238
|
-
#
|
237
|
+
# No longer does anything, and will be removed on dd-trace-rb 2.0.
|
238
|
+
#
|
239
|
+
# This was added as a temporary support option in case of issues with the new `Profiling::HttpTransport` class
|
240
|
+
# but we're now confident it's working nicely so we've removed the old code path.
|
239
241
|
option :legacy_transport_enabled do |o|
|
240
|
-
o.
|
241
|
-
|
242
|
+
o.on_set do
|
243
|
+
Datadog.logger.warn(
|
244
|
+
'The profiling.advanced.legacy_transport_enabled setting has been deprecated for removal and no ' \
|
245
|
+
'longer does anything. Please remove it from your Datadog.configure block.'
|
246
|
+
)
|
247
|
+
end
|
242
248
|
end
|
243
249
|
|
244
250
|
# Forces enabling the new profiler. We do not yet recommend turning on this option.
|
@@ -250,6 +256,25 @@ module Datadog
|
|
250
256
|
o.default { env_to_bool('DD_PROFILING_FORCE_ENABLE_NEW', false) }
|
251
257
|
o.lazy
|
252
258
|
end
|
259
|
+
|
260
|
+
# Forces enabling of profiling of time/resources spent in Garbage Collection.
|
261
|
+
#
|
262
|
+
# Note that setting this to "false" (or not setting it) will not prevent the feature from being
|
263
|
+
# being automatically enabled in the future.
|
264
|
+
#
|
265
|
+
# This toggle was added because, although this feature is safe and enabled by default on Ruby 2.x,
|
266
|
+
# on Ruby 3.x it can break in applications that make use of Ractors due to two Ruby VM bugs:
|
267
|
+
# https://bugs.ruby-lang.org/issues/19112 AND https://bugs.ruby-lang.org/issues/18464.
|
268
|
+
#
|
269
|
+
# If you use Ruby 3.x and your application does not use Ractors (or if your Ruby has been patched), the
|
270
|
+
# feature is fully safe to enable and this toggle can be used to do so.
|
271
|
+
#
|
272
|
+
# We expect that once the above issue is patched, we'll automatically re-enable the feature on fixed Ruby
|
273
|
+
# versions.
|
274
|
+
option :force_enable_gc_profiling do |o|
|
275
|
+
o.default { env_to_bool('DD_PROFILING_FORCE_ENABLE_GC', false) }
|
276
|
+
o.lazy
|
277
|
+
end
|
253
278
|
end
|
254
279
|
|
255
280
|
# @public_api
|
@@ -667,13 +692,27 @@ module Datadog
|
|
667
692
|
# Whether client IP collection is enabled. When enabled client IPs from HTTP requests will
|
668
693
|
# be reported in traces.
|
669
694
|
#
|
695
|
+
# Usage of the DD_TRACE_CLIENT_IP_HEADER_DISABLED environment variable is deprecated.
|
696
|
+
#
|
670
697
|
# @see https://docs.datadoghq.com/tracing/configure_data_security#configuring-a-client-ip-header
|
671
698
|
#
|
672
|
-
# @default
|
673
|
-
# variable or `true` if it doesn't exist.
|
699
|
+
# @default `DD_TRACE_CLIENT_IP_ENABLED` environment variable, otherwise `false`.
|
674
700
|
# @return [Boolean]
|
675
701
|
option :enabled do |o|
|
676
|
-
o.default
|
702
|
+
o.default do
|
703
|
+
disabled = env_to_bool(Tracing::Configuration::Ext::ClientIp::ENV_DISABLED)
|
704
|
+
|
705
|
+
enabled = if disabled.nil?
|
706
|
+
false
|
707
|
+
else
|
708
|
+
Datadog.logger.warn { "#{Tracing::Configuration::Ext::ClientIp::ENV_DISABLED} environment variable is deprecated, found set to #{disabled}, use #{Tracing::Configuration::Ext::ClientIp::ENV_ENABLED}=#{!disabled}" }
|
709
|
+
|
710
|
+
!disabled
|
711
|
+
end
|
712
|
+
|
713
|
+
# ENABLED env var takes precedence over deprecated DISABLED
|
714
|
+
env_to_bool(Tracing::Configuration::Ext::ClientIp::ENV_ENABLED, enabled)
|
715
|
+
end
|
677
716
|
o.lazy
|
678
717
|
end
|
679
718
|
|
@@ -686,6 +725,19 @@ module Datadog
|
|
686
725
|
o.lazy
|
687
726
|
end
|
688
727
|
end
|
728
|
+
|
729
|
+
# Maximum size for the `x-datadog-tags` distributed trace tags header.
|
730
|
+
#
|
731
|
+
# If the serialized size of distributed trace tags is larger than this value, it will
|
732
|
+
# not be parsed if incoming, nor exported if outgoing. An error message will be logged
|
733
|
+
# in this case.
|
734
|
+
#
|
735
|
+
# @default `DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH` environment variable, otherwise `512`
|
736
|
+
# @return [Integer]
|
737
|
+
option :x_datadog_tags_max_length do |o|
|
738
|
+
o.default { env_to_int(Tracing::Configuration::Ext::Distributed::ENV_X_DATADOG_TAGS_MAX_LENGTH, 512) }
|
739
|
+
o.lazy
|
740
|
+
end
|
689
741
|
end
|
690
742
|
|
691
743
|
# The `version` tag in Datadog. Use it to enable [Deployment Tracking](https://docs.datadoghq.com/tracing/deployment_tracking/).
|
@@ -106,10 +106,14 @@ module Datadog
|
|
106
106
|
Datadog.configuration.version
|
107
107
|
end
|
108
108
|
|
109
|
-
# @return [String] target agent URL for trace flushing
|
109
|
+
# @return [String, nil] target agent URL for trace flushing
|
110
110
|
def agent_url
|
111
111
|
# Retrieve the effect agent URL, regardless of how it was configured
|
112
112
|
transport = Tracing.send(:tracer).writer.transport
|
113
|
+
|
114
|
+
# return `nil` with IO transport
|
115
|
+
return unless transport.respond_to?(:client)
|
116
|
+
|
113
117
|
adapter = transport.client.api.adapter
|
114
118
|
adapter.url
|
115
119
|
end
|
@@ -6,10 +6,13 @@ require 'zlib'
|
|
6
6
|
module Datadog
|
7
7
|
module Core
|
8
8
|
module Utils
|
9
|
-
#
|
9
|
+
# Compression/decompression utility functions.
|
10
|
+
#
|
11
|
+
# @deprecated This is no longer used by ddtrace and will be removed in 2.0.
|
10
12
|
module Compression
|
11
13
|
module_function
|
12
14
|
|
15
|
+
# @deprecated This is no longer used by ddtrace and will be removed in 2.0.
|
13
16
|
def gzip(string, level: nil, strategy: nil)
|
14
17
|
sio = StringIO.new
|
15
18
|
sio.binmode
|
@@ -19,6 +22,7 @@ module Datadog
|
|
19
22
|
sio.string
|
20
23
|
end
|
21
24
|
|
25
|
+
# @deprecated This is no longer used by ddtrace and will be removed in 2.0.
|
22
26
|
def gunzip(string, encoding = ::Encoding::ASCII_8BIT)
|
23
27
|
sio = StringIO.new(string)
|
24
28
|
gz = Zlib::GzipReader.new(sio, encoding: encoding)
|