ddtrace 1.7.0 → 1.9.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 (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);