ddtrace 1.7.0 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +100 -1
  3. data/README.md +2 -2
  4. data/ext/ddtrace_profiling_loader/extconf.rb +4 -1
  5. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +1 -1
  6. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +3 -2
  7. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +24 -50
  8. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.h +1 -1
  9. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +284 -74
  10. data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.c +142 -0
  11. data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.h +14 -0
  12. data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.c +241 -0
  13. data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.h +3 -0
  14. data/ext/ddtrace_profiling_native_extension/collectors_stack.c +32 -32
  15. data/ext/ddtrace_profiling_native_extension/collectors_stack.h +2 -2
  16. data/ext/ddtrace_profiling_native_extension/extconf.rb +21 -7
  17. data/ext/ddtrace_profiling_native_extension/helpers.h +5 -0
  18. data/ext/ddtrace_profiling_native_extension/http_transport.c +50 -49
  19. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +5 -1
  20. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +42 -12
  21. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +116 -22
  22. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +9 -0
  23. data/ext/ddtrace_profiling_native_extension/profiling.c +205 -0
  24. data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +86 -0
  25. data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +28 -6
  26. data/ext/ddtrace_profiling_native_extension/setup_signal_handler.c +23 -4
  27. data/ext/ddtrace_profiling_native_extension/setup_signal_handler.h +4 -0
  28. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +47 -50
  29. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +4 -4
  30. data/ext/ddtrace_profiling_native_extension/time_helpers.c +17 -0
  31. data/ext/ddtrace_profiling_native_extension/time_helpers.h +10 -0
  32. data/lib/datadog/appsec/assets/waf_rules/recommended.json +75 -8
  33. data/lib/datadog/appsec/assets/waf_rules/risky.json +1 -1
  34. data/lib/datadog/appsec/assets/waf_rules/strict.json +1 -1
  35. data/lib/datadog/appsec/assets.rb +1 -1
  36. data/lib/datadog/appsec/configuration/settings.rb +35 -22
  37. data/lib/datadog/appsec/configuration.rb +4 -2
  38. data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
  39. data/lib/datadog/appsec/contrib/configuration/settings.rb +1 -1
  40. data/lib/datadog/appsec/contrib/integration.rb +1 -1
  41. data/lib/datadog/appsec/contrib/patcher.rb +1 -1
  42. data/lib/datadog/appsec/contrib/rack/configuration/settings.rb +1 -1
  43. data/lib/datadog/appsec/contrib/rack/ext.rb +1 -1
  44. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +1 -1
  45. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +1 -1
  46. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +1 -1
  47. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +1 -1
  48. data/lib/datadog/appsec/contrib/rack/request.rb +1 -1
  49. data/lib/datadog/appsec/contrib/rack/response.rb +1 -1
  50. data/lib/datadog/appsec/contrib/rails/configuration/settings.rb +1 -1
  51. data/lib/datadog/appsec/contrib/rails/ext.rb +1 -1
  52. data/lib/datadog/appsec/contrib/rails/framework.rb +1 -1
  53. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +1 -1
  54. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +1 -1
  55. data/lib/datadog/appsec/contrib/rails/request.rb +1 -1
  56. data/lib/datadog/appsec/contrib/rails/request_middleware.rb +1 -1
  57. data/lib/datadog/appsec/contrib/sinatra/configuration/settings.rb +1 -1
  58. data/lib/datadog/appsec/contrib/sinatra/ext.rb +1 -1
  59. data/lib/datadog/appsec/contrib/sinatra/framework.rb +1 -1
  60. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +1 -1
  61. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +1 -1
  62. data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +1 -1
  63. data/lib/datadog/appsec/event.rb +1 -1
  64. data/lib/datadog/appsec/extensions.rb +36 -26
  65. data/lib/datadog/appsec/instrumentation/gateway.rb +3 -3
  66. data/lib/datadog/appsec/processor.rb +15 -19
  67. data/lib/datadog/appsec/rate_limiter.rb +1 -1
  68. data/lib/datadog/appsec/reactive/address_hash.rb +1 -1
  69. data/lib/datadog/appsec/reactive/engine.rb +1 -1
  70. data/lib/datadog/appsec/reactive/operation.rb +2 -2
  71. data/lib/datadog/appsec/reactive/subscriber.rb +1 -1
  72. data/lib/datadog/appsec/response.rb +18 -9
  73. data/lib/datadog/appsec/utils/http/media_range.rb +201 -0
  74. data/lib/datadog/appsec/utils/http/media_type.rb +87 -0
  75. data/lib/datadog/appsec/utils/http.rb +9 -0
  76. data/lib/datadog/appsec/utils.rb +7 -0
  77. data/lib/datadog/appsec.rb +1 -1
  78. data/lib/datadog/ci/ext/environment.rb +57 -13
  79. data/lib/datadog/core/configuration/agent_settings_resolver.rb +2 -2
  80. data/lib/datadog/core/configuration/base.rb +3 -0
  81. data/lib/datadog/core/configuration/components.rb +27 -6
  82. data/lib/datadog/core/configuration/ext.rb +26 -0
  83. data/lib/datadog/core/configuration/option_definition.rb +11 -2
  84. data/lib/datadog/core/configuration/settings.rb +16 -341
  85. data/lib/datadog/core/diagnostics/environment_logger.rb +4 -3
  86. data/lib/datadog/core/diagnostics/health.rb +4 -22
  87. data/lib/datadog/core/environment/variable_helpers.rb +58 -10
  88. data/lib/datadog/core/metrics/client.rb +3 -2
  89. data/lib/datadog/core/metrics/ext.rb +0 -2
  90. data/lib/datadog/core/telemetry/collector.rb +1 -0
  91. data/lib/datadog/core/utils.rb +0 -21
  92. data/lib/datadog/core.rb +21 -1
  93. data/lib/datadog/kit/appsec/events.rb +75 -0
  94. data/lib/datadog/kit/enable_core_dumps.rb +1 -0
  95. data/lib/datadog/kit/identity.rb +8 -7
  96. data/lib/datadog/opentelemetry/api/context.rb +187 -0
  97. data/lib/datadog/opentelemetry/api/trace/span.rb +15 -0
  98. data/lib/datadog/opentelemetry/sdk/configurator.rb +38 -0
  99. data/lib/datadog/opentelemetry/sdk/id_generator.rb +27 -0
  100. data/lib/datadog/opentelemetry/sdk/propagator.rb +91 -0
  101. data/lib/datadog/opentelemetry/sdk/span_processor.rb +92 -0
  102. data/lib/datadog/opentelemetry.rb +48 -0
  103. data/lib/datadog/opentracer/distributed_headers.rb +2 -2
  104. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +16 -5
  105. data/lib/datadog/profiling/collectors/dynamic_sampling_rate.rb +14 -0
  106. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +68 -0
  107. data/lib/datadog/profiling/stack_recorder.rb +14 -0
  108. data/lib/datadog/profiling.rb +2 -0
  109. data/lib/datadog/tracing/configuration/ext.rb +33 -4
  110. data/lib/datadog/tracing/configuration/settings.rb +433 -0
  111. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +4 -1
  112. data/lib/datadog/tracing/contrib/aws/ext.rb +1 -0
  113. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +4 -1
  114. data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
  115. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +5 -1
  116. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +1 -0
  117. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +6 -1
  118. data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
  119. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -1
  120. data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
  121. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +5 -1
  122. data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
  123. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +6 -1
  124. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +9 -4
  125. data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
  126. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -1
  127. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +10 -3
  128. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +9 -4
  129. data/lib/datadog/tracing/contrib/http/ext.rb +2 -0
  130. data/lib/datadog/tracing/contrib/http/instrumentation.rb +3 -6
  131. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -1
  132. data/lib/datadog/tracing/contrib/httpclient/ext.rb +2 -0
  133. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +3 -4
  134. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -1
  135. data/lib/datadog/tracing/contrib/httprb/ext.rb +2 -0
  136. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +3 -4
  137. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +5 -1
  138. data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
  139. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +4 -1
  140. data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
  141. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +2 -2
  142. data/lib/datadog/tracing/contrib/patcher.rb +3 -2
  143. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +4 -1
  144. data/lib/datadog/tracing/contrib/pg/ext.rb +1 -0
  145. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +56 -33
  146. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +4 -1
  147. data/lib/datadog/tracing/contrib/presto/ext.rb +1 -0
  148. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +1 -0
  149. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +10 -12
  150. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +4 -1
  151. data/lib/datadog/tracing/contrib/redis/ext.rb +1 -0
  152. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +30 -23
  153. data/lib/datadog/tracing/contrib/redis/integration.rb +34 -2
  154. data/lib/datadog/tracing/contrib/redis/patcher.rb +18 -14
  155. data/lib/datadog/tracing/contrib/redis/quantize.rb +12 -9
  156. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -6
  157. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +72 -0
  158. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +6 -1
  159. data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
  160. data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +33 -0
  161. data/lib/datadog/tracing/contrib/stripe/ext.rb +26 -0
  162. data/lib/datadog/tracing/contrib/stripe/integration.rb +43 -0
  163. data/lib/datadog/tracing/contrib/stripe/patcher.rb +29 -0
  164. data/lib/datadog/tracing/contrib/stripe/request.rb +67 -0
  165. data/lib/datadog/tracing/contrib.rb +1 -0
  166. data/lib/datadog/{core → tracing}/diagnostics/ext.rb +1 -6
  167. data/lib/datadog/tracing/diagnostics/health.rb +40 -0
  168. data/lib/datadog/tracing/distributed/{b3.rb → b3_multi.rb} +2 -2
  169. data/lib/datadog/tracing/distributed/helpers.rb +2 -1
  170. data/lib/datadog/tracing/distributed/none.rb +19 -0
  171. data/lib/datadog/tracing/distributed/trace_context.rb +378 -0
  172. data/lib/datadog/tracing/metadata/ext.rb +1 -1
  173. data/lib/datadog/tracing/metadata/tagging.rb +6 -0
  174. data/lib/datadog/tracing/sampling/priority_sampler.rb +11 -0
  175. data/lib/datadog/tracing/sampling/rate_sampler.rb +3 -3
  176. data/lib/datadog/tracing/span.rb +3 -19
  177. data/lib/datadog/tracing/span_operation.rb +5 -4
  178. data/lib/datadog/tracing/trace_digest.rb +85 -2
  179. data/lib/datadog/tracing/trace_operation.rb +13 -4
  180. data/lib/datadog/tracing/utils.rb +50 -0
  181. data/lib/ddtrace/version.rb +1 -1
  182. metadata +41 -9
