datadog 2.7.1 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +69 -1
  3. data/ext/datadog_profiling_native_extension/clock_id.h +2 -2
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +64 -54
  5. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
  7. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +16 -16
  8. data/ext/datadog_profiling_native_extension/collectors_stack.c +7 -7
  9. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +259 -132
  10. data/ext/datadog_profiling_native_extension/extconf.rb +0 -8
  11. data/ext/datadog_profiling_native_extension/heap_recorder.c +11 -89
  12. data/ext/datadog_profiling_native_extension/heap_recorder.h +1 -1
  13. data/ext/datadog_profiling_native_extension/http_transport.c +4 -4
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +4 -1
  15. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -1
  16. data/ext/datadog_profiling_native_extension/profiling.c +10 -8
  17. data/ext/datadog_profiling_native_extension/ruby_helpers.c +8 -8
  18. data/ext/datadog_profiling_native_extension/stack_recorder.c +54 -88
  19. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -1
  20. data/ext/datadog_profiling_native_extension/time_helpers.h +1 -1
  21. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +47 -0
  22. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +31 -0
  23. data/ext/libdatadog_api/crashtracker.c +3 -0
  24. data/ext/libdatadog_extconf_helpers.rb +1 -1
  25. data/lib/datadog/appsec/assets/waf_rules/recommended.json +355 -157
  26. data/lib/datadog/appsec/assets/waf_rules/strict.json +62 -32
  27. data/lib/datadog/appsec/component.rb +1 -8
  28. data/lib/datadog/appsec/context.rb +54 -0
  29. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +73 -0
  30. data/lib/datadog/appsec/contrib/active_record/integration.rb +41 -0
  31. data/lib/datadog/appsec/contrib/active_record/patcher.rb +53 -0
  32. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +6 -6
  33. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +4 -4
  34. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +19 -28
  35. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +5 -5
  36. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
  37. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +64 -96
  38. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +10 -10
  39. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +5 -5
  40. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +6 -6
  41. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
  42. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -49
  43. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +21 -32
  44. data/lib/datadog/appsec/contrib/rails/patcher.rb +1 -1
  45. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +6 -6
  46. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +41 -63
  47. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +2 -2
  48. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +5 -5
  49. data/lib/datadog/appsec/event.rb +6 -6
  50. data/lib/datadog/appsec/ext.rb +3 -1
  51. data/lib/datadog/appsec/monitor/gateway/watcher.rb +22 -32
  52. data/lib/datadog/appsec/monitor/reactive/set_user.rb +5 -5
  53. data/lib/datadog/appsec/processor/context.rb +2 -2
  54. data/lib/datadog/appsec/processor/rule_loader.rb +0 -3
  55. data/lib/datadog/appsec/remote.rb +1 -3
  56. data/lib/datadog/appsec/response.rb +7 -11
  57. data/lib/datadog/appsec.rb +6 -5
  58. data/lib/datadog/auto_instrument.rb +3 -0
  59. data/lib/datadog/core/configuration/agent_settings_resolver.rb +39 -11
  60. data/lib/datadog/core/configuration/components.rb +20 -2
  61. data/lib/datadog/core/configuration/settings.rb +10 -0
  62. data/lib/datadog/core/configuration.rb +10 -2
  63. data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
  64. data/lib/datadog/core/crashtracking/component.rb +1 -3
  65. data/lib/datadog/core/remote/client/capabilities.rb +6 -0
  66. data/lib/datadog/core/remote/client.rb +65 -59
  67. data/lib/datadog/core/telemetry/component.rb +9 -3
  68. data/lib/datadog/core/telemetry/event.rb +87 -3
  69. data/lib/datadog/core/telemetry/ext.rb +1 -0
  70. data/lib/datadog/core/telemetry/logging.rb +2 -2
  71. data/lib/datadog/core/telemetry/metric.rb +22 -0
  72. data/lib/datadog/core/telemetry/worker.rb +33 -0
  73. data/lib/datadog/di/base.rb +115 -0
  74. data/lib/datadog/di/code_tracker.rb +11 -7
  75. data/lib/datadog/di/component.rb +21 -11
  76. data/lib/datadog/di/configuration/settings.rb +11 -1
  77. data/lib/datadog/di/contrib/active_record.rb +1 -0
  78. data/lib/datadog/di/contrib/railtie.rb +15 -0
  79. data/lib/datadog/di/contrib.rb +26 -0
  80. data/lib/datadog/di/error.rb +5 -0
  81. data/lib/datadog/di/instrumenter.rb +111 -20
  82. data/lib/datadog/di/preload.rb +18 -0
  83. data/lib/datadog/di/probe.rb +11 -1
  84. data/lib/datadog/di/probe_builder.rb +1 -0
  85. data/lib/datadog/di/probe_manager.rb +8 -5
  86. data/lib/datadog/di/probe_notification_builder.rb +27 -7
  87. data/lib/datadog/di/probe_notifier_worker.rb +5 -6
  88. data/lib/datadog/di/remote.rb +124 -0
  89. data/lib/datadog/di/serializer.rb +14 -7
  90. data/lib/datadog/di/transport.rb +3 -5
  91. data/lib/datadog/di/utils.rb +7 -0
  92. data/lib/datadog/di.rb +23 -62
  93. data/lib/datadog/kit/appsec/events.rb +3 -3
  94. data/lib/datadog/kit/identity.rb +4 -4
  95. data/lib/datadog/profiling/component.rb +59 -69
  96. data/lib/datadog/profiling/http_transport.rb +1 -26
  97. data/lib/datadog/tracing/configuration/settings.rb +4 -8
  98. data/lib/datadog/tracing/contrib/action_cable/integration.rb +5 -2
  99. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +6 -2
  100. data/lib/datadog/tracing/contrib/action_pack/integration.rb +5 -2
  101. data/lib/datadog/tracing/contrib/action_view/integration.rb +5 -2
  102. data/lib/datadog/tracing/contrib/active_job/integration.rb +5 -2
  103. data/lib/datadog/tracing/contrib/active_record/integration.rb +6 -2
  104. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +3 -1
  105. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +3 -1
  106. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +16 -4
  107. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -0
  108. data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -2
  109. data/lib/datadog/tracing/contrib/auto_instrument.rb +2 -2
  110. data/lib/datadog/tracing/contrib/aws/integration.rb +3 -0
  111. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -0
  112. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +4 -0
  113. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
  114. data/lib/datadog/tracing/contrib/httprb/integration.rb +3 -0
  115. data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -0
  116. data/lib/datadog/tracing/contrib/mongodb/integration.rb +3 -0
  117. data/lib/datadog/tracing/contrib/opensearch/integration.rb +3 -0
  118. data/lib/datadog/tracing/contrib/presto/integration.rb +3 -0
  119. data/lib/datadog/tracing/contrib/rack/integration.rb +2 -2
  120. data/lib/datadog/tracing/contrib/rails/framework.rb +2 -2
  121. data/lib/datadog/tracing/contrib/rails/patcher.rb +1 -1
  122. data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -0
  123. data/lib/datadog/tracing/span.rb +12 -4
  124. data/lib/datadog/tracing/span_event.rb +123 -3
  125. data/lib/datadog/tracing/span_operation.rb +6 -0
  126. data/lib/datadog/tracing/transport/serializable_trace.rb +24 -6
  127. data/lib/datadog/version.rb +2 -2
  128. data/lib/datadog.rb +3 -0
  129. metadata +30 -17
  130. data/lib/datadog/appsec/processor/actions.rb +0 -49
  131. data/lib/datadog/appsec/reactive/operation.rb +0 -68
  132. data/lib/datadog/appsec/scope.rb +0 -58
  133. data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
