datadog 2.2.0 → 2.4.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 (196) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -2
  3. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +9 -1
  4. data/ext/datadog_profiling_loader/extconf.rb +14 -26
  5. data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
  6. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
  7. data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
  8. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +257 -69
  9. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +53 -28
  10. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
  11. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
  12. data/ext/datadog_profiling_native_extension/collectors_stack.c +136 -81
  13. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +661 -48
  15. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +10 -1
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +83 -0
  17. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +53 -0
  18. data/ext/datadog_profiling_native_extension/extconf.rb +91 -69
  19. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +50 -0
  20. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +75 -0
  21. data/ext/datadog_profiling_native_extension/heap_recorder.c +54 -12
  22. data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
  23. data/ext/datadog_profiling_native_extension/helpers.h +6 -17
  24. data/ext/datadog_profiling_native_extension/http_transport.c +41 -9
  25. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
  26. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
  27. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -172
  28. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +116 -139
  29. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +20 -11
  30. data/ext/datadog_profiling_native_extension/profiling.c +1 -3
  31. data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
  32. data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
  33. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
  34. data/ext/datadog_profiling_native_extension/stack_recorder.c +14 -2
  35. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
  36. data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
  37. data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
  38. data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +37 -22
  39. data/ext/libdatadog_api/datadog_ruby_common.c +83 -0
  40. data/ext/libdatadog_api/datadog_ruby_common.h +53 -0
  41. data/ext/libdatadog_api/extconf.rb +108 -0
  42. data/ext/libdatadog_api/macos_development.md +26 -0
  43. data/ext/libdatadog_extconf_helpers.rb +130 -0
  44. data/lib/datadog/appsec/assets/waf_rules/recommended.json +2184 -108
  45. data/lib/datadog/appsec/assets/waf_rules/strict.json +1430 -2
  46. data/lib/datadog/appsec/component.rb +29 -8
  47. data/lib/datadog/appsec/configuration/settings.rb +2 -2
  48. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +1 -0
  49. data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +21 -0
  50. data/lib/datadog/appsec/contrib/devise/patcher.rb +12 -2
  51. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +35 -0
  52. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +109 -0
  53. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +71 -0
  54. data/lib/datadog/appsec/contrib/graphql/integration.rb +54 -0
  55. data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
  56. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
  57. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +3 -6
  58. data/lib/datadog/appsec/event.rb +1 -1
  59. data/lib/datadog/appsec/processor/actions.rb +1 -1
  60. data/lib/datadog/appsec/processor/rule_loader.rb +3 -1
  61. data/lib/datadog/appsec/processor/rule_merger.rb +33 -15
  62. data/lib/datadog/appsec/processor.rb +36 -37
  63. data/lib/datadog/appsec/rate_limiter.rb +25 -40
  64. data/lib/datadog/appsec/remote.rb +7 -3
  65. data/lib/datadog/appsec/response.rb +15 -1
  66. data/lib/datadog/appsec.rb +3 -2
  67. data/lib/datadog/core/configuration/components.rb +18 -15
  68. data/lib/datadog/core/configuration/settings.rb +135 -9
  69. data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
  70. data/lib/datadog/core/crashtracking/component.rb +111 -0
  71. data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
  72. data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
  73. data/lib/datadog/core/environment/execution.rb +5 -5
  74. data/lib/datadog/core/metrics/client.rb +7 -0
  75. data/lib/datadog/core/rate_limiter.rb +183 -0
  76. data/lib/datadog/core/remote/client/capabilities.rb +4 -3
  77. data/lib/datadog/core/remote/component.rb +4 -2
  78. data/lib/datadog/core/remote/negotiation.rb +4 -4
  79. data/lib/datadog/core/remote/tie.rb +2 -0
  80. data/lib/datadog/core/runtime/metrics.rb +1 -1
  81. data/lib/datadog/core/telemetry/component.rb +51 -2
  82. data/lib/datadog/core/telemetry/emitter.rb +9 -11
  83. data/lib/datadog/core/telemetry/event.rb +37 -1
  84. data/lib/datadog/core/telemetry/ext.rb +1 -0
  85. data/lib/datadog/core/telemetry/http/adapters/net.rb +10 -12
  86. data/lib/datadog/core/telemetry/http/ext.rb +3 -0
  87. data/lib/datadog/core/telemetry/http/transport.rb +38 -9
  88. data/lib/datadog/core/telemetry/logger.rb +51 -0
  89. data/lib/datadog/core/telemetry/logging.rb +71 -0
  90. data/lib/datadog/core/telemetry/request.rb +13 -1
  91. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  92. data/lib/datadog/core/utils/time.rb +12 -0
  93. data/lib/datadog/di/code_tracker.rb +168 -0
  94. data/lib/datadog/di/configuration/settings.rb +163 -0
  95. data/lib/datadog/di/configuration.rb +11 -0
  96. data/lib/datadog/di/error.rb +31 -0
  97. data/lib/datadog/di/extensions.rb +16 -0
  98. data/lib/datadog/di/probe.rb +133 -0
  99. data/lib/datadog/di/probe_builder.rb +41 -0
  100. data/lib/datadog/di/redactor.rb +188 -0
  101. data/lib/datadog/di/serializer.rb +193 -0
  102. data/lib/datadog/di.rb +14 -0
  103. data/lib/datadog/kit/appsec/events.rb +2 -4
  104. data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -0
  105. data/lib/datadog/opentelemetry/sdk/span_processor.rb +10 -0
  106. data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
  107. data/lib/datadog/profiling/collectors/code_provenance.rb +7 -7
  108. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +28 -26
  109. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
  110. data/lib/datadog/profiling/collectors/info.rb +15 -6
  111. data/lib/datadog/profiling/collectors/thread_context.rb +30 -2
  112. data/lib/datadog/profiling/component.rb +89 -95
  113. data/lib/datadog/profiling/exporter.rb +3 -3
  114. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
  115. data/lib/datadog/profiling/ext.rb +21 -21
  116. data/lib/datadog/profiling/flush.rb +1 -1
  117. data/lib/datadog/profiling/http_transport.rb +14 -7
  118. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  119. data/lib/datadog/profiling/preload.rb +1 -1
  120. data/lib/datadog/profiling/profiler.rb +5 -8
  121. data/lib/datadog/profiling/scheduler.rb +33 -25
  122. data/lib/datadog/profiling/stack_recorder.rb +3 -0
  123. data/lib/datadog/profiling/tag_builder.rb +2 -2
  124. data/lib/datadog/profiling/tasks/exec.rb +5 -5
  125. data/lib/datadog/profiling/tasks/setup.rb +16 -35
  126. data/lib/datadog/profiling.rb +4 -5
  127. data/lib/datadog/single_step_instrument.rb +12 -0
  128. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -12
  129. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -0
  130. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +78 -0
  131. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +33 -0
  132. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +2 -0
  133. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +4 -0
  134. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +3 -1
  135. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +4 -1
  136. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +5 -1
  137. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +5 -0
  138. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
  139. data/lib/datadog/tracing/contrib/ext.rb +14 -0
  140. data/lib/datadog/tracing/contrib/faraday/middleware.rb +9 -0
  141. data/lib/datadog/tracing/contrib/grape/endpoint.rb +19 -0
  142. data/lib/datadog/tracing/contrib/graphql/patcher.rb +9 -12
  143. data/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +3 -3
  144. data/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +3 -3
  145. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +14 -10
  146. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +10 -4
  147. data/lib/datadog/tracing/contrib/http/instrumentation.rb +18 -15
  148. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -5
  149. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +1 -14
  150. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +5 -0
  151. data/lib/datadog/tracing/contrib/httprb/patcher.rb +1 -14
  152. data/lib/datadog/tracing/contrib/lograge/patcher.rb +15 -0
  153. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
  154. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  155. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
  156. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +13 -6
  157. data/lib/datadog/tracing/contrib/patcher.rb +2 -1
  158. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  159. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
  160. data/lib/datadog/tracing/contrib/presto/patcher.rb +1 -13
  161. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
  162. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  163. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
  164. data/lib/datadog/tracing/contrib/rack/middlewares.rb +27 -0
  165. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -0
  166. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +4 -0
  167. data/lib/datadog/tracing/contrib/stripe/request.rb +3 -2
  168. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
  169. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
  170. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
  171. data/lib/datadog/tracing/distributed/propagation.rb +7 -0
  172. data/lib/datadog/tracing/metadata/errors.rb +9 -1
  173. data/lib/datadog/tracing/metadata/ext.rb +6 -0
  174. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  175. data/lib/datadog/tracing/remote.rb +5 -2
  176. data/lib/datadog/tracing/sampling/matcher.rb +6 -1
  177. data/lib/datadog/tracing/sampling/rate_sampler.rb +1 -1
  178. data/lib/datadog/tracing/sampling/rule.rb +2 -0
  179. data/lib/datadog/tracing/sampling/rule_sampler.rb +9 -5
  180. data/lib/datadog/tracing/sampling/span/ext.rb +1 -1
  181. data/lib/datadog/tracing/sampling/span/rule.rb +2 -2
  182. data/lib/datadog/tracing/span.rb +9 -2
  183. data/lib/datadog/tracing/span_event.rb +41 -0
  184. data/lib/datadog/tracing/span_operation.rb +6 -2
  185. data/lib/datadog/tracing/trace_operation.rb +26 -2
  186. data/lib/datadog/tracing/tracer.rb +14 -12
  187. data/lib/datadog/tracing/transport/http/client.rb +1 -0
  188. data/lib/datadog/tracing/transport/io/client.rb +1 -0
  189. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
  190. data/lib/datadog/tracing/workers/trace_writer.rb +1 -1
  191. data/lib/datadog/tracing/workers.rb +1 -1
  192. data/lib/datadog/version.rb +1 -1
  193. metadata +46 -11
  194. data/lib/datadog/profiling/crashtracker.rb +0 -91
  195. data/lib/datadog/profiling/ext/forking.rb +0 -98
  196. data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
