datadog 2.8.0 → 2.10.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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -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 +66 -56
  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_stack.h +2 -2
  10. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +221 -127
  11. data/ext/datadog_profiling_native_extension/heap_recorder.c +50 -92
  12. data/ext/datadog_profiling_native_extension/heap_recorder.h +2 -2
  13. data/ext/datadog_profiling_native_extension/http_transport.c +4 -4
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +3 -0
  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 +63 -76
  19. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -2
  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/lib/datadog/appsec/actions_handler.rb +27 -0
  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 +14 -8
  28. data/lib/datadog/appsec/configuration/settings.rb +9 -0
  29. data/lib/datadog/appsec/context.rb +74 -0
  30. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +12 -8
  31. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +6 -6
  32. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +4 -4
  33. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +1 -7
  34. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +20 -30
  35. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +6 -6
  36. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
  37. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +67 -96
  38. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +11 -11
  39. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +6 -6
  40. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +7 -7
  41. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
  42. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -60
  43. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +23 -33
  44. data/lib/datadog/appsec/contrib/rails/patcher.rb +4 -14
  45. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +7 -7
  46. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +45 -65
  47. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +5 -28
  48. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +6 -6
  49. data/lib/datadog/appsec/event.rb +6 -6
  50. data/lib/datadog/appsec/ext.rb +8 -1
  51. data/lib/datadog/appsec/metrics/collector.rb +38 -0
  52. data/lib/datadog/appsec/metrics/exporter.rb +35 -0
  53. data/lib/datadog/appsec/metrics/telemetry.rb +23 -0
  54. data/lib/datadog/appsec/metrics.rb +13 -0
  55. data/lib/datadog/appsec/monitor/gateway/watcher.rb +23 -32
  56. data/lib/datadog/appsec/monitor/reactive/set_user.rb +6 -6
  57. data/lib/datadog/appsec/processor/rule_loader.rb +0 -3
  58. data/lib/datadog/appsec/processor.rb +4 -3
  59. data/lib/datadog/appsec/response.rb +18 -80
  60. data/lib/datadog/appsec/security_engine/result.rb +67 -0
  61. data/lib/datadog/appsec/security_engine/runner.rb +88 -0
  62. data/lib/datadog/appsec/security_engine.rb +9 -0
  63. data/lib/datadog/appsec.rb +17 -8
  64. data/lib/datadog/auto_instrument.rb +3 -0
  65. data/lib/datadog/core/configuration/agent_settings_resolver.rb +39 -11
  66. data/lib/datadog/core/configuration/components.rb +4 -2
  67. data/lib/datadog/core/configuration.rb +1 -1
  68. data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
  69. data/lib/datadog/core/crashtracking/component.rb +1 -3
  70. data/lib/datadog/core/telemetry/event.rb +87 -3
  71. data/lib/datadog/core/telemetry/logging.rb +2 -2
  72. data/lib/datadog/core/telemetry/metric.rb +22 -0
  73. data/lib/datadog/core/telemetry/worker.rb +33 -0
  74. data/lib/datadog/di/base.rb +115 -0
  75. data/lib/datadog/di/code_tracker.rb +7 -4
  76. data/lib/datadog/di/component.rb +19 -11
  77. data/lib/datadog/di/configuration/settings.rb +11 -1
  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 +39 -18
  82. data/lib/datadog/di/{init.rb → preload.rb} +2 -4
  83. data/lib/datadog/di/probe_manager.rb +4 -4
  84. data/lib/datadog/di/probe_notification_builder.rb +22 -2
  85. data/lib/datadog/di/probe_notifier_worker.rb +5 -6
  86. data/lib/datadog/di/redactor.rb +0 -1
  87. data/lib/datadog/di/remote.rb +30 -9
  88. data/lib/datadog/di/transport.rb +2 -4
  89. data/lib/datadog/di.rb +5 -108
  90. data/lib/datadog/kit/appsec/events.rb +3 -3
  91. data/lib/datadog/kit/identity.rb +4 -4
  92. data/lib/datadog/profiling/component.rb +55 -53
  93. data/lib/datadog/profiling/http_transport.rb +1 -26
  94. data/lib/datadog/tracing/contrib/action_cable/integration.rb +5 -2
  95. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +6 -2
  96. data/lib/datadog/tracing/contrib/action_pack/integration.rb +5 -2
  97. data/lib/datadog/tracing/contrib/action_view/integration.rb +5 -2
  98. data/lib/datadog/tracing/contrib/active_job/integration.rb +5 -2
  99. data/lib/datadog/tracing/contrib/active_record/integration.rb +6 -2
  100. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +3 -1
  101. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +3 -1
  102. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -0
  103. data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -2
  104. data/lib/datadog/tracing/contrib/auto_instrument.rb +2 -2
  105. data/lib/datadog/tracing/contrib/aws/integration.rb +3 -0
  106. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -0
  107. data/lib/datadog/tracing/contrib/extensions.rb +15 -3
  108. data/lib/datadog/tracing/contrib/http/integration.rb +3 -0
  109. data/lib/datadog/tracing/contrib/httprb/integration.rb +3 -0
  110. data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -0
  111. data/lib/datadog/tracing/contrib/mongodb/integration.rb +3 -0
  112. data/lib/datadog/tracing/contrib/opensearch/integration.rb +3 -0
  113. data/lib/datadog/tracing/contrib/presto/integration.rb +3 -0
  114. data/lib/datadog/tracing/contrib/rack/integration.rb +2 -2
  115. data/lib/datadog/tracing/contrib/rails/framework.rb +2 -2
  116. data/lib/datadog/tracing/contrib/rails/patcher.rb +1 -1
  117. data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -0
  118. data/lib/datadog/tracing/span.rb +12 -4
  119. data/lib/datadog/tracing/span_event.rb +123 -3
  120. data/lib/datadog/tracing/span_operation.rb +6 -0
  121. data/lib/datadog/tracing/transport/serializable_trace.rb +24 -6
  122. data/lib/datadog/version.rb +1 -1
  123. metadata +40 -17
  124. data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -14
  125. data/lib/datadog/appsec/processor/context.rb +0 -107
  126. data/lib/datadog/appsec/reactive/operation.rb +0 -68
  127. data/lib/datadog/appsec/scope.rb +0 -58
  128. data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
