ddtrace 1.5.2 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -1
  3. data/ext/ddtrace_profiling_loader/ddtrace_profiling_loader.c +9 -2
  4. data/ext/ddtrace_profiling_loader/extconf.rb +17 -0
  5. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +38 -2
  6. data/ext/ddtrace_profiling_native_extension/clock_id.h +1 -0
  7. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +1 -0
  8. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +517 -42
  9. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.h +3 -0
  10. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +208 -30
  11. data/ext/ddtrace_profiling_native_extension/collectors_stack.c +156 -46
  12. data/ext/ddtrace_profiling_native_extension/collectors_stack.h +11 -2
  13. data/ext/ddtrace_profiling_native_extension/extconf.rb +11 -1
  14. data/ext/ddtrace_profiling_native_extension/http_transport.c +83 -64
  15. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +4 -4
  16. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +3 -2
  17. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +59 -0
  18. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +3 -0
  19. data/ext/ddtrace_profiling_native_extension/profiling.c +10 -0
  20. data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +0 -1
  21. data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +4 -2
  22. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +45 -29
  23. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +7 -7
  24. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +4 -0
  25. data/lib/datadog/appsec/event.rb +6 -0
  26. data/lib/datadog/core/configuration/components.rb +20 -14
  27. data/lib/datadog/core/configuration/settings.rb +42 -4
  28. data/lib/datadog/core/diagnostics/environment_logger.rb +5 -1
  29. data/lib/datadog/core/utils/compression.rb +5 -1
  30. data/lib/datadog/core.rb +0 -54
  31. data/lib/datadog/profiling/collectors/cpu_and_wall_time.rb +12 -2
  32. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +5 -3
  33. data/lib/datadog/profiling/exporter.rb +2 -4
  34. data/lib/datadog/profiling/http_transport.rb +1 -1
  35. data/lib/datadog/tracing/configuration/ext.rb +1 -0
  36. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +2 -0
  37. data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
  38. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +4 -0
  39. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +2 -0
  40. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +3 -0
  41. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +2 -0
  42. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +2 -0
  43. data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -0
  44. data/lib/datadog/tracing/contrib/ext.rb +6 -0
  45. data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -0
  46. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +5 -0
  47. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +7 -1
  48. data/lib/datadog/tracing/contrib/grpc/ext.rb +2 -0
  49. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +47 -0
  50. data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +22 -0
  51. data/lib/datadog/tracing/contrib/hanami/ext.rb +24 -0
  52. data/lib/datadog/tracing/contrib/hanami/integration.rb +44 -0
  53. data/lib/datadog/tracing/contrib/hanami/patcher.rb +33 -0
  54. data/lib/datadog/tracing/contrib/hanami/plugin.rb +23 -0
  55. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +41 -0
  56. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +44 -0
  57. data/lib/datadog/tracing/contrib/http/instrumentation.rb +2 -0
  58. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +2 -0
  59. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +2 -0
  60. data/lib/datadog/tracing/contrib/mongodb/ext.rb +7 -0
  61. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +4 -0
  62. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +12 -0
  63. data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
  64. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +16 -0
  65. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +12 -0
  66. data/lib/datadog/tracing/contrib/pg/ext.rb +2 -1
  67. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +34 -18
  68. data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +43 -0
  69. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +32 -0
  70. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +28 -0
  71. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +49 -0
  72. data/lib/datadog/tracing/contrib/rack/middlewares.rb +11 -5
  73. data/lib/datadog/tracing/contrib/redis/ext.rb +2 -0
  74. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +4 -2
  75. data/lib/datadog/tracing/contrib/redis/patcher.rb +41 -0
  76. data/lib/datadog/tracing/contrib/redis/tags.rb +5 -0
  77. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +2 -0
  78. data/lib/datadog/tracing/contrib/sinatra/env.rb +12 -23
  79. data/lib/datadog/tracing/contrib/sinatra/ext.rb +7 -3
  80. data/lib/datadog/tracing/contrib/sinatra/patcher.rb +2 -2
  81. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +8 -80
  82. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +14 -9
  83. data/lib/datadog/tracing/contrib.rb +1 -0
  84. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +84 -0
  85. data/lib/datadog/tracing/distributed/headers/datadog.rb +122 -30
  86. data/lib/datadog/tracing/distributed/headers/ext.rb +2 -0
  87. data/lib/datadog/tracing/flush.rb +1 -1
  88. data/lib/datadog/tracing/metadata/ext.rb +8 -0
  89. data/lib/datadog/tracing/propagation/http.rb +9 -1
  90. data/lib/datadog/tracing/sampling/ext.rb +31 -0
  91. data/lib/datadog/tracing/sampling/priority_sampler.rb +46 -4
  92. data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +8 -9
  93. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +29 -5
  94. data/lib/datadog/tracing/sampling/rate_sampler.rb +10 -3
  95. data/lib/datadog/tracing/sampling/rule_sampler.rb +4 -3
  96. data/lib/datadog/tracing/sampling/span/ext.rb +0 -4
  97. data/lib/datadog/tracing/sampling/span/rule.rb +1 -1
  98. data/lib/datadog/tracing/sampling/span/sampler.rb +14 -3
  99. data/lib/datadog/tracing/trace_digest.rb +3 -0
  100. data/lib/datadog/tracing/trace_operation.rb +10 -0
  101. data/lib/datadog/tracing/trace_segment.rb +6 -0
  102. data/lib/datadog/tracing/tracer.rb +3 -1
  103. data/lib/datadog/tracing/writer.rb +7 -0
  104. data/lib/ddtrace/transport/trace_formatter.rb +7 -0
  105. data/lib/ddtrace/transport/traces.rb +1 -1
  106. data/lib/ddtrace/version.rb +2 -2
  107. metadata +18 -14
  108. data/lib/datadog/profiling/old_ext.rb +0 -42
  109. data/lib/datadog/profiling/transport/http/api/endpoint.rb +0 -85
  110. data/lib/datadog/profiling/transport/http/api/instance.rb +0 -38
  111. data/lib/datadog/profiling/transport/http/api/spec.rb +0 -42
  112. data/lib/datadog/profiling/transport/http/api.rb +0 -45
  113. data/lib/datadog/profiling/transport/http/builder.rb +0 -30
  114. data/lib/datadog/profiling/transport/http/client.rb +0 -37
  115. data/lib/datadog/profiling/transport/http/response.rb +0 -21
  116. 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 ddprof_ffi_Profile in a Ruby object and expose Ruby-level serialization APIs
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 `ddprof_ffi_Profile` in parallel, we will
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 `ddprof_ffi_Profile` profile instances inside itself.
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
- ddprof_ffi_Profile *slot_one_profile;
138
+ ddog_Profile *slot_one_profile;
139
139
 