@@ -3,13 +3,22 @@
3
3
  #include <ruby.h>
4
4
  #include <stdbool.h>
5
5
 
6
+ #include "gvl_profiling_helper.h"
7
+
6
8
  void thread_context_collector_sample(
7
9
  VALUE self_instance,
8
10
  long current_monotonic_wall_time_ns,
9
11
  VALUE profiler_overhead_stack_thread
10
12
  );
11
13
  void thread_context_collector_sample_allocation(VALUE self_instance, unsigned int sample_weight, VALUE new_object);
14
+ void thread_context_collector_sample_skipped_allocation_samples(VALUE self_instance, unsigned int skipped_samples);
12
15
  VALUE thread_context_collector_sample_after_gc(VALUE self_instance);
13
16
  void thread_context_collector_on_gc_start(VALUE self_instance);
14
- bool thread_context_collector_on_gc_finish(VALUE self_instance);
17
+ __attribute__((warn_unused_result)) bool thread_context_collector_on_gc_finish(VALUE self_instance);
15
18
  VALUE enforce_thread_context_collector_instance(VALUE object);
19
+
20
+ #ifndef NO_GVL_INSTRUMENTATION
21
+ void thread_context_collector_on_gvl_waiting(gvl_profiling_thread thread);
22
+ __attribute__((warn_unused_result)) bool thread_context_collector_on_gvl_running(gvl_profiling_thread thread);
23
+ VALUE thread_context_collector_sample_after_gvl_running(VALUE self_instance);
24
+ #endif
@@ -0,0 +1,83 @@
1
+ #include "datadog_ruby_common.h"
2
+
3
+ // IMPORTANT: Currently this file is copy-pasted between extensions. Make sure to update all versions when doing any change!
4
+
5
+ void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name) {
6
+ rb_exc_raise(
7
+ rb_exc_new_str(
8
+ rb_eTypeError,
9
+ rb_sprintf("wrong argument %"PRIsVALUE" for '%s' (expected a %s) at %s:%d:in `%s'",
10
+ rb_inspect(value),
11
+ value_name,
12
+ type_name,
13
+ file,
14
+ line,
15
+ function_name
16
+ )
17
+ )
18
+ );
19
+ }
20
+
21
+ VALUE datadog_gem_version(void) {
22
+ VALUE ddtrace_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
23
+ ENFORCE_TYPE(ddtrace_module, T_MODULE);
24
+ VALUE version_module = rb_const_get(ddtrace_module, rb_intern("VERSION"));
25
+ ENFORCE_TYPE(version_module, T_MODULE);
26
+ VALUE version_string = rb_const_get(version_module, rb_intern("STRING"));
27
+ ENFORCE_TYPE(version_string, T_STRING);
28
+ return version_string;
29
+ }
30
+
31
+ static VALUE log_failure_to_process_tag(VALUE err_details) {
32
+ VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
33
+ VALUE logger = rb_funcall(datadog_module, rb_intern("logger"), 0);
34
+
35
+ return rb_funcall(logger, rb_intern("warn"), 1, rb_sprintf("Failed to convert tag: %"PRIsVALUE, err_details));
36
+ }
37
+
38
+ __attribute__((warn_unused_result))
39
+ ddog_Vec_Tag convert_tags(VALUE tags_as_array) {
40
+ ENFORCE_TYPE(tags_as_array, T_ARRAY);
41
+
42
+ long tags_count = RARRAY_LEN(tags_as_array);
43
+ ddog_Vec_Tag tags = ddog_Vec_Tag_new();
44
+
45
+ for (long i = 0; i < tags_count; i++) {
46
+ VALUE name_value_pair = rb_ary_entry(tags_as_array, i);
47
+
48
+ if (!RB_TYPE_P(name_value_pair, T_ARRAY)) {
49
+ ddog_Vec_Tag_drop(tags);
50
+ ENFORCE_TYPE(name_value_pair, T_ARRAY);
51
+ }
52
+
53
+ // Note: We can index the array without checking its size first because rb_ary_entry returns Qnil if out of bounds
54
+ VALUE tag_name = rb_ary_entry(name_value_pair, 0);
55
+ VALUE tag_value = rb_ary_entry(name_value_pair, 1);
56
+
57
+ if (!(RB_TYPE_P(tag_name, T_STRING) && RB_TYPE_P(tag_value, T_STRING))) {
58
+ ddog_Vec_Tag_drop(tags);
59
+ ENFORCE_TYPE(tag_name, T_STRING);
60
+ ENFORCE_TYPE(tag_value, T_STRING);
61
+ }
62
+
63
+ ddog_Vec_Tag_PushResult push_result =
64
+ ddog_Vec_Tag_push(&tags, char_slice_from_ruby_string(tag_name), char_slice_from_ruby_string(tag_value));
65
+
66
+ if (push_result.tag == DDOG_VEC_TAG_PUSH_RESULT_ERR) {
67
+ // libdatadog validates tags and may catch invalid tags that ddtrace didn't actually catch.
68
+ // We warn users about such tags, and then just ignore them.
69
+
70
+ int exception_state;
71
+ rb_protect(log_failure_to_process_tag, get_error_details_and_drop(&push_result.err), &exception_state);
72
+
73
+ // Since we are calling into Ruby code, it may raise an exception. Ensure that dynamically-allocated tags
74
+ // get cleaned before propagating the exception.
75
+ if (exception_state) {
76
+ ddog_Vec_Tag_drop(tags);
77
+ rb_jump_tag(exception_state); // "Re-raise" exception
78
+ }
79
+ }
80
+ }
81
+
82
+ return tags;
83
+ }
@@ -0,0 +1,53 @@
1
+ #pragma once
2
+
3
+ // IMPORTANT: Currently this file is copy-pasted between extensions. Make sure to update all versions when doing any change!
4
+
5
+ #include <ruby.h>
6
+ #include <datadog/profiling.h>
7
+
8
+ // Used to mark symbols to be exported to the outside of the extension.
9
+ // Consider very carefully before tagging a function with this.
10
+ #define DDTRACE_EXPORT __attribute__ ((visibility ("default")))
11
+
12
+ // Used to mark function arguments that are deliberately left unused
13
+ #ifdef __GNUC__
14
+ #define DDTRACE_UNUSED __attribute__((unused))
15
+ #else
16
+ #define DDTRACE_UNUSED
17
+ #endif
18
+
19
+ #define ADD_QUOTES_HELPER(x) #x
20
+ #define ADD_QUOTES(x) ADD_QUOTES_HELPER(x)
21
+
22
+ // Ruby has a Check_Type(value, type) that is roughly equivalent to this BUT Ruby's version is rather cryptic when it fails
23
+ // e.g. "wrong argument type nil (expected String)". This is a replacement that prints more information to help debugging.
24
+ #define ENFORCE_TYPE(value, type) \
25
+ { if (RB_UNLIKELY(!RB_TYPE_P(value, type))) raise_unexpected_type(value, ADD_QUOTES(value), ADD_QUOTES(type), __FILE__, __LINE__, __func__); }
26
+
27
+ #define ENFORCE_BOOLEAN(value) \
28
+ { if (RB_UNLIKELY(value != Qtrue && value != Qfalse)) raise_unexpected_type(value, ADD_QUOTES(value), "true or false", __FILE__, __LINE__, __func__); }
29
+
30
+ NORETURN(void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name));
31
+
32
+ // Helper to retrieve Datadog::VERSION::STRING
33
+ VALUE datadog_gem_version(void);
34
+
35
+ static inline ddog_CharSlice char_slice_from_ruby_string(VALUE string) {
36
+ ENFORCE_TYPE(string, T_STRING);
37
+ ddog_CharSlice char_slice = {.ptr = RSTRING_PTR(string), .len = RSTRING_LEN(string)};
38
+ return char_slice;
39
+ }
40
+
41
+ __attribute__((warn_unused_result))
42
+ ddog_Vec_Tag convert_tags(VALUE tags_as_array);
43
+
44
+ static inline VALUE ruby_string_from_error(const ddog_Error *error) {
45
+ ddog_CharSlice char_slice = ddog_Error_message(error);
46
+ return rb_str_new(char_slice.ptr, char_slice.len);
47
+ }
48
+
49
+ static inline VALUE get_error_details_and_drop(ddog_Error *error) {
50
+ VALUE result = ruby_string_from_error(error);
51
+ ddog_Error_drop(error);
52
+ return result;
53
+ }
@@ -1,11 +1,16 @@
1
1
  # rubocop:disable Style/StderrPuts