@@ -173,18 +173,18 @@ static const uint8_t all_value_types_positions[] =
173
173
 
174
174
  // Struct for storing stats related to a profile in a particular slot.
175
175
  // These stats will share the same lifetime as the data in that profile slot.
176
- typedef struct slot_stats {
176
+ typedef struct {
177
177
  // How many individual samples were recorded into this slot (un-weighted)
178
178
  uint64_t recorded_samples;
179
179
  } stats_slot;
180
180
 
181
- typedef struct profile_slot {
181
+ typedef struct {
182
182
  ddog_prof_Profile profile;
183
183
  stats_slot stats;
184
184
  } profile_slot;
185
185
 
186
186
  // Contains native state for each instance
187
- struct stack_recorder_state {
187
+ typedef struct {
188
188
  // Heap recorder instance
189
189
  heap_recorder *heap_recorder;
190
190
  bool heap_clean_after_gc_enabled;
@@ -210,17 +210,17 @@ struct stack_recorder_state {
210
210
  long serialization_time_ns_max;
211
211
  uint64_t serialization_time_ns_total;
212
212
  } stats_lifetime;
213
- };
213
+ } stack_recorder_state;
214
214
 
215
215
  // Used to group mutex and the corresponding profile slot for easy unlocking after work is done.
216
- typedef struct locked_profile_slot {
216
+ typedef struct {
217
217
  pthread_mutex_t *mutex;
218
218
  profile_slot *data;
219
219
  } locked_profile_slot;
220
220
 
221
- struct call_serialize_without_gvl_arguments {
221
+ typedef struct {
222
222
  // Set by caller
223
- struct stack_recorder_state *state;
223
+ stack_recorder_state *state;
224
224
  ddog_Timespec finish_timestamp;
225
225
 
226
226
  // Set by callee
@@ -231,26 +231,26 @@ struct call_serialize_without_gvl_arguments {
231
231
 
232
232
  // Set by both
233
233
  bool serialize_ran;
234
- };
234
+ } call_serialize_without_gvl_arguments;
235
235
 
236
236
  static VALUE _native_new(VALUE klass);
237
- static void initialize_slot_concurrency_control(struct stack_recorder_state *state);
238
- static void initialize_profiles(struct stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types);
237
+ static void initialize_slot_concurrency_control(stack_recorder_state *state);
238
+ static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types);
239
239
  static void stack_recorder_typed_data_free(void *data);
240
240
  static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
241
241
  static VALUE _native_serialize(VALUE self, VALUE recorder_instance);
242
242
  static VALUE ruby_time_from(ddog_Timespec ddprof_time);
243
243
  static void *call_serialize_without_gvl(void *call_args);
244
- static locked_profile_slot sampler_lock_active_profile(struct stack_recorder_state *state);
244
+ static locked_profile_slot sampler_lock_active_profile(stack_recorder_state *state);
245
245
  static void sampler_unlock_active_profile(locked_profile_slot active_slot);
246
- static profile_slot* serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state);
246
+ static profile_slot* serializer_flip_active_and_inactive_slots(stack_recorder_state *state);
247
247
  static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
248
248
  static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
249
249
  static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
250
250
  static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot);
251
251
  static ddog_Timespec system_epoch_now_timespec(void);
252
252
  static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance);