140
140
  pthread_mutex_t slot_two_mutex;
141
- ddprof_ffi_Profile *slot_two_profile;
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
- ddprof_ffi_Profile *profile;
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
- ddprof_ffi_Profile *profile;
158
- ddprof_ffi_SerializeResult result;
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(ddprof_ffi_Timespec ddprof_time);
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 ddprof_ffi_Profile *serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state);
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 ddprof_ffi_Profile instance
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
- ddprof_ffi_Slice_value_type sample_types = {.ptr = enabled_value_types, .len = ENABLED_VALUE_TYPES_COUNT};
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 = ddprof_ffi_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
231
- state->slot_two_profile = ddprof_ffi_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
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
- ddprof_ffi_Profile_free(state->slot_one_profile);
241
+ ddog_Profile_free(state->slot_one_profile);
241
242
 
242
243
  pthread_mutex_destroy(&state->slot_two_mutex);
243
- ddprof_ffi_Profile_free(state->slot_two_profile);
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
- ddprof_ffi_SerializeResult serialized_profile = args.result;
271
+ ddog_SerializeResult serialized_profile = args.result;
271
272
 
272
- if (serialized_profile.tag == DDPROF_FFI_SERIALIZE_RESULT_ERR) {
273
+ if (serialized_profile.tag == DDOG_SERIALIZE_RESULT_ERR) {
273
274
  VALUE err_details = ruby_string_from_vec_u8(serialized_profile.err);
274
- ddprof_ffi_SerializeResult_drop(serialized_profile);
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
- ddprof_ffi_Timespec ddprof_start = serialized_profile.ok.start;
281
- ddprof_ffi_Timespec ddprof_finish = serialized_profile.ok.end;
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
- ddprof_ffi_SerializeResult_drop(serialized_profile);
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 (!ddprof_ffi_Profile_reset(args.profile, NULL /* start_time is optional */ )) {
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(ddprof_ffi_Timespec ddprof_time) {
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, ddprof_ffi_Sample sample) {
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
- ddprof_ffi_Profile_add(active_slot.profile, sample);
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
- args->profile = serializer_flip_active_and_inactive_slots(args->state);
321
- args->result = ddprof_ffi_Profile_serialize(args->profile, NULL /* end_time is optional */, NULL /* duration_nanos is optional */);
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 ddprof_ffi_Profile *serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state) {
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, &current_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 <ddprof/ffi.h>
3
+ #include <datadog/profiling.h>
4
4
 
5
- // Note: Please DO NOT use `VALUE_STRING` anywhere else, instead use `DDPROF_FFI_CHARSLICE_C`.
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
- // `DDPROF_FFI_CHARSLICE_C` with the following error:
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 ddprof_ffi_ValueType enabled_value_types[] = {CPU_TIME_VALUE, CPU_SAMPLES_VALUE, WALL_TIME_VALUE};
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 ddprof_ffi_ValueType enabled_value_types[] = {
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(ddprof_ffi_ValueType))
35
+ #define ENABLED_VALUE_TYPES_COUNT (sizeof(enabled_value_types) / sizeof(ddog_ValueType))
36
36
 
37
- void record_sample(VALUE recorder_instance, ddprof_ffi_Sample sample);
37
+ void record_sample(VALUE recorder_instance, ddog_Sample sample);
38
38
  VALUE enforce_recorder_instance(VALUE object);
@@ -114,6 +114,10 @@ module Datadog
114
114
 
115
115
  # Ensure these tags reach the backend
116
116
  trace.keep!
117
+ trace.set_tag(
118
+ Datadog::Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER,
119
+ Datadog::Tracing::Sampling::Ext::Decision::ASM
120
+ )
117
121
  end
118
122
  end
119
123
  end
@@ -51,6 +51,7 @@ module Datadog
51
51
  end
52
52
  end
53
53
 
54
+ # rubocop:disable Metrics/MethodLength
54
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
@@ -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|
@@ -106,6 +111,7 @@ module Datadog
106
111
  end
107
112
  end
108
113
  end
114
+ # rubocop:enable Metrics/MethodLength
109
115
  end
110
116
  end
111
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
- # Use legacy transport code instead of HttpTransport. Temporarily added for migration to HttpTransport,
238
- # and will be removed soon. Do not use unless instructed to by support.
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.default { env_to_bool('DD_PROFILING_LEGACY_TRANSPORT_ENABLED', false) }
241
- o.lazy
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
@@ -700,6 +725,19 @@ module Datadog
700
725
  o.lazy
701
726
  end
702
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
703
741
  end
704
742
 
705
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
- # Common database-related utility functions.
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)
data/lib/datadog/core.rb CHANGED
@@ -1,59 +1,5 @@
1
1
  # typed: strict
2
2
 
3
- # TODO: Move these requires to smaller modules.
4
- # Would be better to lazy load these; not
5
- # all of these components will be used in
6
- # every application.
7
- # require_relative 'core/buffer/cruby'
8
- # require_relative 'core/buffer/random'
9
- # require_relative 'core/buffer/thread_safe'
10
- # require_relative 'core/chunker'
11
- # require_relative 'core/configuration'
12
- # require_relative 'core/diagnostics/environment_logger'
13
- # require_relative 'core/diagnostics/ext'
14
- # require_relative 'core/diagnostics/health'
15
- # require_relative 'core/encoding'
16
- # require_relative 'core/environment/cgroup'
17
- # require_relative 'core/environment/class_count'
18
- # require_relative 'core/environment/container'
19
- # require_relative 'core/environment/ext'
20
- # require_relative 'core/environment/gc'
21
- # require_relative 'core/environment/identity'
22
- # require_relative 'core/environment/socket'
23
- # require_relative 'core/environment/thread_count'
24
- # require_relative 'core/environment/variable_helpers'
25
- # require_relative 'core/environment/vm_cache'
26
- # require_relative 'core/error'
27
- # require_relative 'core/event'
28
- # require_relative 'core/git/ext'
29
- # require_relative 'core/logger'
30
- # require_relative 'core/metrics/client'
31
- # require_relative 'core/metrics/ext'
32
- # require_relative 'core/metrics/helpers'
33
- # require_relative 'core/metrics/logging'
34
- # require_relative 'core/metrics/metric'
35
- # require_relative 'core/metrics/options'
36
- # require_relative 'core/pin'
37
- # require_relative 'core/quantization/hash'
38
- # require_relative 'core/quantization/http'
39
- # require_relative 'core/runtime/ext'
40
- # require_relative 'core/runtime/metrics'
41
- # require_relative 'core/utils'
42
- # require_relative 'core/utils/compression'
43
- # require_relative 'core/utils/database'
44
- # require_relative 'core/utils/forking'
45
- # require_relative 'core/utils/object_set'
46
- # require_relative 'core/utils/only_once'
47
- # require_relative 'core/utils/sequence'
48
- # require_relative 'core/utils/string_table'
49
- # require_relative 'core/utils/time'
50
- # require_relative 'core/worker'
51
- # require_relative 'core/workers/async'
52
- # require_relative 'core/workers/interval_loop'
53
- # require_relative 'core/workers/polling'
54
- # require_relative 'core/workers/queue'
55
- # require_relative 'core/workers/runtime_metrics'
56
-
57
3
  require_relative 'core/extensions'
58
4
 
59
5
  # We must load core extensions to make certain global APIs
@@ -11,8 +11,9 @@ module Datadog
11
11
  #
12
12
  # Methods prefixed with _native_ are implemented in `collectors_cpu_and_wall_time.c`
13
13
  class CpuAndWallTime
14
- def initialize(recorder:, max_frames:)
15
- self.class._native_initialize(self, recorder, max_frames)
14
+ def initialize(recorder:, max_frames:, tracer:)
15
+ tracer_context_key = safely_extract_context_key_from(tracer)
16
+ self.class._native_initialize(self, recorder, max_frames, tracer_context_key)
16
17
  end
17
18
 
18
19
  def inspect
@@ -21,6 +22,15 @@ module Datadog
21
22
  result[-1] = "#{self.class._native_inspect(self)}>"
22
23
  result
23
24
  end
25
+
26
+ private
27
+
28
+ def safely_extract_context_key_from(tracer)
29
+ tracer &&
30
+ tracer.respond_to?(:provider) &&
31
+ # NOTE: instance_variable_get always works, even on nil -- it just returns nil if the variable doesn't exist
32
+ tracer.provider.instance_variable_get(:@context).instance_variable_get(:@key)
33
+ end
24
34
  end
25
35
  end
26
36
  end
@@ -18,9 +18,11 @@ module Datadog
18
18
  def initialize(
19
19
  recorder:,
20
20
  max_frames:,
21
- cpu_and_wall_time_collector: CpuAndWallTime.new(recorder: recorder, max_frames: max_frames)
21
+ tracer:,
22
+ gc_profiling_enabled:,
23
+ cpu_and_wall_time_collector: CpuAndWallTime.new(recorder: recorder, max_frames: max_frames, tracer: tracer)
22
24
  )
23
- self.class._native_initialize(self, cpu_and_wall_time_collector)
25
+ self.class._native_initialize(self, cpu_and_wall_time_collector, gc_profiling_enabled)
24
26
  @worker_thread = nil
25
27
  @failure_exception = nil
26
28
  @start_stop_mutex = Mutex.new
@@ -28,7 +30,7 @@ module Datadog
28
30
 
29
31
  def start
30
32
  @start_stop_mutex.synchronize do
31
- return if @worker_thread
33
+ return if @worker_thread && @worker_thread.alive?
32
34
 
33
35
  Datadog.logger.debug { "Starting thread for: #{self}" }
34
36
  @worker_thread = Thread.new do
@@ -1,7 +1,6 @@
1
1
  # typed: true
2
2
 
3
3
  require_relative 'ext'
4
- require_relative '../core/utils/compression'
5
4
  require_relative 'tag_builder'
6
5
 
7
6
  module Datadog
@@ -60,10 +59,9 @@ module Datadog
60
59
  start: start,
61
60
  finish: finish,
62
61
  pprof_file_name: Datadog::Profiling::Ext::Transport::HTTP::PPROF_DEFAULT_FILENAME,
63
- pprof_data: Datadog::Core::Utils::Compression.gzip(uncompressed_pprof),
62
+ pprof_data: uncompressed_pprof.to_s,
64
63
  code_provenance_file_name: Datadog::Profiling::Ext::Transport::HTTP::CODE_PROVENANCE_FILENAME,
65
- code_provenance_data:
66
- (Datadog::Core::Utils::Compression.gzip(uncompressed_code_provenance) if uncompressed_code_provenance),
64
+ code_provenance_data: uncompressed_code_provenance,
67
65
  tags_as_array: Datadog::Profiling::TagBuilder.call(settings: Datadog.configuration).to_a,
68
66
  )
69
67
  end
@@ -58,7 +58,7 @@ module Datadog
58
58
  end
59
59
  end
60
60
 
61
- # Used to log soft failures in `ddprof_ffi_Vec_tag_push` (e.g. we still report the profile in these cases)
61
+ # Used to log soft failures in `ddog_Vec_tag_push` (e.g. we still report the profile in these cases)
62
62
  # Called from native code
63
63
  def self.log_failure_to_process_tag(failure_details)
64
64
  Datadog.logger.warn("Failed to add tag to profiling request: #{failure_details}")
@@ -21,6 +21,7 @@ module Datadog
21
21
  PROPAGATION_STYLE_B3_SINGLE_HEADER = 'B3 single header'.freeze
22
22
  ENV_PROPAGATION_STYLE_INJECT = 'DD_PROPAGATION_STYLE_INJECT'.freeze
23
23
  ENV_PROPAGATION_STYLE_EXTRACT = 'DD_PROPAGATION_STYLE_EXTRACT'.freeze
24
+ ENV_X_DATADOG_TAGS_MAX_LENGTH = 'DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH'.freeze
24
25
  end
25
26
 
26
27
  # @public_api
@@ -33,6 +33,8 @@ module Datadog
33
33
  span.name = Ext::SPAN_COMMAND
34
34
  span.resource = context.safely(:resource)
35
35
 
36
+ span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_CLIENT)
37
+
36
38
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
37
39
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_COMMAND)
38
40
 
@@ -17,6 +17,7 @@ module Datadog
17
17
  TAG_COMMAND = 'memcached.command'.freeze
18
18
  TAG_COMPONENT = 'dalli'.freeze
19
19
  TAG_OPERATION_COMMAND = 'command'.freeze
20
+ TAG_SYSTEM = 'memcached'.freeze
20
21
  end
21
22
  end
22
23
  end
@@ -3,6 +3,7 @@
3
3
  require_relative '../../metadata/ext'
4
4
  require_relative '../analytics'
5
5
  require_relative 'ext'
6
+ require_relative '../ext'
6
7
  require_relative 'quantize'
7
8
 
8
9
  module Datadog
@@ -37,6 +38,9 @@ module Datadog
37
38
 
38
39
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_HOST, hostname)
39
40
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_PORT, port)
41
+
42
+ span.set_tag(Contrib::Ext::DB::TAG_SYSTEM, Ext::TAG_SYSTEM)
43
+
40
44
  cmd = Quantize.format_command(op, args)
41
45
  span.set_tag(Ext::TAG_COMMAND, cmd)
42
46