2
2
  # rubocop:disable Style/GlobalVars
3
3
 
4
- require_relative 'native_extension_helpers'
4
+ require_relative "native_extension_helpers"
5
+ require_relative "../libdatadog_extconf_helpers"
5
6
 
6
7
  SKIPPED_REASON_FILE = "#{__dir__}/skipped_reason.txt".freeze
7
8
  # Not a problem if the file doesn't exist or we can't delete it
8
- File.delete(SKIPPED_REASON_FILE) rescue nil
9
+ begin
10
+ File.delete(SKIPPED_REASON_FILE)
11
+ rescue
12
+ nil
13
+ end
9
14
 
10
15
  def skip_building_extension!(reason)
11
16
  fail_install_if_missing_extension =
@@ -24,13 +29,13 @@ def skip_building_extension!(reason)
24
29
  )
25
30
 
26
31
  if fail_install_if_missing_extension
27
- require 'mkmf'
32
+ require "mkmf"
28
33
  Logging.message(
29
- '[datadog] Failure cause: ' \
34
+ "[datadog] Failure cause: " \
30
35
  "#{Datadog::Profiling::NativeExtensionHelpers::Supported.render_skipped_reason_file(**reason)}\n"
31
36
  )
32
37
  else
33
- File.write('Makefile', 'all install clean: # dummy makefile that does nothing')
38
+ File.write("Makefile", "all install clean: # dummy makefile that does nothing")
34
39
  end