@@ -10,3 +10,8 @@
10
10
  #else
11
11
  #define DDTRACE_UNUSED
12
12
  #endif
13
+
14
+ // @ivoanjo: After trying to read through https://stackoverflow.com/questions/3437404/min-and-max-in-c I decided I
15
+ // don't like C and I just implemented this as a function.
16
+ inline static uint64_t uint64_max_of(uint64_t a, uint64_t b) { return a > b ? a : b; }
17
+ inline static uint64_t uint64_min_of(uint64_t a, uint64_t b) { return a > b ? b : a; }
@@ -20,20 +20,20 @@ static VALUE http_transport_class = Qnil;
20
20
  static VALUE library_version_string = Qnil;
21
21
 
22
22
  struct call_exporter_without_gvl_arguments {
23
- ddog_ProfileExporter *exporter;
24
- ddog_Request *request;
23
+ ddog_prof_Exporter *exporter;
24
+ ddog_prof_Exporter_Request *request;
25
25
  ddog_CancellationToken *cancel_token;
26
- ddog_SendResult result;
26
+ ddog_prof_Exporter_SendResult result;
27
27
  bool send_ran;
28
28
  };
29
29
 
30
30
  inline static ddog_ByteSlice byte_slice_from_ruby_string(VALUE string);
31
31
  static VALUE _native_validate_exporter(VALUE self, VALUE exporter_configuration);