253
- static void serializer_set_start_timestamp_for_next_profile(struct stack_recorder_state *state, ddog_Timespec start_time);
253
+ static void serializer_set_start_timestamp_for_next_profile(stack_recorder_state *state, ddog_Timespec start_time);
254
254
  static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint);
255
255
  static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /* Can be null */);
256
256
  static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE new_obj, VALUE weight, VALUE alloc_class);
@@ -258,8 +258,6 @@ static VALUE _native_check_heap_hashes(DDTRACE_UNUSED VALUE _self, VALUE locatio
258
258
  static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
259
259
  static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
260
260
  static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
261
- static VALUE _native_gc_force_recycle(DDTRACE_UNUSED VALUE _self, VALUE obj);
262
- static VALUE _native_has_seen_id_flag(DDTRACE_UNUSED VALUE _self, VALUE obj);
263
261
  static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance);
264
262
  static VALUE build_profile_stats(profile_slot *slot, long serialization_time_ns, long heap_iteration_prep_time_ns, long heap_profile_build_time_ns);
265
263
  static VALUE _native_is_object_recorded(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE object_id);
@@ -297,10 +295,6 @@ void stack_recorder_init(VALUE profiling_module) {
297
295
  _native_end_fake_slow_heap_serialization, 1);
298
296
  rb_define_singleton_method(testing_module, "_native_debug_heap_recorder",
299
297
  _native_debug_heap_recorder, 1);
300
- rb_define_singleton_method(testing_module, "_native_gc_force_recycle",
301
- _native_gc_force_recycle, 1);
302
- rb_define_singleton_method(testing_module, "_native_has_seen_id_flag",
303
- _native_has_seen_id_flag, 1);
304
298
  rb_define_singleton_method(testing_module, "_native_is_object_recorded?", _native_is_object_recorded, 2);