@@ -1,8 +1,6 @@
1
1
  #include "heap_recorder.h"
2
- #include <pthread.h>
3
2
  #include "ruby/st.h"
4
3
  #include "ruby_helpers.h"
5
- #include <errno.h>
6
4
  #include "collectors_stack.h"
7
5
  #include "libdatadog_helpers.h"
8
6
  #include "time_helpers.h"
@@ -30,7 +28,6 @@ typedef struct {
30
28
  char *filename;
31
29
  int32_t line;
32
30
  } heap_frame;
33
- static st_index_t heap_frame_hash(heap_frame*, st_index_t seed);
34
31
 
35
32
  // A compact representation of a stacktrace for a heap allocation.
36
33
  //
@@ -113,14 +110,6 @@ static void object_record_free(object_record*);
113
110
  static VALUE object_record_inspect(object_record*);
114
111
  static object_record SKIPPED_RECORD = {0};
115
112
 
116
- // A wrapper around an object record that is in the process of being recorded and was not
117
- // yet committed.
118
- typedef struct {
119
- // Pointer to the (potentially partial) object_record containing metadata about an ongoing recording.
120
- // When NULL, this symbolizes an unstarted/invalid recording.
121
- object_record *object_record;
122
- } recording;
123
-
124
113
  struct heap_recorder {
125
114
  // Config
126
115
  // Whether the recorder should try to determine approximate sizes for tracked objects.
@@ -140,6 +129,9 @@ struct heap_recorder {
140
129
  // outside the GVL.
141
130
  // NOTE: This table has ownership of its object_records. The keys are longs and so are
142
131
  // passed as values.
132
+ //
133
+ // TODO: @ivoanjo We've evolved to actually never need to look up on object_records (we only insert and iterate),
134
+ // so right now this seems to be just a really really fancy self-resizing list/set.
143
135
  st_table *object_records;
144
136
 
145
137
  // Map[obj_id: long, record: object_record*]
@@ -162,7 +154,7 @@ struct heap_recorder {
162
154
  long last_update_ns;
163
155
 
164
156
  // Data for a heap recording that was started but not yet ended
165
- recording active_recording;
157
+ object_record *active_recording;
166
158
 
167
159
  // Reusable location array, implementing a flyweight pattern for things like iteration.
168
160
  ddog_prof_Location *reusable_locations;
@@ -207,7 +199,7 @@ static int st_object_record_update(st_data_t, st_data_t, st_data_t);
207
199
  static int st_object_records_iterate(st_data_t, st_data_t, st_data_t);
208
200
  static int st_object_records_debug(st_data_t key, st_data_t value, st_data_t extra);
209
201
  static int update_object_record_entry(st_data_t*, st_data_t*, st_data_t, int);
210
- static void commit_recording(heap_recorder*, heap_record*, recording);
202
+ static void commit_recording(heap_recorder *, heap_record *, object_record *active_recording);
211
203
  static VALUE end_heap_allocation_recording(VALUE end_heap_allocation_args);
212
204
  static void heap_recorder_update(heap_recorder *heap_recorder, bool full_update);
213
205
  static inline double ewma_stat(double previous, double current);
@@ -228,7 +220,7 @@ heap_recorder* heap_recorder_new(void) {
228
220
  recorder->object_records = st_init_numtable();
229
221
  recorder->object_records_snapshot = NULL;
230
222
  recorder->reusable_locations = ruby_xcalloc(MAX_FRAMES_LIMIT, sizeof(ddog_prof_Location));
231
- recorder->active_recording = (recording) {0};
223
+ recorder->active_recording = NULL;
232
224
  recorder->size_enabled = true;
233
225
  recorder->sample_rate = 1; // By default do no sampling on top of what allocation profiling already does
234
226
 
@@ -254,9 +246,9 @@ void heap_recorder_free(heap_recorder *heap_recorder) {
254
246
  st_foreach(heap_recorder->heap_records, st_heap_record_entry_free, 0);
255
247
  st_free_table(heap_recorder->heap_records);
256
248
 
257
- if (heap_recorder->active_recording.object_record != NULL && heap_recorder->active_recording.object_record != &SKIPPED_RECORD) {
249
+ if (heap_recorder->active_recording != NULL && heap_recorder->active_recording != &SKIPPED_RECORD) {
258
250
  // If there's a partial object record, clean it up as well
259
- object_record_free(heap_recorder->active_recording.object_record);
251
+ object_record_free(heap_recorder->active_recording);
260
252
  }
261
253
 
262
254
  ruby_xfree(heap_recorder->reusable_locations);
@@ -301,7 +293,7 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder) {
301
293
  //
302
294
  // There is one small caveat though: fork only preserves one thread and in a Ruby app, that
303
295
  // will be the thread holding on to the GVL. Since we support iteration on the heap recorder
304
- // outside of the GVL, any state specific to that interaction may be incosistent after fork
296
+ // outside of the GVL, any state specific to that interaction may be inconsistent after fork
305
297
  // (e.g. an acquired lock for thread safety). Iteration operates on object_records_snapshot
306
298
  // though and that one will be updated on next heap_recorder_prepare_iteration so we really
307
299
  // only need to finish any iteration that might have been left unfinished.
@@ -313,18 +305,17 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder) {
313
305
  heap_recorder->stats_lifetime = (struct stats_lifetime) {0};
314
306
  }
315
307
 
316
- void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice *alloc_class) {
308
+ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice alloc_class) {
317
309
  if (heap_recorder == NULL) {
318
310
  return;
319
311
  }
320
312
 
321
- if (heap_recorder->active_recording.object_record != NULL) {
313
+ if (heap_recorder->active_recording != NULL) {
322
314
  rb_raise(rb_eRuntimeError, "Detected consecutive heap allocation recording starts without end.");
323
315
  }
324
316
 
325
- if (heap_recorder->num_recordings_skipped + 1 < heap_recorder->sample_rate) {
326
- heap_recorder->active_recording.object_record = &SKIPPED_RECORD;
327
- heap_recorder->num_recordings_skipped++;
317
+ if (++heap_recorder->num_recordings_skipped < heap_recorder->sample_rate) {
318
+ heap_recorder->active_recording = &SKIPPED_RECORD;
328
319
  return;
329
320
  }
330
321
 
@@ -335,13 +326,15 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj
335
326
  rb_raise(rb_eRuntimeError, "Detected a bignum object id. These are not supported by heap profiling.");
336
327
  }
337
328
 
338
- heap_recorder->active_recording = (recording) {
339
- .object_record = object_record_new(FIX2LONG(ruby_obj_id), NULL, (live_object_data) {
340
- .weight = weight * heap_recorder->sample_rate,
341
- .class = alloc_class != NULL ? string_from_char_slice(*alloc_class) : NULL,
342
- .alloc_gen = rb_gc_count(),
343
- }),
344
- };
329
+ heap_recorder->active_recording = object_record_new(
330
+ FIX2LONG(ruby_obj_id),
331
+ NULL,
332
+ (live_object_data) {
333
+ .weight = weight * heap_recorder->sample_rate,
334
+ .class = string_from_char_slice(alloc_class),
335
+ .alloc_gen = rb_gc_count(),
336
+ }
337
+ );
345
338
  }
346
339
 
347
340
  // end_heap_allocation_recording_with_rb_protect gets called while the stack_recorder is holding one of the profile
@@ -349,6 +342,10 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj
349
342
  // with an rb_protect.
350
343
  __attribute__((warn_unused_result))
351
344
  int end_heap_allocation_recording_with_rb_protect(struct heap_recorder *heap_recorder, ddog_prof_Slice_Location locations) {
345
+ if (heap_recorder == NULL) {
346
+ return 0;
347
+ }
348
+
352
349
  int exception_state;
353
350
  struct end_heap_allocation_args end_heap_allocation_args = {
354
351
  .heap_recorder = heap_recorder,
@@ -364,22 +361,18 @@ static VALUE end_heap_allocation_recording(VALUE end_heap_allocation_args) {
364
361
  struct heap_recorder *heap_recorder = args->heap_recorder;
365
362
  ddog_prof_Slice_Location locations = args->locations;
366
363
 
367
- if (heap_recorder == NULL) {
368
- return Qnil;
369
- }
364
+ object_record *active_recording = heap_recorder->active_recording;
370
365
 
371
- recording active_recording = heap_recorder->active_recording;
372
-
373
- if (active_recording.object_record == NULL) {
366
+ if (active_recording == NULL) {
374
367
  // Recording ended without having been started?
375
368
  rb_raise(rb_eRuntimeError, "Ended a heap recording that was not started");
376
369
  }
377
370
  // From now on, mark the global active recording as invalid so we can short-circuit at any point
378
371
  // and not end up with a still active recording. the local active_recording still holds the
379
372
  // data required for committing though.
380
- heap_recorder->active_recording = (recording) {0};
373
+ heap_recorder->active_recording = NULL;
381
374
 
382
- if (active_recording.object_record == &SKIPPED_RECORD) { // special marker when we decided to skip due to sampling
375
+ if (active_recording == &SKIPPED_RECORD) { // special marker when we decided to skip due to sampling
383
376
  return Qnil;
384
377
  }
385
378
 
@@ -698,6 +691,7 @@ static int st_object_records_iterate(DDTRACE_UNUSED st_data_t key, st_data_t val
698
691
  iteration_data.object_data = record->object_data;
699
692
  iteration_data.locations = (ddog_prof_Slice_Location) {.ptr = locations, .len = stack->frames_len};
700
693
 
694
+ // This is expected to be StackRecorder's add_heap_sample_to_active_profile_without_gvl
701
695
  if (!context->for_each_callback(iteration_data, context->for_each_callback_extra_arg)) {
702
696
  return ST_STOP;
703
697
  }
@@ -715,49 +709,35 @@ static int st_object_records_debug(DDTRACE_UNUSED st_data_t key, st_data_t value
715
709
  return ST_CONTINUE;
716
710
  }
717
711
 
718
- // Struct holding data required for an update operation on heap_records
719
- typedef struct {
720
- // [in] The recording containing the new object record we want to add.
721
- // NOTE: Transfer of ownership of the contained object record is assumed, do not re-use it after call to ::update_object_record_entry
722
- recording recording;
723
-
724
- // [in] The heap recorder where the update is happening.
725
- heap_recorder *heap_recorder;
726
- } object_record_update_data;
727
-
728
- static int update_object_record_entry(DDTRACE_UNUSED st_data_t *key, st_data_t *value, st_data_t data, int existing) {
729
- object_record_update_data *update_data = (object_record_update_data*) data;
730
- recording recording = update_data->recording;
731
- object_record *new_object_record = recording.object_record;
732
- if (existing) {
733
- object_record *existing_record = (object_record*) (*value);
734
-
735
- // This is not supposed to happen, raising...
736
- VALUE existing_inspect = object_record_inspect(existing_record);
737
- VALUE new_inspect = object_record_inspect(new_object_record);
738
- rb_raise(rb_eRuntimeError, "Object ids are supposed to be unique. We got 2 allocation recordings with "
739
- "the same id. previous=%"PRIsVALUE" new=%"PRIsVALUE, existing_inspect, new_inspect);
712
+ static int update_object_record_entry(DDTRACE_UNUSED st_data_t *key, st_data_t *value, st_data_t new_object_record, int existing) {
713
+ if (!existing) {
714
+ (*value) = (st_data_t) new_object_record; // Expected to be a `object_record *`
715
+ } else {
716
+ // If key already existed, we don't touch the existing value, so it can be used for diagnostics
740
717
  }
741
- // Always carry on with the update, we want the new record to be there at the end
742
- (*value) = (st_data_t) new_object_record;
743
718
  return ST_CONTINUE;
744
719
  }
745
720
 
746
- static void commit_recording(heap_recorder *heap_recorder, heap_record *heap_record, recording recording) {
721
+ static void commit_recording(heap_recorder *heap_recorder, heap_record *heap_record, object_record *active_recording) {
747
722
  // Link the object record with the corresponding heap record. This was the last remaining thing we
748
723
  // needed to fully build the object_record.
749
- recording.object_record->heap_record = heap_record;
724
+ active_recording->heap_record = heap_record;
750
725
  if (heap_record->num_tracked_objects == UINT32_MAX) {
751
726
  rb_raise(rb_eRuntimeError, "Reached maximum number of tracked objects for heap record");
752
727
  }
753
728
  heap_record->num_tracked_objects++;
754
729
 
755
- // Update object_records with the data for this new recording
756
- object_record_update_data update_data = (object_record_update_data) {
757
- .heap_recorder = heap_recorder,
758
- .recording = recording,
759
- };
760
- st_update(heap_recorder->object_records, recording.object_record->obj_id, update_object_record_entry, (st_data_t) &update_data);
730
+ int existing_error = st_update(heap_recorder->object_records, active_recording->obj_id, update_object_record_entry, (st_data_t) active_recording);
731
+ if (existing_error) {
732
+ object_record *existing_record = NULL;
733
+ st_lookup(heap_recorder->object_records, active_recording->obj_id, (st_data_t *) &existing_record);
734
+ if (existing_record == NULL) rb_raise(rb_eRuntimeError, "Unexpected NULL when reading existing record");
735
+
736
+ VALUE existing_inspect = object_record_inspect(existing_record);
737
+ VALUE new_inspect = object_record_inspect(active_recording);
738
+ rb_raise(rb_eRuntimeError, "Object ids are supposed to be unique. We got 2 allocation recordings with "
739
+ "the same id. previous={%"PRIsVALUE"} new={%"PRIsVALUE"}", existing_inspect, new_inspect);
740
+ }
761
741
  }
762
742
 
763
743
  // Struct holding data required for an update operation on heap_records
@@ -867,7 +847,6 @@ void heap_record_free(heap_record *record) {
867
847
  ruby_xfree(record);
868
848
  }
869
849
 
870
-
871
850
  // =================
872
851
  // Object Record API
873
852
  // =================
@@ -917,25 +896,6 @@ VALUE object_record_inspect(object_record *record) {
917
896
  // ==============
918
897
  // Heap Frame API
919
898
  // ==============
920
- int heap_frame_cmp(heap_frame *f1, heap_frame *f2) {
921
- int line_diff = (int) (f1->line - f2->line);
922
- if (line_diff != 0) {
923
- return line_diff;
924
- }
925
- int cmp = strcmp(f1->name, f2->name);
926
- if (cmp != 0) {
927
- return cmp;
928
- }
929
- return strcmp(f1->filename, f2->filename);
930
- }
931
-
932
- // TODO: Research potential performance improvements around hashing stuff here
933
- // once we have a benchmarking suite.
934
- // Example: Each call to st_hash is calling murmur_finish and we may want
935
- // to only finish once per structure, not per field?
936
- // Example: There may be a more efficient hashing for line that is not the
937
- // generic st_hash algorithm?
938
-
939
899
  // WARN: Must be kept in-sync with ::char_slice_hash
940
900
  st_index_t string_hash(char *str, st_index_t seed) {
941
901
  return st_hash(str, strlen(str), seed);
@@ -971,9 +931,7 @@ st_index_t ddog_location_hash(ddog_prof_Location location, st_index_t seed) {
971
931
  heap_stack* heap_stack_new(ddog_prof_Slice_Location locations) {
972
932
  uint16_t frames_len = locations.len;
973
933
  if (frames_len > MAX_FRAMES_LIMIT) {
974
- // This should not be happening anyway since MAX_FRAMES_LIMIT should be shared with
975
- // the stacktrace construction mechanism. If it happens, lets just raise. This should
976
- // be safe since only allocate with the GVL anyway.
934
+ // This is not expected as MAX_FRAMES_LIMIT is shared with the stacktrace construction mechanism
977
935
  rb_raise(rb_eRuntimeError, "Found stack with more than %d frames (%d)", MAX_FRAMES_LIMIT, frames_len);
978
936
  }
979
937
  heap_stack *stack = ruby_xcalloc(1, sizeof(heap_stack) + frames_len * sizeof(heap_frame));
@@ -17,7 +17,7 @@
17
17
  typedef struct heap_recorder heap_recorder;
18
18
 
19
19
  // Extra data associated with each live object being tracked.
20
- typedef struct live_object_data {
20
+ typedef struct {
21
21
  // The weight of this object from a sampling perspective.
22
22
  //
23
23
  // A notion of weight is preserved for each tracked object to allow for an approximate
@@ -105,7 +105,7 @@ void heap_recorder_after_fork(heap_recorder *heap_recorder);
105
105
  // The sampling weight of this object.
106
106
  //
107
107
  // WARN: It needs to be paired with a ::end_heap_allocation_recording call.
108
- void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice *alloc_class);
108
+ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj, unsigned int weight, ddog_CharSlice alloc_class);
109
109
 
110
110
  // End a previously started heap allocation recording on the heap recorder.
111
111
  //
@@ -13,13 +13,13 @@ static VALUE error_symbol = Qnil; // :error in Ruby
13
13
 
14
14
  static VALUE library_version_string = Qnil;
15
15
 
16
- struct call_exporter_without_gvl_arguments {
16
+ typedef struct {
17
17
  ddog_prof_Exporter *exporter;
18
18
  ddog_prof_Exporter_Request_BuildResult *build_result;
19
19
  ddog_CancellationToken *cancel_token;
20
20
  ddog_prof_Exporter_SendResult result;
21
21
  bool send_ran;
22
- };
22
+ } call_exporter_without_gvl_arguments;
23
23
 
24
24
  static inline ddog_ByteSlice byte_slice_from_ruby_string(VALUE string);
25
25
  static VALUE _native_validate_exporter(VALUE self, VALUE exporter_configuration);
@@ -165,7 +165,7 @@ static VALUE perform_export(
165
165
 
166
166
  // We'll release the Global VM Lock while we're calling send, so that the Ruby VM can continue to work while this
167
167
  // is pending
168
- struct call_exporter_without_gvl_arguments args =
168
+ call_exporter_without_gvl_arguments args =
169
169
  {.exporter = exporter, .build_result = &build_result, .cancel_token = cancel_token, .send_ran = false};
170
170
 
171
171
  // We use rb_thread_call_without_gvl2 instead of rb_thread_call_without_gvl as the gvl2 variant never raises any
@@ -300,7 +300,7 @@ static VALUE _native_do_export(
300
300
  }
301
301
 
302
302
  static void *call_exporter_without_gvl(void *call_args) {
303
- struct call_exporter_without_gvl_arguments *args = (struct call_exporter_without_gvl_arguments*) call_args;
303
+ call_exporter_without_gvl_arguments *args = (call_exporter_without_gvl_arguments*) call_args;
304
304
 
305
305
  args->result = ddog_prof_Exporter_send(args->exporter, &args->build_result->ok, args->cancel_token);
306
306
  args->send_ran = true;
@@ -800,3 +800,6 @@ static inline int ddtrace_imemo_type(VALUE imemo) {
800
800
  return current_thread;
801
801
  }
802
802
  #endif
803
+
804
+ // Is the VM smack in the middle of raising an exception?
805
+ bool is_raised_flag_set(VALUE thread) { return thread_struct_from_object(thread)->ec->raised_flag > 0; }
@@ -18,7 +18,7 @@ typedef struct {
18
18
  rb_nativethread_id_t owner;
19
19
  } current_gvl_owner;
20
20
 
21
- typedef struct frame_info {
21
+ typedef struct {
22
22
  union {
23
23
  struct {
24
24
  VALUE iseq;
@@ -68,3 +68,5 @@ const char *imemo_kind(VALUE imemo);
68
68
 
69
69
  #define ENFORCE_THREAD(value) \
70
70
  { if (RB_UNLIKELY(!rb_typeddata_is_kind_of(value, RTYPEDDATA_TYPE(rb_thread_current())))) raise_unexpected_type(value, ADD_QUOTES(value), "Thread", __FILE__, __LINE__, __func__); }
71
+
72
+ bool is_raised_flag_set(VALUE thread);
@@ -11,6 +11,7 @@
11
11
  #include "ruby_helpers.h"
12
12
  #include "setup_signal_handler.h"
13
13
  #include "time_helpers.h"
14
+ #include "unsafe_api_calls_check.h"
14
15
 
15
16
  // Each class/module here is implemented in their separate file
16
17
  void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module);
@@ -56,6 +57,7 @@ void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
56
57
  collectors_thread_context_init(profiling_module);
57
58
  http_transport_init(profiling_module);
58
59
  stack_recorder_init(profiling_module);
60
+ unsafe_api_calls_check_init();
59
61
 
60
62
  // Hosts methods used for testing the native code using RSpec
61
63
  VALUE testing_module = rb_define_module_under(native_extension_module, "Testing");
@@ -83,16 +85,16 @@ static VALUE native_working_p(DDTRACE_UNUSED VALUE _self) {
83
85
  return Qtrue;
84
86
  }
85
87
 
86
- struct trigger_grab_gvl_and_raise_arguments {
88
+ typedef struct {
87
89
  VALUE exception_class;
88
90
  char *test_message;
89
91
  int test_message_arg;
90
- };
92
+ } trigger_grab_gvl_and_raise_arguments;
91
93
 
92
94
  static VALUE _native_grab_gvl_and_raise(DDTRACE_UNUSED VALUE _self, VALUE exception_class, VALUE test_message, VALUE test_message_arg, VALUE release_gvl) {
93
95
  ENFORCE_TYPE(test_message, T_STRING);
94
96
 
95
- struct trigger_grab_gvl_and_raise_arguments args;
97
+ trigger_grab_gvl_and_raise_arguments args;
96
98
 
97
99
  args.exception_class = exception_class;
98
100
  args.test_message = StringValueCStr(test_message);
@@ -108,7 +110,7 @@ static VALUE _native_grab_gvl_and_raise(DDTRACE_UNUSED VALUE _self, VALUE except
108
110
  }
109
111
 
110
112
  static void *trigger_grab_gvl_and_raise(void *trigger_args) {
111
- struct trigger_grab_gvl_and_raise_arguments *args = (struct trigger_grab_gvl_and_raise_arguments *) trigger_args;
113
+ trigger_grab_gvl_and_raise_arguments *args = (trigger_grab_gvl_and_raise_arguments *) trigger_args;
112
114
 
113
115
  if (args->test_message_arg >= 0) {
114
116
  grab_gvl_and_raise(args->exception_class, "%s%d", args->test_message, args->test_message_arg);
@@ -119,16 +121,16 @@ static void *trigger_grab_gvl_and_raise(void *trigger_args) {
119
121
  return NULL;
120
122
  }
121
123
 
122
- struct trigger_grab_gvl_and_raise_syserr_arguments {
124
+ typedef struct {
123
125
  int syserr_errno;
124
126
  char *test_message;
125
127
  int test_message_arg;
126
- };
128
+ } trigger_grab_gvl_and_raise_syserr_arguments;
127
129
 
128
130
  static VALUE _native_grab_gvl_and_raise_syserr(DDTRACE_UNUSED VALUE _self, VALUE syserr_errno, VALUE test_message, VALUE test_message_arg, VALUE release_gvl) {
129
131
  ENFORCE_TYPE(test_message, T_STRING);
130
132
 
131
- struct trigger_grab_gvl_and_raise_syserr_arguments args;
133
+ trigger_grab_gvl_and_raise_syserr_arguments args;
132
134
 
133
135
  args.syserr_errno = NUM2INT(syserr_errno);
134
136
  args.test_message = StringValueCStr(test_message);
@@ -144,7 +146,7 @@ static VALUE _native_grab_gvl_and_raise_syserr(DDTRACE_UNUSED VALUE _self, VALUE
144
146
  }
145
147
 
146
148
  static void *trigger_grab_gvl_and_raise_syserr(void *trigger_args) {
147
- struct trigger_grab_gvl_and_raise_syserr_arguments *args = (struct trigger_grab_gvl_and_raise_syserr_arguments *) trigger_args;
149
+ trigger_grab_gvl_and_raise_syserr_arguments *args = (trigger_grab_gvl_and_raise_syserr_arguments *) trigger_args;
148
150
 
149
151
  if (args->test_message_arg >= 0) {
150
152
  grab_gvl_and_raise_syserr(args->syserr_errno, "%s%d", args->test_message, args->test_message_arg);
@@ -23,18 +23,18 @@ void ruby_helpers_init(void) {
23
23
 
24
24
  #define MAX_RAISE_MESSAGE_SIZE 256
25
25
 
26
- struct raise_arguments {
26
+ typedef struct {
27
27
  VALUE exception_class;
28
28
  char exception_message[MAX_RAISE_MESSAGE_SIZE];
29
- };
29
+ } raise_args;
30
30
 
31
31
  static void *trigger_raise(void *raise_arguments) {
32
- struct raise_arguments *args = (struct raise_arguments *) raise_arguments;
32
+ raise_args *args = (raise_args *) raise_arguments;
33
33
  rb_raise(args->exception_class, "%s", args->exception_message);
34
34
  }
35
35
 
36
36
  void grab_gvl_and_raise(VALUE exception_class, const char *format_string, ...) {
37
- struct raise_arguments args;
37
+ raise_args args;
38
38
 
39
39
  args.exception_class = exception_class;
40
40
 
@@ -55,18 +55,18 @@ void grab_gvl_and_raise(VALUE exception_class, const char *format_string, ...) {
55
55
  rb_bug("[ddtrace] Unexpected: Reached the end of grab_gvl_and_raise while raising '%s'\n", args.exception_message);
56
56
  }
57
57
 
58
- struct syserr_raise_arguments {
58
+ typedef struct {
59
59
  int syserr_errno;
60
60
  char exception_message[MAX_RAISE_MESSAGE_SIZE];
61
- };
61
+ } syserr_raise_args;
62
62
 
63
63
  static void *trigger_syserr_raise(void *syserr_raise_arguments) {
64
- struct syserr_raise_arguments *args = (struct syserr_raise_arguments *) syserr_raise_arguments;
64
+ syserr_raise_args *args = (syserr_raise_args *) syserr_raise_arguments;
65
65
  rb_syserr_fail(args->syserr_errno, args->exception_message);
66
66
  }
67
67
 
68
68
  void grab_gvl_and_raise_syserr(int syserr_errno, const char *format_string, ...) {
69
- struct syserr_raise_arguments args;
69
+ syserr_raise_args args;
70
70
 
71
71
  args.syserr_errno = syserr_errno;
72
72