ddtrace 1.20.0 → 1.22.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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +115 -1
  3. data/LICENSE-3rdparty.csv +1 -1
  4. data/bin/ddprofrb +15 -0
  5. data/bin/ddtracerb +3 -1
  6. data/ext/{ddtrace_profiling_loader/ddtrace_profiling_loader.c → datadog_profiling_loader/datadog_profiling_loader.c} +2 -2
  7. data/ext/{ddtrace_profiling_loader → datadog_profiling_loader}/extconf.rb +3 -3
  8. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_cpu_and_wall_time_worker.c +238 -61
  9. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_discrete_dynamic_sampler.c +145 -72
  10. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_discrete_dynamic_sampler.h +17 -5
  11. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.c +97 -4
  12. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/extconf.rb +2 -2
  13. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/heap_recorder.c +45 -3
  14. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/heap_recorder.h +7 -1
  15. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/http_transport.c +15 -19
  16. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/native_extension_helpers.rb +4 -4
  17. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/private_vm_api_access.c +14 -0
  18. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/private_vm_api_access.h +4 -0
  19. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/profiling.c +1 -1
  20. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/ruby_helpers.c +10 -0
  21. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/ruby_helpers.h +2 -0
  22. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/stack_recorder.c +7 -9
  23. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -13
  24. data/lib/datadog/appsec/event.rb +1 -1
  25. data/lib/datadog/auto_instrument.rb +3 -0
  26. data/lib/datadog/core/configuration/components.rb +7 -6
  27. data/lib/datadog/core/configuration/option.rb +8 -6
  28. data/lib/datadog/core/configuration/settings.rb +130 -63
  29. data/lib/datadog/core/configuration.rb +20 -4
  30. data/lib/datadog/core/diagnostics/environment_logger.rb +4 -3
  31. data/lib/datadog/core/environment/git.rb +25 -0
  32. data/lib/datadog/core/environment/identity.rb +18 -48
  33. data/lib/datadog/core/environment/platform.rb +7 -1
  34. data/lib/datadog/core/git/ext.rb +2 -23
  35. data/lib/datadog/core/remote/client/capabilities.rb +1 -1
  36. data/lib/datadog/core/remote/negotiation.rb +2 -2
  37. data/lib/datadog/core/remote/transport/http/config.rb +1 -1
  38. data/lib/datadog/core/remote/worker.rb +7 -4
  39. data/lib/datadog/core/telemetry/client.rb +18 -10
  40. data/lib/datadog/core/telemetry/emitter.rb +9 -13
  41. data/lib/datadog/core/telemetry/event.rb +247 -57
  42. data/lib/datadog/core/telemetry/ext.rb +1 -0
  43. data/lib/datadog/core/telemetry/heartbeat.rb +1 -3
  44. data/lib/datadog/core/telemetry/http/ext.rb +4 -1
  45. data/lib/datadog/core/telemetry/http/transport.rb +9 -4
  46. data/lib/datadog/core/telemetry/request.rb +59 -0
  47. data/lib/datadog/core/transport/ext.rb +2 -0
  48. data/lib/datadog/core/utils/url.rb +25 -0
  49. data/lib/datadog/profiling/collectors/code_provenance.rb +10 -4
  50. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +31 -0
  51. data/lib/datadog/profiling/collectors/info.rb +101 -0
  52. data/lib/datadog/profiling/component.rb +34 -28
  53. data/lib/datadog/profiling/exporter.rb +19 -5
  54. data/lib/datadog/profiling/ext.rb +2 -0
  55. data/lib/datadog/profiling/flush.rb +6 -3
  56. data/lib/datadog/profiling/http_transport.rb +5 -1
  57. data/lib/datadog/profiling/load_native_extension.rb +19 -6
  58. data/lib/datadog/profiling/native_extension.rb +1 -1
  59. data/lib/datadog/profiling/tag_builder.rb +5 -0
  60. data/lib/datadog/profiling/tasks/exec.rb +3 -3
  61. data/lib/datadog/profiling/tasks/help.rb +3 -3
  62. data/lib/datadog/profiling.rb +13 -2
  63. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +1 -1
  64. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +11 -4
  65. data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +20 -0
  66. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +11 -1
  67. data/lib/datadog/tracing/contrib/configurable.rb +1 -1
  68. data/lib/datadog/tracing/contrib/extensions.rb +6 -2
  69. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +11 -4
  70. data/lib/datadog/tracing/sampling/matcher.rb +23 -3
  71. data/lib/datadog/tracing/sampling/rule.rb +7 -2
  72. data/lib/datadog/tracing/sampling/rule_sampler.rb +2 -0
  73. data/lib/datadog/tracing/trace_operation.rb +1 -2
  74. data/lib/datadog/tracing/transport/http.rb +1 -0
  75. data/lib/datadog/tracing/transport/trace_formatter.rb +31 -0
  76. data/lib/ddtrace/version.rb +1 -1
  77. metadata +55 -62
  78. data/ext/ddtrace_profiling_native_extension/pid_controller.c +0 -57
  79. data/ext/ddtrace_profiling_native_extension/pid_controller.h +0 -45
  80. data/lib/datadog/core/telemetry/collector.rb +0 -250
  81. data/lib/datadog/core/telemetry/v1/app_event.rb +0 -59
  82. data/lib/datadog/core/telemetry/v1/application.rb +0 -92
  83. data/lib/datadog/core/telemetry/v1/configuration.rb +0 -25
  84. data/lib/datadog/core/telemetry/v1/dependency.rb +0 -43
  85. data/lib/datadog/core/telemetry/v1/host.rb +0 -59
  86. data/lib/datadog/core/telemetry/v1/install_signature.rb +0 -38
  87. data/lib/datadog/core/telemetry/v1/integration.rb +0 -64
  88. data/lib/datadog/core/telemetry/v1/product.rb +0 -36
  89. data/lib/datadog/core/telemetry/v1/telemetry_request.rb +0 -106
  90. data/lib/datadog/core/telemetry/v2/app_client_configuration_change.rb +0 -41
  91. data/lib/datadog/core/telemetry/v2/request.rb +0 -29
  92. data/lib/datadog/profiling/diagnostics/environment_logger.rb +0 -39
  93. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/NativeExtensionDesign.md +0 -0
  94. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id.h +0 -0
  95. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id_from_pthread.c +0 -0
  96. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id_noop.c +0 -0
  97. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_dynamic_sampling_rate.c +0 -0
  98. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_dynamic_sampling_rate.h +0 -0
  99. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_gc_profiling_helper.c +0 -0
  100. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_gc_profiling_helper.h +0 -0
  101. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_idle_sampling_helper.c +0 -0
  102. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_idle_sampling_helper.h +0 -0
  103. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_stack.c +0 -0
  104. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_stack.h +0 -0
  105. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.h +0 -0
  106. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/helpers.h +0 -0
  107. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/libdatadog_helpers.c +0 -0
  108. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/libdatadog_helpers.h +0 -0
  109. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/setup_signal_handler.c +0 -0
  110. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/setup_signal_handler.h +0 -0
  111. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/stack_recorder.h +0 -0
  112. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/time_helpers.c +0 -0
  113. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/time_helpers.h +0 -0