305
299
  rb_define_singleton_method(testing_module, "_native_heap_recorder_reset_last_update", _native_heap_recorder_reset_last_update, 1);
306
300
  rb_define_singleton_method(testing_module, "_native_recorder_after_gc_step", _native_recorder_after_gc_step, 1);
@@ -322,7 +316,7 @@ static const rb_data_type_t stack_recorder_typed_data = {
322
316
  };
323
317
 
324
318
  static VALUE _native_new(VALUE klass) {
325
- struct stack_recorder_state *state = ruby_xcalloc(1, sizeof(struct stack_recorder_state));
319
+ stack_recorder_state *state = ruby_xcalloc(1, sizeof(stack_recorder_state));
326
320
 
327
321
  // Note: Any exceptions raised from this note until the TypedData_Wrap_Struct call will lead to the state memory
328
322
  // being leaked.
@@ -360,7 +354,7 @@ static VALUE _native_new(VALUE klass) {
360
354
  return stack_recorder;
361
355
  }
362
356
 
363
- static void initialize_slot_concurrency_control(struct stack_recorder_state *state) {
357
+ static void initialize_slot_concurrency_control(stack_recorder_state *state) {
364
358
  state->mutex_slot_one = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
365
359
  state->mutex_slot_two = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
366
360
 
@@ -370,7 +364,7 @@ static void initialize_slot_concurrency_control(struct stack_recorder_state *sta
370
364
  state->active_slot = 1;
371
365
  }
372
366
 
373
- static void initialize_profiles(struct stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types) {
367
+ static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types) {
374
368
  ddog_prof_Profile_NewResult slot_one_profile_result =
375
369
  ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
376
370
 
@@ -397,7 +391,7 @@ static void initialize_profiles(struct stack_recorder_state *state, ddog_prof_Sl
397
391
  }
398
392
 
399
393
  static void stack_recorder_typed_data_free(void *state_ptr) {
400
- struct stack_recorder_state *state = (struct stack_recorder_state *) state_ptr;
394
+ stack_recorder_state *state = (stack_recorder_state *) state_ptr;
401
395
 
402
396
  pthread_mutex_destroy(&state->mutex_slot_one);
403
397
  ddog_prof_Profile_drop(&state->profile_slot_one.profile);
@@ -432,8 +426,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
432
426
  ENFORCE_BOOLEAN(timeline_enabled);
433
427
  ENFORCE_BOOLEAN(heap_clean_after_gc_enabled);
434
428
 
435
- struct stack_recorder_state *state;
436
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
429
+ stack_recorder_state *state;
430
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
437
431
 
438
432
  state->heap_clean_after_gc_enabled = (heap_clean_after_gc_enabled == Qtrue);
439
433
 
@@ -523,8 +517,8 @@ static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _sel
523
517
  }
524
518
 
525
519
  static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
526
- struct stack_recorder_state *state;
527
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
520
+ stack_recorder_state *state;
521
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
528
522
 
529
523
  ddog_Timespec finish_timestamp = system_epoch_now_timespec();
530
524
  // Need to do this while still holding on to the Global VM Lock; see comments on method for why
@@ -538,7 +532,7 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
538
532
 
539
533
  // We'll release the Global VM Lock while we're calling serialize, so that the Ruby VM can continue to work while this
540
534
  // is pending
541
- struct call_serialize_without_gvl_arguments args = {
535
+ call_serialize_without_gvl_arguments args = {
542
536
  .state = state,
543
537
  .finish_timestamp = finish_timestamp,
544
538
  .serialize_ran = false
@@ -603,8 +597,8 @@ static VALUE ruby_time_from(ddog_Timespec ddprof_time) {
603
597
  }
604
598
 
605
599
  void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations, sample_values values, sample_labels labels) {
606
- struct stack_recorder_state *state;
607
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
600
+ stack_recorder_state *state;
601
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
608
602
 
609
603
  locked_profile_slot active_slot = sampler_lock_active_profile(state);
610
604
 
@@ -658,8 +652,8 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
658
652
  }
659
653
 
660
654
  void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample_weight, ddog_CharSlice *alloc_class) {
661
- struct stack_recorder_state *state;
662
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
655
+ stack_recorder_state *state;
656
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
663
657
  // FIXME: Heap sampling currently has to be done in 2 parts because the construction of locations is happening
664
658
  // very late in the allocation-sampling path (which is shared with the cpu sampling path). This can
665
659
  // be fixed with some refactoring but for now this leads to a less impactful change.
@@ -667,8 +661,8 @@ void track_object(VALUE recorder_instance, VALUE new_object, unsigned int sample
667
661
  }
668
662
 
669
663
  void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_CharSlice endpoint) {
670
- struct stack_recorder_state *state;
671
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
664
+ stack_recorder_state *state;
665
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
672
666
 
673
667
  locked_profile_slot active_slot = sampler_lock_active_profile(state);
674
668
 
@@ -682,8 +676,8 @@ void record_endpoint(VALUE recorder_instance, uint64_t local_root_span_id, ddog_
682
676
  }
683
677
 
684
678
  void recorder_after_gc_step(VALUE recorder_instance) {
685
- struct stack_recorder_state *state;
686
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
679
+ stack_recorder_state *state;
680
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
687
681
 
688
682
  if (state->heap_clean_after_gc_enabled) heap_recorder_update_young_objects(state->heap_recorder);
689
683
  }
@@ -693,7 +687,7 @@ void recorder_after_gc_step(VALUE recorder_instance) {
693
687
  // Heap recorder iteration context allows us access to stack recorder state and profile being serialized
694
688
  // during iteration of heap recorder live objects.
695
689
  typedef struct heap_recorder_iteration_context {
696
- struct stack_recorder_state *state;
690
+ stack_recorder_state *state;
697
691
  profile_slot *slot;
698
692
 
699
693
  bool error;
@@ -755,7 +749,7 @@ static bool add_heap_sample_to_active_profile_without_gvl(heap_recorder_iteratio
755
749
  return true;
756
750
  }
757
751
 
758
- static void build_heap_profile_without_gvl(struct stack_recorder_state *state, profile_slot *slot) {
752
+ static void build_heap_profile_without_gvl(stack_recorder_state *state, profile_slot *slot) {
759
753
  heap_recorder_iteration_context iteration_context = {
760
754
  .state = state,
761
755
  .slot = slot,
@@ -776,7 +770,7 @@ static void build_heap_profile_without_gvl(struct stack_recorder_state *state, p
776
770
  }
777
771
 
778
772
  static void *call_serialize_without_gvl(void *call_args) {
779
- struct call_serialize_without_gvl_arguments *args = (struct call_serialize_without_gvl_arguments *) call_args;
773
+ call_serialize_without_gvl_arguments *args = (call_serialize_without_gvl_arguments *) call_args;
780
774
 
781
775
  long serialize_no_gvl_start_time_ns = monotonic_wall_time_now_ns(DO_NOT_RAISE_ON_FAILURE);
782
776
 
@@ -802,7 +796,7 @@ VALUE enforce_recorder_instance(VALUE object) {
802
796
  return object;
803
797
  }
804
798
 
805
- static locked_profile_slot sampler_lock_active_profile(struct stack_recorder_state *state) {
799
+ static locked_profile_slot sampler_lock_active_profile(stack_recorder_state *state) {
806
800
  int error;
807
801
 
808
802
  for (int attempts = 0; attempts < 2; attempts++) {
@@ -829,7 +823,7 @@ static void sampler_unlock_active_profile(locked_profile_slot active_slot) {
829
823
  ENFORCE_SUCCESS_GVL(pthread_mutex_unlock(active_slot.mutex));
830
824
  }
831
825
 
832
- static profile_slot* serializer_flip_active_and_inactive_slots(struct stack_recorder_state *state) {
826
+ static profile_slot* serializer_flip_active_and_inactive_slots(stack_recorder_state *state) {
833
827
  int previously_active_slot = state->active_slot;
834
828
 
835
829
  if (previously_active_slot != 1 && previously_active_slot != 2) {
@@ -855,8 +849,8 @@ static profile_slot* serializer_flip_active_and_inactive_slots(struct stack_reco
855
849
  // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
856
850
  // It SHOULD NOT be used for other purposes.
857
851
  static VALUE _native_active_slot(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
858
- struct stack_recorder_state *state;
859
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
852
+ stack_recorder_state *state;
853
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
860
854
 
861
855
  return INT2NUM(state->active_slot);
862
856
  }
@@ -870,8 +864,8 @@ static VALUE _native_is_slot_one_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE
870
864
  static VALUE _native_is_slot_two_mutex_locked(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) { return test_slot_mutex_state(recorder_instance, 2); }
871
865
 
872
866
  static VALUE test_slot_mutex_state(VALUE recorder_instance, int slot) {
873
- struct stack_recorder_state *state;
874
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
867
+ stack_recorder_state *state;
868
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
875
869
 
876
870
  pthread_mutex_t *slot_mutex = (slot == 1) ? &state->mutex_slot_one : &state->mutex_slot_two;
877
871
 
@@ -901,8 +895,8 @@ static ddog_Timespec system_epoch_now_timespec(void) {
901
895
  // Assumption: This method gets called BEFORE restarting profiling -- e.g. there are no components attempting to
902
896
  // trigger samples at the same time.
903
897
  static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance) {
904
- struct stack_recorder_state *state;
905
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
898
+ stack_recorder_state *state;
899
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
906
900
 
907
901
  // In case the fork happened halfway through `serializer_flip_active_and_inactive_slots` execution and the
908
902
  // resulting state is inconsistent, we make sure to reset it back to the initial state.
@@ -918,7 +912,7 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_
918
912
 
919
913
  // Assumption 1: This method is called with the GVL being held, because `ddog_prof_Profile_reset` mutates the profile and must
920
914
  // not be interrupted part-way through by a VM fork.
921
- static void serializer_set_start_timestamp_for_next_profile(struct stack_recorder_state *state, ddog_Timespec start_time) {
915
+ static void serializer_set_start_timestamp_for_next_profile(stack_recorder_state *state, ddog_Timespec start_time) {
922
916
  // Before making this profile active, we reset it so that it uses the correct start_time for its start
923
917
  profile_slot *next_profile_slot = (state->active_slot == 1) ? &state->profile_slot_two : &state->profile_slot_one;
924
918
  reset_profile_slot(next_profile_slot, &start_time);
@@ -978,8 +972,8 @@ static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /*
978
972
  // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
979
973
  // It SHOULD NOT be used for other purposes.
980
974
  static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
981
- struct stack_recorder_state *state;
982
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
975
+ stack_recorder_state *state;
976
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
983
977
 
984
978
  heap_recorder_prepare_iteration(state->heap_recorder);
985
979
 
@@ -989,8 +983,8 @@ static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _se
989
983
  // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
990
984
  // It SHOULD NOT be used for other purposes.
991
985
  static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
992
- struct stack_recorder_state *state;
993
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
986
+ stack_recorder_state *state;
987
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
994
988
 
995
989
  heap_recorder_finish_iteration(state->heap_recorder);
996
990
 
@@ -1000,43 +994,15 @@ static VALUE _native_end_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self
1000
994
  // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
1001
995
  // It SHOULD NOT be used for other purposes.
1002
996
  static VALUE _native_debug_heap_recorder(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
1003
- struct stack_recorder_state *state;
1004
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
997
+ stack_recorder_state *state;
998
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
1005
999
 
1006
1000
  return heap_recorder_testonly_debug(state->heap_recorder);
1007
1001
  }
1008
1002
 
1009
- #pragma GCC diagnostic push
1010
- // rb_gc_force_recycle was deprecated in latest versions of Ruby and is a noop.
1011
- #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1012
- #pragma GCC diagnostic ignored "-Wunused-parameter"
1013
- // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
1014
- // It SHOULD NOT be used for other purposes.
1015
- static VALUE _native_gc_force_recycle(DDTRACE_UNUSED VALUE _self, VALUE obj) {
1016
- #ifdef HAVE_WORKING_RB_GC_FORCE_RECYCLE
1017
- rb_gc_force_recycle(obj);
1018
- #endif
1019
- return Qnil;
1020
- }
1021
- #pragma GCC diagnostic pop
1022
-
1023
- // This method exists only to enable testing Datadog::Profiling::StackRecorder behavior using RSpec.
1024
- // It SHOULD NOT be used for other purposes.
1025
- static VALUE _native_has_seen_id_flag(DDTRACE_UNUSED VALUE _self, VALUE obj) {
1026
- #ifndef NO_SEEN_OBJ_ID_FLAG
1027
- if (RB_FL_TEST(obj, RUBY_FL_SEEN_OBJ_ID)) {
1028
- return Qtrue;
1029
- } else {
1030
- return Qfalse;
1031
- }
1032
- #else
1033
- return Qfalse;
1034
- #endif
1035
- }
1036
-
1037
1003
  static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE recorder_instance) {
1038
- struct stack_recorder_state *state;
1039
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
1004
+ stack_recorder_state *state;
1005
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
1040
1006
 
1041
1007
  uint64_t total_serializations = state->stats_lifetime.serialization_successes + state->stats_lifetime.serialization_failures;
1042
1008
 
@@ -1074,15 +1040,15 @@ static VALUE build_profile_stats(profile_slot *slot, long serialization_time_ns,
1074
1040
  static VALUE _native_is_object_recorded(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE obj_id) {
1075
1041
  ENFORCE_TYPE(obj_id, T_FIXNUM);
1076
1042
 
1077
- struct stack_recorder_state *state;
1078
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
1043
+ stack_recorder_state *state;
1044
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
1079
1045
 
1080
1046
  return heap_recorder_testonly_is_object_recorded(state->heap_recorder, obj_id);
1081
1047
  }
1082
1048
 
1083
1049
  static VALUE _native_heap_recorder_reset_last_update(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance) {
1084
- struct stack_recorder_state *state;
1085
- TypedData_Get_Struct(recorder_instance, struct stack_recorder_state, &stack_recorder_typed_data, state);
1050
+ stack_recorder_state *state;
1051
+ TypedData_Get_Struct(recorder_instance, stack_recorder_state, &stack_recorder_typed_data, state);
1086
1052
 
1087
1053
  heap_recorder_testonly_reset_last_update(state->heap_recorder);
1088
1054
 
@@ -13,7 +13,7 @@ typedef struct {
13
13
  int64_t timeline_wall_time_ns;
14
14
  } sample_values;
15
15
 
16
- typedef struct sample_labels {
16
+ typedef struct {
17
17
  ddog_prof_Slice_Label labels;
18
18
 
19
19
  // This is used to allow the `Collectors::Stack` to modify the existing label, if any. This MUST be NULL or point
@@ -39,7 +39,7 @@ static inline long system_epoch_time_now_ns(raise_on_failure_setting raise_on_fa
39
39
  // https://docs.redhat.com/en/documentation/red_hat_enterprise_linux_for_real_time/7/html/reference_guide/sect-posix_clocks#Using_clock_getres_to_compare_clock_resolution
40
40
  // We introduce here a separate type for it, so as to make it harder to misuse/more explicit when these timestamps are used
41
41
 
42
- typedef struct coarse_instant {
42
+ typedef struct {
43
43
  long timestamp_ns;
44
44
  } coarse_instant;
45
45
 
@@ -0,0 +1,47 @@
1
+ #include <ruby.h>
2
+ #include <ruby/debug.h>
3
+ #include <stdbool.h>
4
+
5
+ #include "datadog_ruby_common.h"
6
+ #include "unsafe_api_calls_check.h"
7
+ #include "extconf.h"
8
+
9
+ static bool inside_unsafe_context = false;
10
+
11
+ #ifndef NO_POSTPONED_TRIGGER
12
+ static rb_postponed_job_handle_t check_for_unsafe_api_calls_handle;
13
+ #endif
14
+
15
+ static void check_for_unsafe_api_calls(DDTRACE_UNUSED void *_unused);
16
+
17
+ void unsafe_api_calls_check_init(void) {
18
+ #ifndef NO_POSTPONED_TRIGGER
19
+ int unused_flags = 0;
20
+
21
+ check_for_unsafe_api_calls_handle = rb_postponed_job_preregister(unused_flags, check_for_unsafe_api_calls, NULL);
22
+
23
+ if (check_for_unsafe_api_calls_handle == POSTPONED_JOB_HANDLE_INVALID) {
24
+ rb_raise(rb_eRuntimeError, "Failed to register check_for_unsafe_api_calls_handle postponed job (got POSTPONED_JOB_HANDLE_INVALID)");
25
+ }
26
+ #endif
27
+ }
28
+
29
+ void debug_enter_unsafe_context(void) {
30
+ inside_unsafe_context = true;
31
+
32
+ #ifndef NO_POSTPONED_TRIGGER
33
+ rb_postponed_job_trigger(check_for_unsafe_api_calls_handle);
34
+ #else
35
+ rb_postponed_job_register(0, check_for_unsafe_api_calls, NULL);
36
+ #endif
37
+ }
38
+
39
+ void debug_leave_unsafe_context(void) {
40
+ inside_unsafe_context = false;
41
+ }
42
+
43
+ static void check_for_unsafe_api_calls(DDTRACE_UNUSED void *_unused) {
44
+ if (inside_unsafe_context) rb_bug(
45
+ "Datadog Ruby profiler detected callback nested inside sample. Please report this at https://github.com/datadog/dd-trace-rb/blob/master/CONTRIBUTING.md#found-a-bug"
46
+ );
47
+ }
@@ -0,0 +1,31 @@
1
+ #pragma once
2
+
3
+ // This checker is used to detect accidental thread scheduling switching points happening during profiling sampling.
4
+ //
5
+ // Specifically, when the profiler is sampling, we're never supposed to call into Ruby code (e.g. methods
6
+ // implemented using Ruby code) or allocate Ruby objects.
7
+ // That's because those events introduce thread switch points, and really we don't the VM switching between threads
8
+ // in the middle of the profiler sampling.
9
+ // This includes raising exceptions, unless we're trying to stop the profiler, and even then we must be careful.
10
+ //
11
+ // The above is especially true in situations such as GC profiling or allocation/heap profiling, as in those situations
12
+ // we can even crash the Ruby VM if we switch away at the wrong time.
13
+ //
14
+ // The below APIs can be used to detect these situations. They work by relying on the following observation:
15
+ // in most (all?) thread switch points, Ruby will check for interrupts and run the postponed jobs.
16
+ //
17
+ // Thus, if we set a flag while we're sampling (inside_unsafe_context), trigger the postponed job, and then only unset
18
+ // the flag after sampling, he correct thing to happen is that the postponed job should never see the flag.
19
+ //
20
+ // If, however, we have a bug and there's a thread switch point, our postponed job will see the flag and immediately
21
+ // stop the Ruby VM before further damage happens (and hopefully giving us a stack trace clearly pointing to the culprit).
22
+
23
+ void unsafe_api_calls_check_init(void);
24
+
25
+ // IMPORTANT: This method **MUST** only be called from test code, as it causes an immediate hard-crash on the Ruby VM
26
+ // when it detects a potential issue, and that's not something we want for production apps.
27
+ //
28
+ // In the future we may introduce some kind of setting (off by default) to also allow this to be safely be used
29
+ // in production code if needed.
30
+ void debug_enter_unsafe_context(void);
31
+ void debug_leave_unsafe_context(void);
@@ -54,6 +54,9 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
54
54
  // Tags and endpoint are heap-allocated, so after here we can't raise exceptions otherwise we'll leak this memory
55
55
  // Start of exception-free zone to prevent leaks {{
56
56
  ddog_Endpoint *endpoint = ddog_endpoint_from_url(char_slice_from_ruby_string(agent_base_url));
57
+ if (endpoint == NULL) {
58
+ rb_raise(rb_eRuntimeError, "Failed to create endpoint from agent_base_url: %"PRIsVALUE, agent_base_url);
59
+ }
57
60
  ddog_Vec_Tag tags = convert_tags(tags_as_array);
58
61
 
59
62
  ddog_crasht_Config config = {
@@ -8,7 +8,7 @@ module Datadog
8
8
  module LibdatadogExtconfHelpers
9
9
  # Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
10
10
  # may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
11
- LIBDATADOG_VERSION = '~> 14.1.0.1.0'
11
+ LIBDATADOG_VERSION = '~> 14.3.1.1.0'
12
12
 
13
13
  # Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
14
14
  # libdatadog are moved after the extension gets compiled.