32
- static ddog_NewProfileExporterResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array);
33
- static VALUE handle_exporter_failure(ddog_NewProfileExporterResult exporter_result);
32
+ static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array);
33
+ static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_result);
34
34
  static ddog_Endpoint endpoint_from(VALUE exporter_configuration);
35
- static ddog_Vec_tag convert_tags(VALUE tags_as_array);
36
- static void safely_log_failure_to_process_tag(ddog_Vec_tag tags, VALUE err_details);
35
+ static ddog_Vec_Tag convert_tags(VALUE tags_as_array);
36
+ static void safely_log_failure_to_process_tag(ddog_Vec_Tag tags, VALUE err_details);
37
37
  static VALUE _native_do_export(
38
38
  VALUE self,
39
39
  VALUE exporter_configuration,
@@ -76,46 +76,46 @@ inline static ddog_ByteSlice byte_slice_from_ruby_string(VALUE string) {
76
76
 
77
77
  static VALUE _native_validate_exporter(DDTRACE_UNUSED VALUE _self, VALUE exporter_configuration) {
78
78
  ENFORCE_TYPE(exporter_configuration, T_ARRAY);
79
- ddog_NewProfileExporterResult exporter_result = create_exporter(exporter_configuration, rb_ary_new());
79
+ ddog_prof_Exporter_NewResult exporter_result = create_exporter(exporter_configuration, rb_ary_new());
80
80
 
81
81
  VALUE failure_tuple = handle_exporter_failure(exporter_result);
82
82
  if (!NIL_P(failure_tuple)) return failure_tuple;
83
83
 
84
84
  // We don't actually need the exporter for now -- we just wanted to validate that we could create it with the
85
85
  // settings we were given
86
- ddog_NewProfileExporterResult_drop(exporter_result);
86
+ ddog_prof_Exporter_NewResult_drop(exporter_result);
87
87
 
88
88
  return rb_ary_new_from_args(2, ok_symbol, Qnil);
89
89
  }
90
90
 
91
- static ddog_NewProfileExporterResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array) {
91
+ static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array) {
92
92
  ENFORCE_TYPE(exporter_configuration, T_ARRAY);
93
93
  ENFORCE_TYPE(tags_as_array, T_ARRAY);
94
94
 
95
- // This needs to be called BEFORE convert_tags since it can raise an exception and thus cause the ddog_Vec_tag
95
+ // This needs to be called BEFORE convert_tags since it can raise an exception and thus cause the ddog_Vec_Tag
96
96
  // to be leaked.
97
97
  ddog_Endpoint endpoint = endpoint_from(exporter_configuration);
98
98
 
99
- ddog_Vec_tag tags = convert_tags(tags_as_array);
99
+ ddog_Vec_Tag tags = convert_tags(tags_as_array);
100
100
 
101
101
  ddog_CharSlice library_name = DDOG_CHARSLICE_C("dd-trace-rb");
102
102
  ddog_CharSlice library_version = char_slice_from_ruby_string(library_version_string);
103
103
  ddog_CharSlice profiling_family = DDOG_CHARSLICE_C("ruby");
104
104
 
105
- ddog_NewProfileExporterResult exporter_result =
106
- ddog_ProfileExporter_new(library_name, library_version, profiling_family, &tags, endpoint);
105
+ ddog_prof_Exporter_NewResult exporter_result =
106
+ ddog_prof_Exporter_new(library_name, library_version, profiling_family, &tags, endpoint);
107
107
 
108
- ddog_Vec_tag_drop(tags);
108
+ ddog_Vec_Tag_drop(tags);
109
109
 
110
110
  return exporter_result;
111
111
  }
112
112
 
113
- static VALUE handle_exporter_failure(ddog_NewProfileExporterResult exporter_result) {
114
- if (exporter_result.tag == DDOG_NEW_PROFILE_EXPORTER_RESULT_OK) return Qnil;
113
+ static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_result) {
114
+ if (exporter_result.tag == DDOG_PROF_EXPORTER_NEW_RESULT_OK) return Qnil;
115
115
 
116
- VALUE err_details = ruby_string_from_vec_u8(exporter_result.err);
116
+ VALUE err_details = ruby_string_from_prof_vec_u8(exporter_result.err);
117
117
 
118
- ddog_NewProfileExporterResult_drop(exporter_result);
118
+ ddog_prof_Exporter_NewResult_drop(exporter_result);
119
119
 
120
120
  return rb_ary_new_from_args(2, error_symbol, err_details);
121
121
  }
@@ -145,17 +145,17 @@ static ddog_Endpoint endpoint_from(VALUE exporter_configuration) {
145
145
  }
146
146
 
147
147
  __attribute__((warn_unused_result))
148
- static ddog_Vec_tag convert_tags(VALUE tags_as_array) {
148
+ static ddog_Vec_Tag convert_tags(VALUE tags_as_array) {
149
149
  ENFORCE_TYPE(tags_as_array, T_ARRAY);
150
150
 
151
151
  long tags_count = RARRAY_LEN(tags_as_array);
152
- ddog_Vec_tag tags = ddog_Vec_tag_new();
152
+ ddog_Vec_Tag tags = ddog_Vec_Tag_new();
153
153
 
154
154
  for (long i = 0; i < tags_count; i++) {
155
155
  VALUE name_value_pair = rb_ary_entry(tags_as_array, i);
156
156
 
157
157
  if (!RB_TYPE_P(name_value_pair, T_ARRAY)) {
158
- ddog_Vec_tag_drop(tags);
158
+ ddog_Vec_Tag_drop(tags);
159
159
  ENFORCE_TYPE(name_value_pair, T_ARRAY);
160
160
  }
161
161
 
@@ -164,23 +164,23 @@ static ddog_Vec_tag convert_tags(VALUE tags_as_array) {
164
164
  VALUE tag_value = rb_ary_entry(name_value_pair, 1);
165
165
 
166
166
  if (!(RB_TYPE_P(tag_name, T_STRING) && RB_TYPE_P(tag_value, T_STRING))) {
167
- ddog_Vec_tag_drop(tags);
167
+ ddog_Vec_Tag_drop(tags);
168
168
  ENFORCE_TYPE(tag_name, T_STRING);
169
169
  ENFORCE_TYPE(tag_value, T_STRING);
170
170
  }
171
171
 
172
- ddog_PushTagResult push_result =
173
- ddog_Vec_tag_push(&tags, char_slice_from_ruby_string(tag_name), char_slice_from_ruby_string(tag_value));
172
+ ddog_Vec_Tag_PushResult push_result =
173
+ ddog_Vec_Tag_push(&tags, char_slice_from_ruby_string(tag_name), char_slice_from_ruby_string(tag_value));
174
174
 
175
- if (push_result.tag == DDOG_PUSH_TAG_RESULT_ERR) {
175
+ if (push_result.tag == DDOG_VEC_TAG_PUSH_RESULT_ERR) {
176
176
  VALUE err_details = ruby_string_from_vec_u8(push_result.err);
177
- ddog_PushTagResult_drop(push_result);
177
+ ddog_Vec_Tag_PushResult_drop(push_result);
178
178
 
179
179
  // libdatadog validates tags and may catch invalid tags that ddtrace didn't actually catch.
180
180
  // We warn users about such tags, and then just ignore them.
181
181
  safely_log_failure_to_process_tag(tags, err_details);
182
182
  } else {
183
- ddog_PushTagResult_drop(push_result);
183
+ ddog_Vec_Tag_PushResult_drop(push_result);
184
184
  }
185
185
  }
186
186
 
@@ -193,12 +193,12 @@ static VALUE log_failure_to_process_tag(VALUE err_details) {
193
193
 
194
194
  // Since we are calling into Ruby code, it may raise an exception. This method ensure that dynamically-allocated tags
195
195
  // get cleaned before propagating the exception.
196
- static void safely_log_failure_to_process_tag(ddog_Vec_tag tags, VALUE err_details) {
196
+ static void safely_log_failure_to_process_tag(ddog_Vec_Tag tags, VALUE err_details) {
197
197
  int exception_state;
198
198
  rb_protect(log_failure_to_process_tag, err_details, &exception_state);
199
199
 
200
200
  if (exception_state) { // An exception was raised
201
- ddog_Vec_tag_drop(tags); // clean up
201
+ ddog_Vec_Tag_drop(tags); // clean up
202
202
  rb_jump_tag(exception_state); // "Re-raise" exception
203
203
  }
204
204
  }
@@ -206,17 +206,18 @@ static void safely_log_failure_to_process_tag(ddog_Vec_tag tags, VALUE err_detai
206
206
  // Note: This function handles a bunch of libdatadog dynamically-allocated objects, so it MUST not use any Ruby APIs
207
207
  // which can raise exceptions, otherwise the objects will be leaked.
208
208
  static VALUE perform_export(
209
- ddog_NewProfileExporterResult valid_exporter_result, // Must be called with a valid exporter result
209
+ ddog_prof_Exporter_NewResult valid_exporter_result, // Must be called with a valid exporter result
210
210
  ddog_Timespec start,
211
211
  ddog_Timespec finish,
212
- ddog_Slice_file slice_files,
213
- ddog_Vec_tag *additional_tags,
212
+ ddog_prof_Exporter_Slice_File slice_files,
213
+ ddog_Vec_Tag *additional_tags,
214
214
  uint64_t timeout_milliseconds
215
215
  ) {
216
- ddog_ProfileExporter *exporter = valid_exporter_result.ok;
216
+ ddog_prof_Exporter *exporter = valid_exporter_result.ok;
217
217
  ddog_CancellationToken *cancel_token = ddog_CancellationToken_new();
218
- ddog_Request *request =
219
- ddog_ProfileExporter_build(exporter, start, finish, slice_files, additional_tags, timeout_milliseconds);
218
+ ddog_prof_ProfiledEndpointsStats *endpoints_stats = NULL; // Not in use yet
219
+ ddog_prof_Exporter_Request *request =
220
+ ddog_prof_Exporter_Request_build(exporter, start, finish, slice_files, additional_tags, endpoints_stats, timeout_milliseconds);
220
221
 
221
222
  // We'll release the Global VM Lock while we're calling send, so that the Ruby VM can continue to work while this
222
223
  // is pending
@@ -246,23 +247,23 @@ static VALUE perform_export(
246
247
 
247
248
  // Cleanup exporter and token, no longer needed
248
249
  ddog_CancellationToken_drop(cancel_token);
249
- ddog_NewProfileExporterResult_drop(valid_exporter_result);
250
+ ddog_prof_Exporter_NewResult_drop(valid_exporter_result);
250
251
 
251
252
  if (pending_exception) {
252
253
  // If we got here send did not run, so we need to explicitly dispose of the request
253
- ddog_Request_drop(request);
254
+ ddog_prof_Exporter_Request_drop(request);
254
255
 
255
256
  // Let Ruby propagate the exception. This will not return.
256
257
  rb_jump_tag(pending_exception);
257
258
  }
258
259
 
259
- ddog_SendResult result = args.result;
260
- bool success = result.tag == DDOG_SEND_RESULT_HTTP_RESPONSE;
260
+ ddog_prof_Exporter_SendResult result = args.result;
261
+ bool success = result.tag == DDOG_PROF_EXPORTER_SEND_RESULT_HTTP_RESPONSE;
261
262
 
262
263
  VALUE ruby_status = success ? ok_symbol : error_symbol;
263
- VALUE ruby_result = success ? UINT2NUM(result.http_response.code) : ruby_string_from_vec_u8(result.err);
264
+ VALUE ruby_result = success ? UINT2NUM(result.http_response.code) : ruby_string_from_prof_vec_u8(result.err);
264
265
 
265
- ddog_SendResult_drop(args.result);
266
+ ddog_prof_Exporter_SendResult_drop(args.result);
266
267
  // The request itself does not need to be freed as libdatadog takes care of it as part of sending.
267
268
 
268
269
  return rb_ary_new_from_args(2, ruby_status, ruby_result);
@@ -303,23 +304,23 @@ static VALUE _native_do_export(
303
304
  {.seconds = NUM2LONG(finish_timespec_seconds), .nanoseconds = NUM2UINT(finish_timespec_nanoseconds)};
304
305
 
305
306
  int files_to_report = 1 + (have_code_provenance ? 1 : 0);
306
- ddog_File files[files_to_report];
307
- ddog_Slice_file slice_files = {.ptr = files, .len = files_to_report};
307
+ ddog_prof_Exporter_File files[files_to_report];
308
+ ddog_prof_Exporter_Slice_File slice_files = {.ptr = files, .len = files_to_report};
308
309
 
309
- files[0] = (ddog_File) {
310
+ files[0] = (ddog_prof_Exporter_File) {
310
311
  .name = char_slice_from_ruby_string(pprof_file_name),
311
312
  .file = byte_slice_from_ruby_string(pprof_data)
312
313
  };
313
314
  if (have_code_provenance) {
314
- files[1] = (ddog_File) {
315
+ files[1] = (ddog_prof_Exporter_File) {
315
316
  .name = char_slice_from_ruby_string(code_provenance_file_name),
316
317
  .file = byte_slice_from_ruby_string(code_provenance_data)
317
318
  };
318
319
  }
319
320
 
320
- ddog_Vec_tag *null_additional_tags = NULL;
321
+ ddog_Vec_Tag *null_additional_tags = NULL;
321
322
 
322
- ddog_NewProfileExporterResult exporter_result = create_exporter(exporter_configuration, tags_as_array);
323
+ ddog_prof_Exporter_NewResult exporter_result = create_exporter(exporter_configuration, tags_as_array);
323
324
  // Note: Do not add anything that can raise exceptions after this line, as otherwise the exporter memory will leak
324
325
 
325
326
  VALUE failure_tuple = handle_exporter_failure(exporter_result);
@@ -331,7 +332,7 @@ static VALUE _native_do_export(
331
332
  static void *call_exporter_without_gvl(void *call_args) {
332
333
  struct call_exporter_without_gvl_arguments *args = (struct call_exporter_without_gvl_arguments*) call_args;
333
334
 
334
- args->result = ddog_ProfileExporter_send(args->exporter, args->request, args->cancel_token);
335
+ args->result = ddog_prof_Exporter_send(args->exporter, args->request, args->cancel_token);
335
336
  args->send_ran = true;
336
337
 
337
338
  return NULL; // Unused
@@ -9,6 +9,10 @@ inline static ddog_CharSlice char_slice_from_ruby_string(VALUE string) {
9
9
  return char_slice;
10
10
  }
11
11
 
12
- inline static VALUE ruby_string_from_vec_u8(ddog_Vec_u8 string) {
12
+ inline static VALUE ruby_string_from_vec_u8(ddog_Vec_U8 string) {
13
+ return rb_str_new((char *) string.ptr, string.len);
14
+ }
15
+
16
+ inline static VALUE ruby_string_from_prof_vec_u8(ddog_prof_Vec_U8 string) {
13
17
  return rb_str_new((char *) string.ptr, string.len);
14
18
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  # typed: ignore
4
4
 
5
- require 'libdatadog'
5
+ require 'rubygems'
6
6
  require 'pathname'
7
7
 
8
8
  module Datadog
@@ -17,6 +17,8 @@ module Datadog
17
17
  # Older Rubies don't have the MJIT header, used by the JIT compiler, so we need to use a different approach
18
18
  CAN_USE_MJIT_HEADER = RUBY_VERSION >= '2.6'
19
19
 
20
+ LIBDATADOG_VERSION = '~> 1.0.1.1.0'
21
+
20
22
  def self.fail_install_if_missing_extension?
21
23
  ENV[ENV_FAIL_INSTALL_IF_MISSING_EXTENSION].to_s.strip.downcase == 'true'
22
24
  end
@@ -88,6 +90,7 @@ module Datadog
88
90
  not_on_amd64_or_arm64? ||
89
91
  on_ruby_2_1? ||
90
92
  expected_to_use_mjit_but_mjit_is_disabled? ||
93
+ libdatadog_not_available? ||
91
94
  libdatadog_not_usable?
92
95
  end
93
96
 
@@ -142,11 +145,6 @@ module Datadog
142
145
  '<https://dtdg.co/ruby-profiler-troubleshooting>.'
143
146
  ].freeze
144
147
 
145
- REPORT_ISSUE = [
146
- 'If you needed to use this, please tell us why on',
147
- '<https://github.com/DataDog/dd-trace-rb/issues/new> so we can fix it :)',
148
- ].freeze
149
-
150
148
  GET_IN_TOUCH = [
151
149
  "Get in touch with us if you're interested in profiling your app!"
152
150
  ].freeze
@@ -172,18 +170,31 @@ module Datadog
172
170
  PKG_CONFIG_IS_MISSING = explain_issue(
173
171
  #+-----------------------------------------------------------------------------+
174
172
  'the `pkg-config` system tool is missing.',
175
- 'This issue can usually be fixed by installing:',
176
- '1. the `pkg-config` package on Homebrew and Debian/Ubuntu-based Linux;',
177
- '2. the `pkgconf` package on Arch and Alpine-based Linux;',
178
- '3. the `pkgconf-pkg-config` package on Fedora/Red Hat-based Linux.',
173
+ 'This issue can usually be fixed by installing one of the following:',
174
+ 'the `pkg-config` package on Homebrew and Debian/Ubuntu-based Linux;',
175
+ 'the `pkgconf` package on Arch and Alpine-based Linux;',
176
+ 'the `pkgconf-pkg-config` package on Fedora/Red Hat-based Linux.',
177
+ suggested: CONTACT_SUPPORT,
178
+ )
179
+
180
+ # Validation for this check is done in extconf.rb because it relies on mkmf
181
+ COMPILER_ATOMIC_MISSING = explain_issue(
182
+ 'your C compiler is missing support for the <stdatomic.h> header.',
183
+ 'This issue can usually be fixed by upgrading to a later version of your',
184
+ 'operating system image or compiler.',
179
185
  suggested: CONTACT_SUPPORT,
180
186
  )
181
187
 
182
188
  private_class_method def self.disabled_via_env?
189
+ report_disabled = [
190
+ 'If you needed to use this, please tell us why on',
191
+ '<https://github.com/DataDog/dd-trace-rb/issues/new> so we can fix it :)',
192
+ ].freeze
193
+
183
194
  disabled_via_env = explain_issue(
184
195
  'the `DD_PROFILING_NO_EXTENSION` environment variable is/was set to',
185
196
  '`true` during installation.',
186
- suggested: REPORT_ISSUE,
197
+ suggested: report_disabled,
187
198
  )
188
199
 
189
200
  return unless ENV[ENV_NO_EXTENSION].to_s.strip.downcase == 'true'
@@ -246,7 +257,7 @@ module Datadog
246
257
  suggested: GET_IN_TOUCH,
247
258
  )
248
259
 
249
- architecture_not_supported unless RUBY_PLATFORM.start_with?('x86_64', 'aarch64')
260
+ architecture_not_supported unless RUBY_PLATFORM.start_with?('x86_64', 'aarch64', 'arm64')
250
261
  end
251
262
 
252
263
  private_class_method def self.on_ruby_2_1?
@@ -271,6 +282,25 @@ module Datadog
271
282
  ruby_without_mjit if CAN_USE_MJIT_HEADER && RbConfig::CONFIG['MJIT_SUPPORT'] != 'yes'
272
283
  end
273
284
 
285
+ private_class_method def self.libdatadog_not_available?
286
+ begin
287
+ gem 'libdatadog', LIBDATADOG_VERSION
288
+ require 'libdatadog'
289
+ nil
290
+ # rubocop:disable Lint/RescueException
291
+ rescue Exception => e
292
+ explain_issue(
293
+ 'there was an exception during loading of the `libdatadog` gem:',
294
+ e.class.name,
295
+ *e.message.split("\n"),
296
+ *Array(e.backtrace),
297
+ '.',
298
+ suggested: CONTACT_SUPPORT,
299
+ )
300
+ end
301
+ # rubocop:enable Lint/RescueException
302
+ end
303
+
274
304
  private_class_method def self.libdatadog_not_usable?
275
305
  no_binaries_for_current_platform = explain_issue(
276
306
  'the `libdatadog` gem installed on your system is missing binaries for your',
@@ -50,6 +50,100 @@ rb_nativethread_id_t pthread_id_for(VALUE thread) {
50
50
  #endif
51
51
  }
52
52
 
53
+ // Queries if the current thread is the owner of the global VM lock.
54
+ //
55
+ // @ivoanjo: Ruby has a similarly-named `ruby_thread_has_gvl_p` but that API is insufficient for our needs because it can
56
+ // still return `true` even when a thread DOES NOT HAVE the global VM lock.
57
+ // In particular, looking at the implementation, that API assumes that if a thread is not in a "blocking region" then it
58
+ // will have the GVL which is probably true for the situations that API was designed to be called from BUT this assumption
59
+ // does not hold true when calling `ruby_thread_has_gvl_p` from a signal handler. (Because the thread may have lost the
60
+ // GVL due to a scheduler decision, not because it decided to block.)
61
+ // I have also submitted https://bugs.ruby-lang.org/issues/19172 to discuss this with upstream Ruby developers.
62
+ //
63
+ // Thus we need our own gvl-checking method which actually looks at the gvl structure to determine if it is the owner.
64
+ bool is_current_thread_holding_the_gvl(void) {
65
+ current_gvl_owner owner = gvl_owner();
66
+ return owner.valid && pthread_equal(pthread_self(), owner.owner);
67
+ }
68
+
69
+ #ifndef NO_GVL_OWNER // Ruby < 2.6 doesn't have the owner/running field
70
+ // NOTE: Reading the owner in this is a racy read, because we're not grabbing the lock that Ruby uses to protect it.
71
+ //
72
+ // While we could potentially grab this lock, I (@ivoanjo) think we actually don't need it because:
73
+ // * In the case where a thread owns the GVL and calls `gvl_owner`, it will always see the correct value. That's
74
+ // because every thread sets itself as the owner when it grabs the GVL and unsets itself at the end.
75
+ // That means that `is_current_thread_holding_the_gvl` is always accurate.
76
+ // * In a case where we observe a different thread, then this may change by the time we do something with this value
77
+ // anyway. So unless we want to prevent the Ruby scheduler from switching threads, we need to deal with races here.
78
+ current_gvl_owner gvl_owner(void) {
79
+ const rb_thread_t *current_owner =
80
+ #ifndef NO_RB_THREAD_SCHED // Introduced in Ruby 3.2 as a replacement for struct rb_global_vm_lock_struct
81
+ GET_RACTOR()->threads.sched.running;
82
+ #elif HAVE_RUBY_RACTOR_H
83
+ GET_RACTOR()->threads.gvl.owner;
84
+ #else
85
+ GET_VM()->gvl.owner;
86
+ #endif
87
+
88
+ if (current_owner == NULL) return (current_gvl_owner) {.valid = false};
89
+
90
+ return (current_gvl_owner) {
91
+ .valid = true,
92
+ .owner =
93
+ #ifndef NO_RB_NATIVE_THREAD
94
+ current_owner->nt->thread_id
95
+ #else
96
+ current_owner->thread_id
97
+ #endif
98
+ };
99
+ }
100
+ #else
101
+ current_gvl_owner gvl_owner(void) {
102
+ rb_vm_t *vm =
103
+ #ifndef NO_GET_VM
104
+ GET_VM();
105
+ #else
106
+ thread_struct_from_object(rb_thread_current())->vm;
107
+ #endif
108
+
109
+ // BIG Issue: Ruby < 2.6 did not have the owner field. The really nice thing about the owner field is that it's
110
+ // "atomic" -- when a thread sets it, it "declares" two things in a single step
111
+ // * Declaration 1: Someone has the GVL
112
+ // * Declaration 2: That someone is the specific thread
113
+ //
114
+ // Observation 1: On older versions of Ruby, this ownership concept is actually split. Specifically, `gvl.acquired`
115
+ // is a boolean that represents declaration 1 above, and `vm->running_thread` (or `ruby_current_thread`/
116
+ // `ruby_current_execution_context_ptr`) represents declaration 2.
117
+ //
118
+ // Observation 2: In addition, when a thread releases the GVL, it only sets `gvl.acquired` back to 0 **BUT CRUCIALLY
119
+ // DOES NOT CHANGE THE OTHER global variables**.
120
+ //
121
+ // Observation 1+2 above lead to the following possible race:
122
+ // * Thread A grabs the GVL (`gvl.acquired == 1`)
123
+ // * Thread A sets `running_thread` (`gvl.acquired == 1` + `running_thread == Thread A`)
124
+ // * Thread A releases the GVL (`gvl.acquired == 0` + `running_thread == Thread A`)
125
+ // * Thread B grabs the GVL (`gvl.acquired == 1` + `running_thread == Thread A`)
126
+ // * Thread A calls gvl_owner. Due to the current state (`gvl.acquired == 1` + `running_thread == Thread A`), this
127
+ // function returns an incorrect result.
128
+ // * Thread B finally sets `running_thread` (`gvl.acquired == 1` + `running_thread == Thread B`)
129
+ //
130
+ // This is especially problematic because we use `gvl_owner` to implement `is_current_thread_holding_the_gvl` which
131
+ // is called in a signal handler to decide "is it safe for me to call `rb_postponed_job_register_one` or not".
132
+ // (See constraints in `collectors_cpu_and_wall_time_worker.c` comments for why).
133
+ //
134
+ // Thus an incorrect `is_current_thread_holding_the_gvl` result may lead to issues inside `rb_postponed_job_register_one`.
135
+ //
136
+ // For this reason we currently do not enable the new Ruby profiler on Ruby 2.5 and below by default, and we print a
137
+ // warning when customers force-enable it.
138
+ bool gvl_acquired = vm->gvl.acquired != 0;
139
+ rb_thread_t *current_owner = vm->running_thread;
140
+
141
+ if (!gvl_acquired || current_owner == NULL) return (current_gvl_owner) {.valid = false};
142
+
143
+ return (current_gvl_owner) {.valid = true, .owner = current_owner->thread_id};
144
+ }
145
+ #endif // NO_GVL_OWNER
146
+
53
147
  // Taken from upstream vm_core.h at commit d9cf0388599a3234b9f3c06ddd006cd59a58ab8b (November 2022, Ruby 3.2 trunk)
54
148
  // Copyright (C) 2004-2007 Koichi Sasada
55
149
  // to support tid_for (see below)
@@ -128,7 +222,12 @@ VALUE ddtrace_thread_list(void) {
128
222
  rb_ractor_t *current_ractor = GET_RACTOR();
129
223
  ccan_list_for_each(&current_ractor->threads.set, thread, lt_node) {
130
224
  #else
131
- rb_vm_t *vm = thread_struct_from_object(rb_thread_current())->vm;
225
+ rb_vm_t *vm =
226
+ #ifndef NO_GET_VM
227
+ GET_VM();
228
+ #else
229
+ thread_struct_from_object(rb_thread_current())->vm;
230
+ #endif
132
231
  list_for_each(&vm->living_threads, thread, vmlt_node) {
133
232
  #endif
134
233
  switch (thread->status) {
@@ -284,9 +383,6 @@ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
284
383
  // * Add `end_cfp == NULL` and `end_cfp <= cfp` safety checks. These are used in a bunch of places in
285
384
  // `vm_backtrace.c` (`backtrace_each`, `backtrace_size`, `rb_ec_partial_backtrace_object`) but are conspicuously
286
385
  // absent from `rb_profile_frames`. Oversight?
287
- // * Distinguish between `end_cfp == NULL` (dead thread or some other error, returns 0) and `end_cfp <= cfp`
288
- // (alive thread which may just be executing native code and has not pushed anything on the Ruby stack, returns
289
- // PLACEHOLDER_STACK_IN_NATIVE_CODE). See comments on `record_placeholder_stack_in_native_code` for more details.
290
386
  // * Skip frames where `cfp->iseq && !cfp->pc`. These seem to be internal and are skipped by `backtrace_each` in
291
387
  // `vm_backtrace.c`.
292
388
  // * Check thread status and do not sample if thread has been killed.
@@ -294,6 +390,7 @@ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
294
390
  // for iseqs created from calls to `eval` and `instance_eval`. This makes it so that `rb_profile_frame_path` on
295
391
  // the `VALUE` returned by rb_profile_frames returns `(eval)` instead of the path of the file where the `eval`
296
392
  // was called from.
393
+ // * Imported fix from https://github.com/ruby/ruby/pull/7116 to avoid sampling threads that are still being created
297
394
  //
298
395
  // **IMPORTANT: WHEN CHANGING THIS FUNCTION, CONSIDER IF THE SAME CHANGE ALSO NEEDS TO BE MADE TO THE VARIANT FOR
299
396
  // RUBY 2.2 AND BELOW WHICH IS ALSO PRESENT ON THIS FILE**
@@ -340,13 +437,17 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, VALUE *buff, i
340
437
  const rb_control_frame_t *cfp = ec->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
341
438
  const rb_callable_method_entry_t *cme;
342
439
 
343
- // `vm_backtrace.c` includes this check in several methods, and I think this happens on either dead or newly-created
344
- // threads, but I'm not entirely sure
345
- if (end_cfp == NULL) return 0;
440
+ // This should not happen for ddtrace (it can only happen when a thread is still being created), but I've imported
441
+ // it from https://github.com/ruby/ruby/pull/7116 in a "just in case" kind of mindset.
442
+ if (cfp == NULL) return 0;
346
443
 
347
444
  // Avoid sampling dead threads
348
445
  if (th->status == THREAD_KILLED) return 0;
349
446
 
447
+ // `vm_backtrace.c` includes this check in several methods. This happens on newly-created threads, and may
448
+ // also (not entirely sure) happen on dead threads
449
+ if (end_cfp == NULL) return PLACEHOLDER_STACK_IN_NATIVE_CODE;
450
+
350
451
  // Fix: Skip dummy frame that shows up in main thread.
351
452
  //
352
453
  // According to a comment in `backtrace_each` (`vm_backtrace.c`), there's two dummy frames that we should ignore
@@ -650,10 +751,8 @@ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
650
751
  // * Add `end_cfp == NULL` and `end_cfp <= cfp` safety checks. These are used in a bunch of places in
651
752
  // `vm_backtrace.c` (`backtrace_each`, `backtrace_size`, `rb_ec_partial_backtrace_object`) but are conspicuously
652
753
  // absent from `rb_profile_frames`. Oversight?
653
- // * Distinguish between `end_cfp == NULL` (dead thread or some other error, returns 0) and `end_cfp <= cfp`
654
- // (alive thread which may just be executing native code and has not pushed anything on the Ruby stack, returns
655
- // PLACEHOLDER_STACK_IN_NATIVE_CODE). See comments on `record_placeholder_stack_in_native_code` for more details.
656
754
  // * Check thread status and do not sample if thread has been killed.
755
+ // * Imported fix from https://github.com/ruby/ruby/pull/7116 to avoid sampling threads that are still being created
657
756
  //
658
757
  // The `rb_profile_frames` function changed quite a bit between Ruby 2.2 and 2.3. Since the change was quite complex
659
758
  // I opted not to try to extend support to Ruby 2.2 using the same custom function, and instead I started
@@ -667,13 +766,17 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, VALUE *buff, i
667
766
  rb_thread_t *th = thread_struct_from_object(thread);
668
767
  rb_control_frame_t *cfp = th->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(th);
669
768
 
670
- // `vm_backtrace.c` includes this check in several methods, and I think this happens on either dead or newly-created
671
- // threads, but I'm not entirely sure
672
- if (end_cfp == NULL) return 0;
769
+ // This should not happen for ddtrace (it can only happen when a thread is still being created), but I've imported
770
+ // it from https://github.com/ruby/ruby/pull/7116 in a "just in case" kind of mindset.
771
+ if (cfp == NULL) return 0;
673
772
 
674
773
  // Avoid sampling dead threads
675
774
  if (th->status == THREAD_KILLED) return 0;
676
775
 
776
+ // `vm_backtrace.c` includes this check in several methods. This happens on newly-created threads, and may
777
+ // also (not entirely sure) happen on dead threads
778
+ if (end_cfp == NULL) return PLACEHOLDER_STACK_IN_NATIVE_CODE;
779
+
677
780
  // Fix: Skip dummy frame that shows up in main thread.
678
781
  //
679
782
  // According to a comment in `backtrace_each` (`vm_backtrace.c`), there's two dummy frames that we should ignore
@@ -717,15 +820,6 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, VALUE *buff, i
717
820
 
718
821
  #endif // USE_LEGACY_RB_PROFILE_FRAMES
719
822
 
720
- #ifdef NO_THREAD_HAS_GVL
721
- int ruby_thread_has_gvl_p(void) {
722
- // TODO: The CpuAndWallTimeWorker needs this function, but Ruby 2.2 doesn't expose it... For now this placeholder
723
- // will enable the profiling native extension to continue to compile on Ruby 2.2, but the CpuAndWallTimeWorker will
724
- // not work properly on 2.2. Will be addressed later.
725
- return 0;
726
- }
727
- #endif // NO_THREAD_HAS_GVL
728
-
729
823
  #ifndef NO_RACTORS
730
824
  // This API and definition are exported as a public symbol by the VM BUT the function header is not defined in any public header, so we
731
825
  // repeat it here to be able to use in our code.
@@ -7,11 +7,20 @@
7
7
  // without also dragging the incompatible includes
8
8
  #ifndef PRIVATE_VM_API_ACCESS_SKIP_RUBY_INCLUDES
9
9
  #include <ruby/thread_native.h>
10
+ #include <ruby/vm.h>
10
11
  #endif
11
12
 
12
13
  #include "extconf.h"
13
14
 
15
+ // Contains the current gvl owner, and a flag to indicate if it is valid
16
+ typedef struct {
17
+ bool valid;
18
+ rb_nativethread_id_t owner;
19
+ } current_gvl_owner;
20
+
14
21
  rb_nativethread_id_t pthread_id_for(VALUE thread);
22
+ bool is_current_thread_holding_the_gvl(void);
23
+ current_gvl_owner gvl_owner(void);
15
24
  uint64_t native_thread_id_for(VALUE thread);
16
25
  ptrdiff_t stack_depth_for(VALUE thread);
17
26
  VALUE ddtrace_thread_list(void);