@@ -100,7 +100,7 @@ add_compiler_flag '-Wno-declaration-after-statement'
100
100
  add_compiler_flag '-Werror-implicit-function-declaration'
101
101
 
102
102
  # The native extension is not intended to expose any symbols/functions for other native libraries to use;
103
- # the sole exception being `Init_ddtrace_profiling_native_extension` which needs to be visible for Ruby to call it when
103
+ # the sole exception being `Init_datadog_profiling_native_extension` which needs to be visible for Ruby to call it when
104
104
  # it `dlopen`s the library.
105
105
  #
106
106
  # By setting this compiler flag, we tell it to assume that everything is private unless explicitly stated.
@@ -237,7 +237,7 @@ Logging.message("[ddtrace] After pkg-config $LDFLAGS were set to: #{$LDFLAGS.ins
237
237
  # This makes it easier for development (avoids "oops I forgot to rebuild when I switched my Ruby") and ensures that
238
238
  # the wrong library is never loaded.
239
239
  # When requiring, we need to use the exact same string, including the version and the platform.
240
- EXTENSION_NAME = "ddtrace_profiling_native_extension.#{RUBY_VERSION}_#{RUBY_PLATFORM}".freeze
240
+ EXTENSION_NAME = "datadog_profiling_native_extension.#{RUBY_VERSION}_#{RUBY_PLATFORM}".freeze
241
241
 
242
242
  if Datadog::Profiling::NativeExtensionHelpers::CAN_USE_MJIT_HEADER
243
243
  mjit_header_file_name = "rb_mjit_min_header-#{RUBY_VERSION}.h"
@@ -10,6 +10,13 @@
10
10
  #define CAN_APPLY_GC_FORCE_RECYCLE_BUG_WORKAROUND
11
11
  #endif
12
12
 
13
+ // Minimum age (in GC generations) of heap objects we want to include in heap
14
+ // recorder iterations. Object with age 0 represent objects that have yet to undergo
15
+ // a GC and, thus, may just be noise/trash at instant of iteration and are usually not
16
+ // relevant for heap profiles as the great majority should be trivially reclaimed
17
+ // during the next GC.
18
+ #define ITERATION_MIN_AGE 1
19
+
13
20
  // A compact representation of a stacktrace frame for a heap allocation.
14
21
  typedef struct {
15
22
  char *name;
@@ -137,6 +144,11 @@ struct heap_recorder {
137
144
  // mutation of the data so iteration can occur without acquiring a lock.
138
145
  // NOTE: Contrary to object_records, this table has no ownership of its data.
139
146
  st_table *object_records_snapshot;
147
+ // The GC gen/epoch/count in which we prepared the current iteration.
148
+ //
149
+ // This enables us to calculate the age of iterated objects in the above snapshot by
150
+ // comparing it against an object's alloc_gen.
151
+ size_t iteration_gen;
140
152
 
141
153
  // Data for a heap recording that was started but not yet ended
142
154
  recording active_recording;
@@ -353,6 +365,8 @@ void heap_recorder_prepare_iteration(heap_recorder *heap_recorder) {
353
365
  return;
354
366
  }
355
367
 
368
+ heap_recorder->iteration_gen = rb_gc_count();
369
+
356
370
  if (heap_recorder->object_records_snapshot != NULL) {
357
371
  // we could trivially handle this but we raise to highlight and catch unexpected usages.
358
372
  rb_raise(rb_eRuntimeError, "New heap recorder iteration prepared without the previous one having been finished.");
@@ -459,6 +473,13 @@ static int st_object_record_entry_free(DDTRACE_UNUSED st_data_t key, st_data_t v
459
473
  return ST_DELETE;
460
474
  }
461
475
 
476
+ // Check to see if an object should not be included in a heap recorder iteration.
477
+ // This centralizes the checking logic to ensure it's equally applied between
478
+ // preparation and iteration codepaths.
479
+ static inline bool should_exclude_from_iteration(object_record *obj_record) {
480
+ return obj_record->object_data.gen_age < ITERATION_MIN_AGE;
481
+ }
482
+
462
483
  static int st_object_record_update(st_data_t key, st_data_t value, st_data_t extra_arg) {
463
484
  long obj_id = (long) key;
464
485
  object_record *record = (object_record*) value;
@@ -466,6 +487,19 @@ static int st_object_record_update(st_data_t key, st_data_t value, st_data_t ext
466
487
 
467
488
  VALUE ref;
468
489
 
490
+ size_t iteration_gen = recorder->iteration_gen;
491
+ size_t alloc_gen = record->object_data.alloc_gen;
492
+ // Guard against potential overflows given unsigned types here.
493
+ record->object_data.gen_age = alloc_gen < iteration_gen ? iteration_gen - alloc_gen : 0;
494
+
495
+ if (should_exclude_from_iteration(record)) {
496
+ // If an object won't be included in the current iteration, there's
497
+ // no point checking for liveness or updating its size, so exit early.
498
+ // NOTE: This means that there should be an equivalent check during actual
499
+ // iteration otherwise we'd iterate/expose stale object data.
500
+ return ST_CONTINUE;
501
+ }
502
+
469
503
  if (!ruby_ref_from_id(LONG2NUM(obj_id), &ref)) {
470
504
  // Id no longer associated with a valid ref. Need to delete this object record!
471
505
  on_committed_object_record_cleanup(recorder, record);
@@ -525,8 +559,16 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
525
559
  const heap_stack *stack = record->heap_record->stack;
526
560
  iteration_context *context = (iteration_context*) extra;
527
561
 
528
- ddog_prof_Location *locations = context->heap_recorder->reusable_locations;
562
+ const heap_recorder *recorder = context->heap_recorder;
563
+
564
+ if (should_exclude_from_iteration(record)) {
565
+ // Skip objects that should not be included in iteration
566
+ // NOTE: This matches the short-circuiting condition in st_object_record_update
567
+ // and prevents iteration over stale objects.
568
+ return ST_CONTINUE;
569
+ }
529
570
 
571
+ ddog_prof_Location *locations = recorder->reusable_locations;
530
572
  for (uint16_t i = 0; i < stack->frames_len; i++) {
531
573
  const heap_frame *frame = &stack->frames[i];
532
574
  ddog_prof_Location *location = &locations[i];
@@ -725,9 +767,9 @@ void object_record_free(object_record *record) {
725
767
 
726
768
  VALUE object_record_inspect(object_record *record) {
727
769
  heap_frame top_frame = record->heap_record->stack->frames[0];
728
- VALUE inspect = rb_sprintf("obj_id=%ld weight=%d size=%zu location=%s:%d alloc_gen=%zu ",
770
+ VALUE inspect = rb_sprintf("obj_id=%ld weight=%d size=%zu location=%s:%d alloc_gen=%zu gen_age=%zu ",
729
771
  record->obj_id, record->object_data.weight, record->object_data.size, top_frame.filename,
730
- (int) top_frame.line, record->object_data.alloc_gen);
772
+ (int) top_frame.line, record->object_data.alloc_gen, record->object_data.gen_age);
731
773
 
732
774
  const char *class = record->object_data.class;
733
775
  if (class != NULL) {
@@ -27,7 +27,9 @@ typedef struct live_object_data {
27
27
  // could be seen as being representative of 50 objects.
28
28
  unsigned int weight;
29
29
 
30
- // Size of this object on last flush/update.
30
+ // Size of this object in memory.
31
+ // NOTE: This only gets updated during heap_recorder_prepare_iteration and only
32
+ // for those objects that meet the minimum iteration age requirements.
31
33
  size_t size;
32
34
 
33
35
  // The class of the object that we're tracking.
@@ -39,6 +41,10 @@ typedef struct live_object_data {
39
41
  // This enables us to calculate the age of this object in terms of GC executions.
40
42
  size_t alloc_gen;
41
43
 
44
+ // The age of this object in terms of GC generations.
45
+ // NOTE: This only gets updated during heap_recorder_prepare_iteration
46
+ size_t gen_age;
47
+
42
48
  // Whether this object was previously seen as being frozen. If this is the case,
43
49
  // we'll skip any further size updates since frozen objects are supposed to be
44
50
  // immutable.
@@ -30,7 +30,7 @@ inline static ddog_ByteSlice byte_slice_from_ruby_string(VALUE string);
30
30
  static VALUE _native_validate_exporter(VALUE self, VALUE exporter_configuration);
31
31
  static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array);
32
32
  static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_result);
33
- static ddog_Endpoint endpoint_from(VALUE exporter_configuration);
33
+ static ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration);
34
34
  static ddog_Vec_Tag convert_tags(VALUE tags_as_array);
35
35
  static void safely_log_failure_to_process_tag(ddog_Vec_Tag tags, VALUE err_details);
36
36
  static VALUE _native_do_export(
@@ -46,17 +46,17 @@ static VALUE _native_do_export(
46
46
  VALUE code_provenance_file_name,
47
47
  VALUE code_provenance_data,
48
48
  VALUE tags_as_array,
49
- VALUE internal_metadata_json
49
+ VALUE internal_metadata_json,
50
+ VALUE info_json
50
51
  );
51
52
  static void *call_exporter_without_gvl(void *call_args);
52
53
  static void interrupt_exporter_call(void *cancel_token);
53
- static VALUE ddtrace_version(void);
54
54
 
55
55
  void http_transport_init(VALUE profiling_module) {
56
56
  VALUE http_transport_class = rb_define_class_under(profiling_module, "HttpTransport", rb_cObject);
57
57
 
58
58
  rb_define_singleton_method(http_transport_class, "_native_validate_exporter", _native_validate_exporter, 1);
59
- rb_define_singleton_method(http_transport_class, "_native_do_export", _native_do_export, 12);
59
+ rb_define_singleton_method(http_transport_class, "_native_do_export", _native_do_export, 13);
60
60
 
61
61
  ok_symbol = ID2SYM(rb_intern_const("ok"));
62
62
  error_symbol = ID2SYM(rb_intern_const("error"));
@@ -94,7 +94,7 @@ static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration
94
94
 
95
95
  // This needs to be called BEFORE convert_tags since it can raise an exception and thus cause the ddog_Vec_Tag
96
96
  // to be leaked.
97
- ddog_Endpoint endpoint = endpoint_from(exporter_configuration);
97
+ ddog_prof_Endpoint endpoint = endpoint_from(exporter_configuration);
98
98
 
99
99
  ddog_Vec_Tag tags = convert_tags(tags_as_array);
100
100
 
@@ -116,7 +116,7 @@ static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_resul
116
116
  rb_ary_new_from_args(2, error_symbol, get_error_details_and_drop(&exporter_result.err));
117
117
  }
118
118
 
119
- static ddog_Endpoint endpoint_from(VALUE exporter_configuration) {
119
+ static ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration) {
120
120
  ENFORCE_TYPE(exporter_configuration, T_ARRAY);
121
121
 
122
122
  ID working_mode = SYM2ID(rb_ary_entry(exporter_configuration, 0)); // SYM2ID verifies its input so we can do this safely
@@ -131,12 +131,12 @@ static ddog_Endpoint endpoint_from(VALUE exporter_configuration) {
131
131
  ENFORCE_TYPE(site, T_STRING);
132
132
  ENFORCE_TYPE(api_key, T_STRING);
133
133
 
134
- return ddog_Endpoint_agentless(char_slice_from_ruby_string(site), char_slice_from_ruby_string(api_key));
134
+ return ddog_prof_Endpoint_agentless(char_slice_from_ruby_string(site), char_slice_from_ruby_string(api_key));
135
135
  } else { // agent_id
136
136
  VALUE base_url = rb_ary_entry(exporter_configuration, 1);
137
137
  ENFORCE_TYPE(base_url, T_STRING);
138
138
 
139
- return ddog_Endpoint_agent(char_slice_from_ruby_string(base_url));
139
+ return ddog_prof_Endpoint_agent(char_slice_from_ruby_string(base_url));
140
140
  }
141
141
  }
142
142
 
@@ -208,6 +208,7 @@ static VALUE perform_export(
208
208
  ddog_prof_Exporter_Slice_File files_to_export_unmodified,
209
209
  ddog_Vec_Tag *additional_tags,
210
210
  ddog_CharSlice internal_metadata,
211
+ ddog_CharSlice info,
211
212
  uint64_t timeout_milliseconds
212
213
  ) {
213
214
  ddog_prof_ProfiledEndpointsStats *endpoints_stats = NULL; // Not in use yet
@@ -220,6 +221,7 @@ static VALUE perform_export(
220
221
  additional_tags,
221
222
  endpoints_stats,
222
223
  &internal_metadata,
224
+ &info,
223
225
  timeout_milliseconds
224
226
  );
225
227
 
@@ -290,7 +292,8 @@ static VALUE _native_do_export(
290
292
  VALUE code_provenance_file_name,
291
293
  VALUE code_provenance_data,
292
294
  VALUE tags_as_array,
293
- VALUE internal_metadata_json
295
+ VALUE internal_metadata_json,
296
+ VALUE info_json
294
297
  ) {
295
298
  ENFORCE_TYPE(upload_timeout_milliseconds, T_FIXNUM);
296
299
  ENFORCE_TYPE(start_timespec_seconds, T_FIXNUM);
@@ -301,6 +304,7 @@ static VALUE _native_do_export(
301
304
  ENFORCE_TYPE(pprof_data, T_STRING);
302
305
  ENFORCE_TYPE(code_provenance_file_name, T_STRING);
303
306
  ENFORCE_TYPE(internal_metadata_json, T_STRING);
307
+ ENFORCE_TYPE(info_json, T_STRING);
304
308
 
305
309
  // Code provenance can be disabled and in that case will be set to nil
306
310
  bool have_code_provenance = !NIL_P(code_provenance_data);
@@ -335,6 +339,7 @@ static VALUE _native_do_export(
335
339
 
336
340
  ddog_Vec_Tag *null_additional_tags = NULL;
337
341
  ddog_CharSlice internal_metadata = char_slice_from_ruby_string(internal_metadata_json);
342
+ ddog_CharSlice info = char_slice_from_ruby_string(info_json);
338
343
 
339
344
  ddog_prof_Exporter_NewResult exporter_result = create_exporter(exporter_configuration, tags_as_array);
340
345
  // Note: Do not add anything that can raise exceptions after this line, as otherwise the exporter memory will leak
@@ -350,6 +355,7 @@ static VALUE _native_do_export(
350
355
  files_to_export_unmodified,
351
356
  null_additional_tags,
352
357
  internal_metadata,
358
+ info,
353
359
  timeout_milliseconds
354
360
  );
355
361
  }
@@ -367,13 +373,3 @@ static void *call_exporter_without_gvl(void *call_args) {
367
373
  static void interrupt_exporter_call(void *cancel_token) {
368
374
  ddog_CancellationToken_cancel((ddog_CancellationToken *) cancel_token);
369
375
  }
370
-
371
- static VALUE ddtrace_version(void) {
372
- VALUE ddtrace_module = rb_const_get(rb_cObject, rb_intern("DDTrace"));
373
- ENFORCE_TYPE(ddtrace_module, T_MODULE);
374
- VALUE version_module = rb_const_get(ddtrace_module, rb_intern("VERSION"));
375
- ENFORCE_TYPE(version_module, T_MODULE);
376
- VALUE version_string = rb_const_get(version_module, rb_intern("STRING"));
377
- ENFORCE_TYPE(version_string, T_STRING);
378
- return version_string;
379
- }
@@ -15,7 +15,7 @@ module Datadog
15
15
  # The MJIT header was introduced on 2.6 and removed on 3.3; for other Rubies we rely on debase-ruby_core_source
16
16
  CAN_USE_MJIT_HEADER = RUBY_VERSION.start_with?('2.6', '2.7', '3.0.', '3.1.', '3.2.')
17
17
 
18
- LIBDATADOG_VERSION = '~> 5.0.0.1.0'
18
+ LIBDATADOG_VERSION = '~> 7.0.0.1.0'
19
19
 
20
20
  def self.fail_install_if_missing_extension?
21
21
  ENV[ENV_FAIL_INSTALL_IF_MISSING_EXTENSION].to_s.strip.downcase == 'true'
@@ -29,7 +29,7 @@ module Datadog
29
29
  # native extension), we need to add a "runpath" -- a list of folders to search for libdatadog.
30
30
  #
31
31
  # This runpath gets hardcoded at native library linking time. You can look at it using the `readelf` tool in
32
- # Linux: e.g. `readelf -d ddtrace_profiling_native_extension.2.7.3_x86_64-linux.so`.
32
+ # Linux: e.g. `readelf -d datadog_profiling_native_extension.2.7.3_x86_64-linux.so`.
33
33
  #
34
34
  # In older versions of ddtrace, we only set as runpath an absolute path to libdatadog.
35
35
  # (This gets set automatically by the call
@@ -305,8 +305,8 @@ module Datadog
305
305
  no_binaries_for_current_platform = explain_issue(
306
306
  'the `libdatadog` gem installed on your system is missing binaries for your',
307
307
  'platform variant.',
308
- "(Your platform: `#{Gem::Platform.local}`)",
309
- '(Available binaries: ',
308
+ "(Your platform: `#{Libdatadog.current_platform}`)",
309
+ '(Available binaries:',
310
310
  "`#{Libdatadog.available_binaries.join('`, `')}`)",
311
311
  suggested: CONTACT_SUPPORT,
312
312
  )
@@ -876,3 +876,17 @@ static inline int ddtrace_imemo_type(VALUE imemo) {
876
876
  return NULL;
877
877
  }
878
878
  #endif
879
+
880
+ // This is used to workaround a VM bug. See "handle_sampling_signal" in "collectors_cpu_and_wall_time_worker" for details.
881
+ #ifdef NO_POSTPONED_TRIGGER
882
+ void *objspace_ptr_for_gc_finalize_deferred_workaround(void) {
883
+ rb_vm_t *vm =
884
+ #ifndef NO_GET_VM // TODO: Inline GET_VM below once we drop support in dd-trace-rb 2.x for < Ruby 2.5
885
+ GET_VM();
886
+ #else
887
+ thread_struct_from_object(rb_thread_current())->vm;
888
+ #endif
889
+
890
+ return vm->objspace;
891
+ }
892
+ #endif
@@ -55,3 +55,7 @@ void self_test_mn_enabled(void);
55
55
 
56
56
  // Provides more specific information on what kind an imemo is
57
57
  const char *imemo_kind(VALUE imemo);
58
+
59
+ #ifdef NO_POSTPONED_TRIGGER
60
+ void *objspace_ptr_for_gc_finalize_deferred_workaround(void);
61
+ #endif
@@ -38,7 +38,7 @@ static VALUE _native_enforce_success(DDTRACE_UNUSED VALUE _self, VALUE syserr_er
38
38
  static void *trigger_enforce_success(void *trigger_args);
39
39
  static VALUE _native_malloc_stats(DDTRACE_UNUSED VALUE _self);
40
40
 
41
- void DDTRACE_EXPORT Init_ddtrace_profiling_native_extension(void) {
41
+ void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
42
42
  VALUE datadog_module = rb_define_module("Datadog");
43
43
  VALUE profiling_module = rb_define_module_under(datadog_module, "Profiling");
44
44
  VALUE native_extension_module = rb_define_module_under(profiling_module, "NativeExtension");
@@ -255,3 +255,13 @@ VALUE ruby_safe_inspect(VALUE obj) {
255
255
  return rb_str_new_cstr("(Not inspectable)");
256
256
  }
257
257
  }
258
+
259
+ VALUE ddtrace_version(void) {
260
+ VALUE ddtrace_module = rb_const_get(rb_cObject, rb_intern("DDTrace"));
261
+ ENFORCE_TYPE(ddtrace_module, T_MODULE);
262
+ VALUE version_module = rb_const_get(ddtrace_module, rb_intern("VERSION"));
263
+ ENFORCE_TYPE(version_module, T_MODULE);
264
+ VALUE version_string = rb_const_get(version_module, rb_intern("STRING"));
265
+ ENFORCE_TYPE(version_string, T_STRING);
266
+ return version_string;
267
+ }
@@ -115,3 +115,5 @@ size_t ruby_obj_memsize_of(VALUE obj);
115
115
  // return a string with the result of that call. Elsif the object responds to
116
116
  // 'to_s', return a string with the result of that call. Otherwise, return Qnil.
117
117
  VALUE ruby_safe_inspect(VALUE obj);
118
+
119
+ VALUE ddtrace_version(void);
@@ -196,7 +196,6 @@ struct call_serialize_without_gvl_arguments {
196
196
  // Set by caller
197
197
  struct stack_recorder_state *state;
198
198
  ddog_Timespec finish_timestamp;
199
- size_t gc_count_before_serialize;
200
199
 
201
200
  // Set by callee
202
201
  ddog_prof_Profile *profile;
@@ -489,7 +488,6 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
489
488
  struct call_serialize_without_gvl_arguments args = {
490
489
  .state = state,
491
490
  .finish_timestamp = finish_timestamp,
492
- .gc_count_before_serialize = rb_gc_count(),
493
491
  .serialize_ran = false
494
492
  };
495
493
 
@@ -613,8 +611,6 @@ typedef struct heap_recorder_iteration_context {
613
611
 
614
612
  bool error;
615
613
  char error_msg[MAX_LEN_HEAP_ITERATION_ERROR_MSG];
616
-
617
- size_t profile_gen;
618
614
  } heap_recorder_iteration_context;
619
615
 
620
616
  static bool add_heap_sample_to_active_profile_without_gvl(heap_recorder_iteration_data iteration_data, void *extra_arg) {
@@ -643,7 +639,7 @@ static bool add_heap_sample_to_active_profile_without_gvl(heap_recorder_iteratio
643
639
  }
644
640
  labels[label_offset++] = (ddog_prof_Label) {
645
641
  .key = DDOG_CHARSLICE_C("gc gen age"),
646
- .num = context->profile_gen - object_data->alloc_gen,
642
+ .num = object_data->gen_age,
647
643
  };
648
644
 
649
645
  ddog_prof_Profile_Result result = ddog_prof_Profile_add(
@@ -670,13 +666,12 @@ static bool add_heap_sample_to_active_profile_without_gvl(heap_recorder_iteratio
670
666
  return true;
671
667
  }
672
668
 
673
- static void build_heap_profile_without_gvl(struct stack_recorder_state *state, ddog_prof_Profile *profile, size_t gc_count_before_serialize) {
669
+ static void build_heap_profile_without_gvl(struct stack_recorder_state *state, ddog_prof_Profile *profile) {
674
670
  heap_recorder_iteration_context iteration_context = {
675
671
  .state = state,
676
672
  .profile = profile,
677
673
  .error = false,
678
674
  .error_msg = {0},
679
- .profile_gen = gc_count_before_serialize,
680
675
  };
681
676
  bool iterated = heap_recorder_for_each_live_object(state->heap_recorder, add_heap_sample_to_active_profile_without_gvl, (void*) &iteration_context);
682
677
  // We wait until we're out of the iteration to grab the gvl and raise. This is important because during
@@ -698,7 +693,7 @@ static void *call_serialize_without_gvl(void *call_args) {
698
693
 
699
694
  // Now that we have the inactive profile with all but heap samples, lets fill it with heap data
700
695
  // without needing to race with the active sampler
701
- build_heap_profile_without_gvl(args->state, args->profile, args->gc_count_before_serialize);
696
+ build_heap_profile_without_gvl(args->state, args->profile);
702
697
 
703
698
  // Note: The profile gets reset by the serialize call
704
699
  args->result = ddog_prof_Profile_serialize(args->profile, &args->finish_timestamp, NULL /* duration_nanos is optional */, NULL /* start_time is optional */);
@@ -918,10 +913,13 @@ static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recor
918
913
  #pragma GCC diagnostic push
919
914
  // rb_gc_force_recycle was deprecated in latest versions of Ruby and is a noop.
920
915
  #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
916
+ #pragma GCC diagnostic ignored "-Wunused-parameter"
921
917
  // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
922
918
  // It SHOULD NOT be used for other purposes.
923
919
  static VALUE _native_gc_force_recycle(DDTRACE_UNUSED VALUE _self, VALUE obj) {
924
- rb_gc_force_recycle(obj);
920
+ #ifdef HAVE_WORKING_RB_GC_FORCE_RECYCLE
921
+ rb_gc_force_recycle(obj);
922
+ #endif
925
923
  return Qnil;
926
924
  }
927
925
  #pragma GCC diagnostic pop
@@ -13,6 +13,18 @@ module Datadog
13
13
  module AppSec
14
14
  module Contrib
15
15
  module Rack
16
+ # Create an array of lowercased headers
17
+ WAF_VENDOR_HEADERS_TAGS = %w[
18
+ X-Amzn-Trace-Id
19
+ Cloudfront-Viewer-Ja3-Fingerprint
20
+ Cf-Ray
21
+ X-Cloud-Trace-Context
22
+ X-Appgw-Trace-id
23
+ X-SigSci-RequestID
24
+ X-SigSci-Tags
25
+ Akamai-User-Risk
26
+ ].map(&:downcase).freeze
27
+
16
28
  # Topmost Rack middleware for AppSec
17
29
  # This should be inserted just below Datadog::Tracing::Contrib::Rack::TraceMiddleware
18
30
  class RequestMiddleware
@@ -20,6 +32,7 @@ module Datadog
20
32
  @app = app
21
33
 
22
34
  @oneshot_tags_sent = false
35
+ @rack_headers = {}
23
36
  end
24
37
 
25
38
  # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/MethodLength
@@ -54,7 +67,8 @@ module Datadog
54
67
 
55
68
  gateway_request = Gateway::Request.new(env)
56
69
 
57
- add_appsec_tags(processor, scope, env)
70
+ add_appsec_tags(processor, scope)
71
+ add_request_tags(scope, env)
58
72
 
59
73
  request_return, request_response = catch(::Datadog::AppSec::Ext::INTERRUPT) do
60
74
  Instrumentation.gateway.push('rack.request', gateway_request) do
@@ -129,7 +143,7 @@ module Datadog
129
143
  Datadog::Tracing.active_span
130
144
  end
131
145
 
132
- def add_appsec_tags(processor, scope, env)
146
+ def add_appsec_tags(processor, scope)
133
147
  span = scope.service_entry_span
134
148
  trace = scope.trace
135
149
 
@@ -139,17 +153,6 @@ module Datadog
139
153
  span.set_tag('_dd.runtime_family', 'ruby')
140
154
  span.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
141
155
 
142
- if span && span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).nil?
143
- request_header_collection = Datadog::Tracing::Contrib::Rack::Header::RequestHeaderCollection.new(env)
144
-
145
- # always collect client ip, as this is part of AppSec provided functionality
146
- Datadog::Tracing::ClientIp.set_client_ip_tag!(
147
- span,
148
- headers: request_header_collection,
149
- remote_ip: env['REMOTE_ADDR']
150
- )
151
- end
152
-
153
156
  if processor.diagnostics
154
157
  diagnostics = processor.diagnostics
155
158
 
@@ -175,6 +178,29 @@ module Datadog
175
178
  end
176
179
  end
177
180
 
181
+ def add_request_tags(scope, env)
182
+ span = scope.service_entry_span
183
+
184
+ return unless span
185
+
186
+ # Always add WAF vendors headers
187
+ WAF_VENDOR_HEADERS_TAGS.each do |lowercase_header|
188
+ rack_header = to_rack_header(lowercase_header)
189
+ span.set_tag("http.request.headers.#{lowercase_header}", env[rack_header]) if env[rack_header]
190
+ end
191
+
192
+ if span && span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).nil?
193
+ request_header_collection = Datadog::Tracing::Contrib::Rack::Header::RequestHeaderCollection.new(env)
194
+
195
+ # always collect client ip, as this is part of AppSec provided functionality
196
+ Datadog::Tracing::ClientIp.set_client_ip_tag!(
197
+ span,
198
+ headers: request_header_collection,
199
+ remote_ip: env['REMOTE_ADDR']
200
+ )
201
+ end
202
+ end
203
+
178
204
  def add_waf_runtime_tags(scope)
179
205
  span = scope.service_entry_span
180
206
  context = scope.processor_context
@@ -187,6 +213,10 @@ module Datadog
187
213
  span.set_tag('_dd.appsec.waf.duration', context.time_ns / 1000.0)
188
214
  span.set_tag('_dd.appsec.waf.duration_ext', context.time_ext_ns / 1000.0)
189
215
  end
216
+
217
+ def to_rack_header(header)
218
+ @rack_headers[header] ||= Datadog::Tracing::Contrib::Rack::Header.to_rack_header(header)
219
+ end
190
220
  end
191
221
  end
192
222
  end
@@ -138,7 +138,7 @@ module Datadog
138
138
  private
139
139
 
140
140
  def compressed_and_base64_encoded(value)
141
- Base64.encode64(gzip(value))
141
+ Base64.strict_encode64(gzip(value))
142
142
  rescue TypeError => e
143
143
  Datadog.logger.debug do
144
144
  "Failed to compress and encode value when populating AppSec::Event. Error: #{e.message}"
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../ddtrace/auto_instrument'
@@ -62,7 +62,8 @@ module Datadog
62
62
 
63
63
  Telemetry::Client.new(
64
64
  enabled: enabled,
65
- heartbeat_interval_seconds: settings.telemetry.heartbeat_interval_seconds
65
+ heartbeat_interval_seconds: settings.telemetry.heartbeat_interval_seconds,
66
+ dependency_collection: settings.telemetry.dependency_collection
66
67
  )
67
68
  end
68
69
  end
@@ -81,6 +82,7 @@ module Datadog
81
82
 
82
83
  def initialize(settings)
83
84
  @logger = self.class.build_logger(settings)
85
+ @environment_logger_extra = {}
84
86
 
85
87
  # This agent_settings is intended for use within Core. If you require
86
88
  # agent_settings within a product outside of core you should extend
@@ -90,11 +92,13 @@ module Datadog
90
92
  @remote = Remote::Component.build(settings, agent_settings)
91
93
  @tracer = self.class.build_tracer(settings, logger: @logger)
92
94
 
93
- @profiler = Datadog::Profiling::Component.build_profiler_component(
95
+ @profiler, profiler_logger_extra = Datadog::Profiling::Component.build_profiler_component(
94
96
  settings: settings,
95
97
  agent_settings: agent_settings,
96
98
  optional_tracer: @tracer,
97
99
  )
100
+ @environment_logger_extra.merge!(profiler_logger_extra) if profiler_logger_extra
101
+
98
102
  @runtime_metrics = self.class.build_runtime_metrics_worker(settings)
99
103
  @health_metrics = self.class.build_health_metrics(settings)
100
104
  @telemetry = self.class.build_telemetry(settings, agent_settings, logger)
@@ -105,18 +109,15 @@ module Datadog
105
109
  def startup!(settings)
106
110
  if settings.profiling.enabled
107
111
  if profiler
108
- @logger.debug('Profiling started')
109
112
  profiler.start
110
113
  else
111
114
  # Display a warning for users who expected profiling to be enabled
112
115
  unsupported_reason = Profiling.unsupported_reason
113
116
  logger.warn("Profiling was requested but is not supported, profiling disabled: #{unsupported_reason}")
114
117
  end
115
- else
116
- @logger.debug('Profiling is disabled')
117
118
  end
118
119
 
119
- Core::Diagnostics::EnvironmentLogger.collect_and_log!
120
+ Core::Diagnostics::EnvironmentLogger.collect_and_log!(@environment_logger_extra)
120
121
  end
121
122
 
122
123
  # Shuts down all the components in use.
@@ -8,7 +8,13 @@ module Datadog
8
8
  # Represents an instance of an integration configuration option
9
9
  # @public_api
10
10
  class Option
11
- attr_reader :definition
11
+ # @!attribute [r] definition
12
+ # The definition object that matches this option.
13
+ # @return [Configuration::OptionDefinition]
14
+ # @!attribute [r] precedence_set
15
+ # When this option was last set, what was the value precedence used?
16
+ # @return [Precedence::Value]
17
+ attr_reader :definition, :precedence_set
12
18
 
13
19
  # Option setting precedence.
14
20
  module Precedence
@@ -264,7 +270,7 @@ module Datadog
264
270
  # when restoring a value from `@value_per_precedence`, and we are only running `definition.setter`
265
271
  # on the original value, not on a valud that has already been processed by `definition.setter`.
266
272
  @value_per_precedence[precedence] = value
267
- context_exec(v, old_value, &definition.after_set) if definition.after_set
273
+ context_exec(v, old_value, precedence, &definition.after_set) if definition.after_set
268
274
  end
269
275
  end
270
276
 
@@ -303,10 +309,6 @@ module Datadog
303
309
  ['true', '1'].include?(ENV.fetch('DD_EXPERIMENTAL_SKIP_CONFIGURATION_VALIDATION', '').strip)
304
310
  end
305
311
 
306
- # Used for testing
307
- attr_reader :precedence_set
308
- private :precedence_set
309
-
310
312
  # Anchor object that represents a value that is not set.
311
313
  # This is necessary because `nil` is a valid value to be set.
312
314
  UNSET = Object.new