35
40
 
36
41
  exit
@@ -63,41 +68,31 @@ $stderr.puts(
63
68
 
64
69
  # NOTE: we MUST NOT require 'mkmf' before we check the #skip_building_extension? because the require triggers checks
65
70
  # that may fail on an environment not properly setup for building Ruby extensions.
66
- require 'mkmf'
71
+ require "mkmf"
67
72
 
68
73
  Logging.message("[datadog] Using compiler:\n")
69
- xsystem("#{CONFIG['CC']} -v")
74
+ xsystem("#{CONFIG["CC"]} -v")
70
75
  Logging.message("[datadog] End of compiler information\n")
71
76
 
72
- # mkmf on modern Rubies actually has an append_cflags that does something similar
73
- # (see https://github.com/ruby/ruby/pull/5760), but as usual we need a bit more boilerplate to deal with legacy Rubies
74
- def add_compiler_flag(flag)
75
- if try_cflags(flag)
76
- $CFLAGS << ' ' << flag
77
- else
78
- $stderr.puts("WARNING: '#{flag}' not accepted by compiler, skipping it")
79
- end
80
- end
81
-
82
77
  # Because we can't control what compiler versions our customers use, shipping with -Werror by default is a no-go.
83
78
  # But we can enable it in CI, so that we quickly spot any new warnings that just got introduced.
84
- add_compiler_flag '-Werror' if ENV['DATADOG_GEM_CI'] == 'true'
79
+ append_cflags "-Werror" if ENV["DATADOG_GEM_CI"] == "true"
85
80
 
86
81
  # Older gcc releases may not default to C99 and we need to ask for this. This is also used:
87
82
  # * by upstream Ruby -- search for gnu99 in the codebase
88
83
  # * by msgpack, another datadog gem dependency
89
84
  # (https://github.com/msgpack/msgpack-ruby/blob/18ce08f6d612fe973843c366ac9a0b74c4e50599/ext/msgpack/extconf.rb#L8)
90
- add_compiler_flag '-std=gnu99'
85
+ append_cflags "-std=gnu99"
91
86
 
92
87
  # Gets really noisy when we include the MJIT header, let's omit it (TODO: Use #pragma GCC diagnostic instead?)
93
- add_compiler_flag '-Wno-unused-function'
88
+ append_cflags "-Wno-unused-function"
94
89
 
95
90
  # Allow defining variables at any point in a function
96
- add_compiler_flag '-Wno-declaration-after-statement'
91
+ append_cflags "-Wno-declaration-after-statement"
97
92
 
98
93
  # If we forget to include a Ruby header, the function call may still appear to work, but then
99
94
  # cause a segfault later. Let's ensure that never happens.
100
- add_compiler_flag '-Werror-implicit-function-declaration'
95
+ append_cflags "-Werror-implicit-function-declaration"
101
96
 
102
97
  # The native extension is not intended to expose any symbols/functions for other native libraries to use;
103
98
  # the sole exception being `Init_datadog_profiling_native_extension` which needs to be visible for Ruby to call it when
@@ -105,22 +100,22 @@ add_compiler_flag '-Werror-implicit-function-declaration'
105
100
  #
106
101
  # By setting this compiler flag, we tell it to assume that everything is private unless explicitly stated.
107
102
  # For more details see https://gcc.gnu.org/wiki/Visibility
108
- add_compiler_flag '-fvisibility=hidden'
103
+ append_cflags "-fvisibility=hidden"
109
104
 
110
105
  # Avoid legacy C definitions
111
- add_compiler_flag '-Wold-style-definition'
106
+ append_cflags "-Wold-style-definition"
112
107
 
113
108
  # Enable all other compiler warnings
114
- add_compiler_flag '-Wall'
115
- add_compiler_flag '-Wextra'
109
+ append_cflags "-Wall"
110
+ append_cflags "-Wextra"
116
111
 
117
- if ENV['DDTRACE_DEBUG']
118
- $defs << '-DDD_DEBUG'
119
- CONFIG['optflags'] = '-O0'
120
- CONFIG['debugflags'] = '-ggdb3'
112
+ if ENV["DDTRACE_DEBUG"] == "true"
113
+ $defs << "-DDD_DEBUG"
114
+ CONFIG["optflags"] = "-O0"
115
+ CONFIG["debugflags"] = "-ggdb3"
121
116
  end
122
117
 
123
- if RUBY_PLATFORM.include?('linux')
118
+ if RUBY_PLATFORM.include?("linux")
124
119
  # Supposedly, the correct way to do this is
125
120
  # ```
126
121
  # have_library 'pthread'
@@ -128,77 +123,83 @@ if RUBY_PLATFORM.include?('linux')
128
123
  # ```
129
124
  # but it's slower to build
130
125
  # so instead we just assume that we have the function we need on Linux, and nowhere else
131
- $defs << '-DHAVE_PTHREAD_GETCPUCLOCKID'
126
+ $defs << "-DHAVE_PTHREAD_GETCPUCLOCKID"
127
+
128
+ # Not available on macOS
129
+ $defs << "-DHAVE_CLOCK_MONOTONIC_COARSE"
132
130
  end
133
131
 
134
- have_func 'malloc_stats'
132
+ have_func "malloc_stats"
135
133
 
136
134
  # On older Rubies, rb_postponed_job_preregister/rb_postponed_job_trigger did not exist
137
- $defs << '-DNO_POSTPONED_TRIGGER' if RUBY_VERSION < '3.3'
135
+ $defs << "-DNO_POSTPONED_TRIGGER" if RUBY_VERSION < "3.3"
138
136
 
139
137
  # On older Rubies, M:N threads were not available
140
- $defs << '-DNO_MN_THREADS_AVAILABLE' if RUBY_VERSION < '3.3'
138
+ $defs << "-DNO_MN_THREADS_AVAILABLE" if RUBY_VERSION < "3.3"
141
139
 
142
140
  # On older Rubies, we did not need to include the ractor header (this was built into the MJIT header)
143
- $defs << '-DNO_RACTOR_HEADER_INCLUDE' if RUBY_VERSION < '3.3'
141
+ $defs << "-DNO_RACTOR_HEADER_INCLUDE" if RUBY_VERSION < "3.3"
144
142
 
145
143
  # On older Rubies, some of the Ractor internal APIs were directly accessible
146
- $defs << '-DUSE_RACTOR_INTERNAL_APIS_DIRECTLY' if RUBY_VERSION < '3.3'
144
+ $defs << "-DUSE_RACTOR_INTERNAL_APIS_DIRECTLY" if RUBY_VERSION < "3.3"
145
+
146
+ # On older Rubies, there was no GVL instrumentation API and APIs created to support it
147
+ $defs << "-DNO_GVL_INSTRUMENTATION" if RUBY_VERSION < "3.2"
148
+
149
+ # Supporting GVL instrumentation on 3.2 needs some workarounds
150
+ $defs << "-DUSE_GVL_PROFILING_3_2_WORKAROUNDS" if RUBY_VERSION.start_with?("3.2")
147
151
 
148
152
  # On older Rubies, there was no struct rb_native_thread. See private_vm_api_acccess.c for details.
149
- $defs << '-DNO_RB_NATIVE_THREAD' if RUBY_VERSION < '3.2'
153
+ $defs << "-DNO_RB_NATIVE_THREAD" if RUBY_VERSION < "3.2"
150
154
 
151
155
  # On older Rubies, there was no struct rb_thread_sched (it was struct rb_global_vm_lock_struct)
152
- $defs << '-DNO_RB_THREAD_SCHED' if RUBY_VERSION < '3.2'
156
+ $defs << "-DNO_RB_THREAD_SCHED" if RUBY_VERSION < "3.2"
153
157
 
154
158
  # On older Rubies, the first_lineno inside a location was a VALUE and not a int (https://github.com/ruby/ruby/pull/6430)
155
- $defs << '-DNO_INT_FIRST_LINENO' if RUBY_VERSION < '3.2'
159
+ $defs << "-DNO_INT_FIRST_LINENO" if RUBY_VERSION < "3.2"
156
160
 
157
161
  # On older Rubies, "pop" was not a primitive operation
158
- $defs << '-DNO_PRIMITIVE_POP' if RUBY_VERSION < '3.2'
162
+ $defs << "-DNO_PRIMITIVE_POP" if RUBY_VERSION < "3.2"
159
163
 
160
164
  # On older Rubies, there was no tid member in the internal thread structure
161
- $defs << '-DNO_THREAD_TID' if RUBY_VERSION < '3.1'
165
+ $defs << "-DNO_THREAD_TID" if RUBY_VERSION < "3.1"
162
166
 
163
167
  # On older Rubies, there was no jit_return member on the rb_control_frame_t struct
164
- $defs << '-DNO_JIT_RETURN' if RUBY_VERSION < '3.1'
168
+ $defs << "-DNO_JIT_RETURN" if RUBY_VERSION < "3.1"
165
169
 
166
170
  # On older Rubies, rb_gc_force_recycle allowed to free objects in a way that
167
171
  # would be invisible to free tracepoints, finalizers and without cleaning
168
172
  # obj_to_id_tbl mappings.
169
- $defs << '-DHAVE_WORKING_RB_GC_FORCE_RECYCLE' if RUBY_VERSION < '3.1'
170
-
171
- # On older Rubies, we need to use a backported version of this function. See private_vm_api_access.h for details.
172
- $defs << '-DUSE_BACKPORTED_RB_PROFILE_FRAME_METHOD_NAME' if RUBY_VERSION < '3'
173
+ $defs << "-DHAVE_WORKING_RB_GC_FORCE_RECYCLE" if RUBY_VERSION < "3.1"
173
174
 
174
175
  # On older Rubies, there are no Ractors
175
- $defs << '-DNO_RACTORS' if RUBY_VERSION < '3'
176
+ $defs << "-DNO_RACTORS" if RUBY_VERSION < "3"
176
177
 
177
178
  # On older Rubies, rb_imemo_name did not exist
178
- $defs << '-DNO_IMEMO_NAME' if RUBY_VERSION < '3'
179
+ $defs << "-DNO_IMEMO_NAME" if RUBY_VERSION < "3"
179
180
 
180
181
  # On older Rubies, objects would not move
181
- $defs << '-DNO_T_MOVED' if RUBY_VERSION < '2.7'
182
+ $defs << "-DNO_T_MOVED" if RUBY_VERSION < "2.7"
182
183
 
183
184
  # On older Rubies, there was no RUBY_SEEN_OBJ_ID flag
184
- $defs << '-DNO_SEEN_OBJ_ID_FLAG' if RUBY_VERSION < '2.7'
185
+ $defs << "-DNO_SEEN_OBJ_ID_FLAG" if RUBY_VERSION < "2.7"
185
186
 
186
187
  # On older Rubies, rb_global_vm_lock_struct did not include the owner field
187
- $defs << '-DNO_GVL_OWNER' if RUBY_VERSION < '2.6'
188
+ $defs << "-DNO_GVL_OWNER" if RUBY_VERSION < "2.6"
188
189
 
189
190
  # On older Rubies, there was no thread->invoke_arg
190
- $defs << '-DNO_THREAD_INVOKE_ARG' if RUBY_VERSION < '2.6'
191
+ $defs << "-DNO_THREAD_INVOKE_ARG" if RUBY_VERSION < "2.6"
191
192
 
192
193
  # If we got here, libdatadog is available and loaded
193
- ENV['PKG_CONFIG_PATH'] = "#{ENV['PKG_CONFIG_PATH']}:#{Libdatadog.pkgconfig_folder}"
194
- Logging.message("[datadog] PKG_CONFIG_PATH set to #{ENV['PKG_CONFIG_PATH'].inspect}\n")
194
+ ENV["PKG_CONFIG_PATH"] = "#{ENV["PKG_CONFIG_PATH"]}:#{Libdatadog.pkgconfig_folder}"
195
+ Logging.message("[datadog] PKG_CONFIG_PATH set to #{ENV["PKG_CONFIG_PATH"].inspect}\n")
195
196
  $stderr.puts("Using libdatadog #{Libdatadog::VERSION} from #{Libdatadog.pkgconfig_folder}")
196
197
 
197
- unless pkg_config('datadog_profiling_with_rpath')
198
+ unless pkg_config("datadog_profiling_with_rpath")
198
199
  Logging.message("[datadog] Ruby detected the pkg-config command is #{$PKGCONFIG.inspect}\n")
199
200
 
200
201
  skip_building_extension!(
201
- if Datadog::Profiling::NativeExtensionHelpers::Supported.pkg_config_missing?
202
+ if Datadog::LibdatadogExtconfHelpers.pkg_config_missing?
202
203
  Datadog::Profiling::NativeExtensionHelpers::Supported::PKG_CONFIG_IS_MISSING
203
204
  else
204
205
  # Less specific error message
@@ -207,7 +208,7 @@ unless pkg_config('datadog_profiling_with_rpath')
207
208
  )
208
209
  end
209
210
 
210
- unless have_type('atomic_int', ['stdatomic.h'])
211
+ unless have_type("atomic_int", ["stdatomic.h"])
211
212
  skip_building_extension!(Datadog::Profiling::NativeExtensionHelpers::Supported::COMPILER_ATOMIC_MISSING)
212
213
  end
213
214
 
@@ -215,8 +216,8 @@ end
215
216
  # The extremely excessive escaping around ORIGIN below seems to be correct and was determined after a lot of
216
217
  # experimentation. We need to get these special characters across a lot of tools untouched...
217
218
  extra_relative_rpaths = [
218
- Datadog::Profiling::NativeExtensionHelpers.libdatadog_folder_relative_to_native_lib_folder,
219
- *Datadog::Profiling::NativeExtensionHelpers.libdatadog_folder_relative_to_ruby_extensions_folders,
219
+ Datadog::LibdatadogExtconfHelpers.libdatadog_folder_relative_to_native_lib_folder(current_folder: __dir__),
220
+ *Datadog::LibdatadogExtconfHelpers.libdatadog_folder_relative_to_ruby_extensions_folders,
220
221
  ]
221
222
  extra_relative_rpaths.each { |folder| $LDFLAGS += " -Wl,-rpath,$$$\\\\{ORIGIN\\}/#{folder.to_str}" }
222
223
  Logging.message("[datadog] After pkg-config $LDFLAGS were set to: #{$LDFLAGS.inspect}\n")
@@ -237,8 +238,8 @@ if Datadog::Profiling::NativeExtensionHelpers::CAN_USE_MJIT_HEADER
237
238
  # use the MJIT header.
238
239
  # Finally, the `COMMON_HEADERS` conflict with the MJIT header so we need to temporarily disable them for this check.
239
240
  original_common_headers = MakeMakefile::COMMON_HEADERS
240
- MakeMakefile::COMMON_HEADERS = ''.freeze
241
- unless have_macro('RUBY_MJIT_H', mjit_header_file_name)
241
+ MakeMakefile::COMMON_HEADERS = "".freeze
242
+ unless have_macro("RUBY_MJIT_H", mjit_header_file_name)
242
243
  skip_building_extension!(Datadog::Profiling::NativeExtensionHelpers::Supported::COMPILATION_BROKEN)
243
244
  end
244
245
  MakeMakefile::COMMON_HEADERS = original_common_headers
@@ -250,7 +251,7 @@ if Datadog::Profiling::NativeExtensionHelpers::CAN_USE_MJIT_HEADER
250
251
 
251
252
  # Warn on unused parameters to functions. Use `DDTRACE_UNUSED` to mark things as known-to-not-be-used.
252
253
  # See the comment on the same flag below for why this is done last.
253
- add_compiler_flag '-Wunused-parameter'
254
+ append_cflags "-Wunused-parameter"
254
255
 
255
256
  create_makefile EXTENSION_NAME
256
257
  else
@@ -261,23 +262,44 @@ else
261
262
 
262
263
  create_header
263
264
 
264
- require 'debase/ruby_core_source'
265
- dir_config('ruby') # allow user to pass in non-standard core include directory
265
+ require "debase/ruby_core_source"
266
+ dir_config("ruby") # allow user to pass in non-standard core include directory
267
+
268
+ # This is a workaround for a weird issue...
269
+ #
270
+ # The mkmf tool defines a `with_cppflags` helper that debase-ruby_core_source uses. This helper temporarily
271
+ # replaces `$CPPFLAGS` (aka the C pre-processor [not c++!] flags) with a different set when doing something.
272
+ #
273
+ # The debase-ruby_core_source gem uses `with_cppflags` during makefile generation to inject extra headers into the
274
+ # path. But because `with_cppflags` replaces `$CPPFLAGS`, well, the default `$CPPFLAGS` are not included in the
275
+ # makefile.
276
+ #
277
+ # This is a problem because the default `$CPPFLAGS` carries configuration that was set when Ruby was being built.
278
+ # Thus, if we ignore it, we don't compile the profiler with the exact same configuration as Ruby.
279
+ # In practice, this can generate crashes and weird bugs if the Ruby configuration is tweaked in a manner that
280
+ # changes some of the internal structures that the profiler relies on. Concretely, setting for instance
281
+ # `VM_CHECK_MODE=1` when building Ruby will trigger this issue (because somethings in structures the profiler reads
282
+ # are ifdef'd out using this setting).
283
+ #
284
+ # To workaround this issue, we override `with_cppflags` for debase-ruby_core_source to still include `$CPPFLAGS`.
285
+ Debase::RubyCoreSource.define_singleton_method(:with_cppflags) do |newflags, &block|
286
+ super("#{newflags} #{$CPPFLAGS}", &block)
287
+ end
266
288
 
267
289
  Debase::RubyCoreSource
268
290
  .create_makefile_with_core(
269
291
  proc do
270
292
  headers_available =
271
- have_header('vm_core.h') &&
272
- have_header('iseq.h') &&
273
- (RUBY_VERSION < '3.3' || have_header('ractor_core.h'))
293
+ have_header("vm_core.h") &&
294
+ have_header("iseq.h") &&
295
+ (RUBY_VERSION < "3.3" || have_header("ractor_core.h"))
274
296
 
275
297
  if headers_available
276
298
  # Warn on unused parameters to functions. Use `DDTRACE_UNUSED` to mark things as known-to-not-be-used.
277
299
  # This is added as late as possible because in some Rubies we support (e.g. 3.3), adding this flag before
278
300
  # checking if internal VM headers are available causes those checks to fail because of this warning (and not
279
301
  # because the headers are not available.)
280
- add_compiler_flag '-Wunused-parameter'
302
+ append_cflags "-Wunused-parameter"
281
303
  end
282
304
 
283
305
  headers_available
@@ -0,0 +1,50 @@
1
+ #include <ruby.h>
2
+ #include <ruby/thread.h>
3
+ #include "gvl_profiling_helper.h"
4
+
5
+ #if !defined(NO_GVL_INSTRUMENTATION) && !defined(USE_GVL_PROFILING_3_2_WORKAROUNDS) // Ruby 3.3+
6
+ rb_internal_thread_specific_key_t gvl_waiting_tls_key;
7
+
8
+ void gvl_profiling_init(void) {
9
+ gvl_waiting_tls_key = rb_internal_thread_specific_key_create();
10
+ }
11
+
12
+ #endif
13
+
14
+ #ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS // Ruby 3.2
15
+ __thread gvl_profiling_thread gvl_waiting_tls;
16
+ static bool gvl_profiling_state_thread_tracking_workaround_installed = false;
17
+
18
+ static void on_thread_start(
19
+ DDTRACE_UNUSED rb_event_flag_t _unused1,
20
+ DDTRACE_UNUSED const rb_internal_thread_event_data_t *_unused2,
21
+ DDTRACE_UNUSED void *_unused3
22
+ ) {
23
+ gvl_waiting_tls = (gvl_profiling_thread) {.thread = NULL};
24
+ }
25
+
26
+ // Hack: We're using the gvl_waiting_tls native thread-local to store per-thread information. Unfortunately, Ruby puts a big hole
27
+ // in our plan because it reuses native threads -- specifically, in Ruby 3.2, native threads are still 1:1 to Ruby
28
+ // threads (M:N wasn't a thing yet) BUT once a Ruby thread dies, the VM will keep the native thread around for a
29
+ // bit, and if another Ruby thread starts right after, Ruby will reuse the native thread, rather than create a new one.
30
+ //
31
+ // This will mean that the new Ruby thread will still have the same native thread-local data that we set on the
32
+ // old thread. For the purposes of our tracking, where we're keeping a pointer to the current thread object in
33
+ // thread-local storage **this is disastrous** since it means we'll be pointing at the wrong thread (and its
34
+ // memory may have been freed or reused since!)
35
+ //
36
+ // To work around this issue, once GVL profiling is enabled, we install an event hook on thread start
37
+ // events that clears the thread-local data. This guarantees that there will be no stale data -- any existing
38
+ // data will be cleared at thread start.
39
+ //
40
+ // Note that once installed, this event hook becomes permanent -- stopping the profiler does not stop this event
41
+ // hook, unlike all others. This is because we can't afford to miss any thread start events while the
42
+ // profiler is stopped (e.g. during reconfiguration) as that would mean stale data once the profiler starts again.
43
+ void gvl_profiling_state_thread_tracking_workaround(void) {
44
+ if (gvl_profiling_state_thread_tracking_workaround_installed) return;
45
+
46
+ rb_internal_thread_add_event_hook(on_thread_start, RUBY_INTERNAL_THREAD_EVENT_STARTED, NULL);
47
+
48
+ gvl_profiling_state_thread_tracking_workaround_installed = true;
49
+ }
50
+ #endif
@@ -0,0 +1,75 @@
1
+ #pragma once
2
+
3
+ // This helper is used by the Datadog::Profiling::Collectors::ThreadContext to store data used when profiling the GVL.
4
+ // It's tested through that class' interfaces.
5
+ // ---
6
+
7
+ #include "extconf.h"
8
+
9
+ #if !defined(NO_GVL_INSTRUMENTATION) && !defined(USE_GVL_PROFILING_3_2_WORKAROUNDS) // Ruby 3.3+
10
+ #include <ruby.h>
11
+ #include <ruby/thread.h>
12
+ #include "datadog_ruby_common.h"
13
+
14
+ typedef struct { VALUE thread; } gvl_profiling_thread;
15
+ extern rb_internal_thread_specific_key_t gvl_waiting_tls_key;
16
+
17
+ void gvl_profiling_init(void);
18
+
19
+ static inline gvl_profiling_thread thread_from_thread_object(VALUE thread) {
20
+ return (gvl_profiling_thread) {.thread = thread};
21
+ }
22
+
23
+ static inline gvl_profiling_thread thread_from_event(const rb_internal_thread_event_data_t *event_data) {
24
+ return thread_from_thread_object(event_data->thread);
25
+ }
26
+
27
+ static inline intptr_t gvl_profiling_state_get(gvl_profiling_thread thread) {
28
+ return (intptr_t) rb_internal_thread_specific_get(thread.thread, gvl_waiting_tls_key);
29
+ }
30
+
31
+ static inline void gvl_profiling_state_set(gvl_profiling_thread thread, intptr_t value) {
32
+ rb_internal_thread_specific_set(thread.thread, gvl_waiting_tls_key, (void *) value);
33
+ }
34
+ #endif
35
+
36
+ #ifdef USE_GVL_PROFILING_3_2_WORKAROUNDS // Ruby 3.2
37
+ typedef struct { void *thread; } gvl_profiling_thread;
38
+ extern __thread gvl_profiling_thread gvl_waiting_tls;
39
+
40
+ static inline void gvl_profiling_init(void) { }
41
+
42
+ // This header gets included in private_vm_access.c which can't include datadog_ruby_common.h so we replicate this
43
+ // helper here
44
+ #ifdef __GNUC__
45
+ #define DDTRACE_UNUSED __attribute__((unused))
46
+ #else
47
+ #define DDTRACE_UNUSED
48
+ #endif
49
+
50
+ // NOTE: This is a hack that relies on the knowledge that on Ruby 3.2 the
51
+ // RUBY_INTERNAL_THREAD_EVENT_READY and RUBY_INTERNAL_THREAD_EVENT_RESUMED events always get called on the thread they
52
+ // are about. Thus, we can use our thread local storage hack to get this data, even though the event doesn't include it.
53
+ static inline gvl_profiling_thread thread_from_event(DDTRACE_UNUSED const void *event_data) {
54
+ return gvl_waiting_tls;
55
+ }
56
+
57
+ void gvl_profiling_state_thread_tracking_workaround(void);
58
+ gvl_profiling_thread gvl_profiling_state_maybe_initialize(void);
59
+
60
+ // Implementing these on Ruby 3.2 requires access to private VM things, so the following methods are
61
+ // implemented in `private_vm_api_access.c`
62
+ gvl_profiling_thread thread_from_thread_object(VALUE thread);
63
+ intptr_t gvl_profiling_state_get(gvl_profiling_thread thread);
64
+ void gvl_profiling_state_set(gvl_profiling_thread thread, intptr_t value);
65
+ #endif
66
+
67
+ #ifndef NO_GVL_INSTRUMENTATION // For all Rubies supporting GVL profiling (3.2+)
68
+ static inline intptr_t gvl_profiling_state_thread_object_get(VALUE thread) {
69
+ return gvl_profiling_state_get(thread_from_thread_object(thread));
70
+ }
71
+
72
+ static inline void gvl_profiling_state_thread_object_set(VALUE thread, intptr_t value) {
73
+ gvl_profiling_state_set(thread_from_thread_object(thread), value);
74
+ }
75
+ #endif