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
@@ -1,18 +1,37 @@
1
1
  #include <ruby.h>
2
+ #include <ruby/thread.h>
3
+ #include <errno.h>
2
4
 
3
5
  #include "clock_id.h"
4
6
  #include "helpers.h"
5
7
  #include "private_vm_api_access.h"
8
+ #include "ruby_helpers.h"
9
+ #include "setup_signal_handler.h"
10
+ #include "time_helpers.h"
6
11
 
7
12
  // Each class/module here is implemented in their separate file
8
13
  void collectors_cpu_and_wall_time_init(VALUE profiling_module);
9
14
  void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module);
15
+ void collectors_dynamic_sampling_rate_init(VALUE profiling_module);
16
+ void collectors_idle_sampling_helper_init(VALUE profiling_module);
10
17
  void collectors_stack_init(VALUE profiling_module);
11
18
  void http_transport_init(VALUE profiling_module);
12
19
  void stack_recorder_init(VALUE profiling_module);
13
20
 
14
21
  static VALUE native_working_p(VALUE self);
22
+ static VALUE _native_grab_gvl_and_raise(DDTRACE_UNUSED VALUE _self, VALUE exception_class, VALUE test_message, VALUE test_message_arg, VALUE release_gvl);
23
+ static void *trigger_grab_gvl_and_raise(void *trigger_args);
24
+ static VALUE _native_grab_gvl_and_raise_syserr(DDTRACE_UNUSED VALUE _self, VALUE syserr_errno, VALUE test_message, VALUE test_message_arg, VALUE release_gvl);
25
+ static void *trigger_grab_gvl_and_raise_syserr(void *trigger_args);
15
26
  static VALUE _native_ddtrace_rb_ractor_main_p(DDTRACE_UNUSED VALUE _self);
27
+ static VALUE _native_is_current_thread_holding_the_gvl(DDTRACE_UNUSED VALUE _self);
28
+ static VALUE _native_release_gvl_and_call_is_current_thread_holding_the_gvl(DDTRACE_UNUSED VALUE _self);
29
+ static void *testing_is_current_thread_holding_the_gvl(DDTRACE_UNUSED void *_unused);
30
+ static VALUE _native_install_holding_the_gvl_signal_handler(DDTRACE_UNUSED VALUE _self);
31
+ static void holding_the_gvl_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
32
+ static VALUE _native_trigger_holding_the_gvl_signal_handler_on(DDTRACE_UNUSED VALUE _self, VALUE background_thread);
33
+ static VALUE _native_enforce_success(DDTRACE_UNUSED VALUE _self, VALUE syserr_errno, VALUE with_gvl);
34
+ static void *trigger_enforce_success(void *trigger_args);
16
35
 
17
36
  void DDTRACE_EXPORT Init_ddtrace_profiling_native_extension(void) {
18
37
  VALUE datadog_module = rb_define_module("Datadog");
@@ -26,13 +45,27 @@ void DDTRACE_EXPORT Init_ddtrace_profiling_native_extension(void) {
26
45
 
27
46
  collectors_cpu_and_wall_time_init(profiling_module);
28
47
  collectors_cpu_and_wall_time_worker_init(profiling_module);
48
+ collectors_dynamic_sampling_rate_init(profiling_module);
49
+ collectors_idle_sampling_helper_init(profiling_module);
29
50
  collectors_stack_init(profiling_module);
30
51
  http_transport_init(profiling_module);
31
52
  stack_recorder_init(profiling_module);
32
53
 
33
54
  // Hosts methods used for testing the native code using RSpec
34
55
  VALUE testing_module = rb_define_module_under(native_extension_module, "Testing");
56
+ rb_define_singleton_method(testing_module, "_native_grab_gvl_and_raise", _native_grab_gvl_and_raise, 4);
57
+ rb_define_singleton_method(testing_module, "_native_grab_gvl_and_raise_syserr", _native_grab_gvl_and_raise_syserr, 4);
35
58
  rb_define_singleton_method(testing_module, "_native_ddtrace_rb_ractor_main_p", _native_ddtrace_rb_ractor_main_p, 0);
59
+ rb_define_singleton_method(testing_module, "_native_is_current_thread_holding_the_gvl", _native_is_current_thread_holding_the_gvl, 0);
60
+ rb_define_singleton_method(
61
+ testing_module,
62
+ "_native_release_gvl_and_call_is_current_thread_holding_the_gvl",
63
+ _native_release_gvl_and_call_is_current_thread_holding_the_gvl,
64
+ 0
65
+ );
66
+ rb_define_singleton_method(testing_module, "_native_install_holding_the_gvl_signal_handler", _native_install_holding_the_gvl_signal_handler, 0);
67
+ rb_define_singleton_method(testing_module, "_native_trigger_holding_the_gvl_signal_handler_on", _native_trigger_holding_the_gvl_signal_handler_on, 1);
68
+ rb_define_singleton_method(testing_module, "_native_enforce_success", _native_enforce_success, 2);
36
69
  }
37
70
 
38
71
  static VALUE native_working_p(DDTRACE_UNUSED VALUE _self) {
@@ -41,6 +74,178 @@ static VALUE native_working_p(DDTRACE_UNUSED VALUE _self) {
41
74
  return Qtrue;
42
75
  }
43
76
 
77
+ struct trigger_grab_gvl_and_raise_arguments {
78
+ VALUE exception_class;
79
+ char *test_message;
80
+ int test_message_arg;
81
+ };
82
+
83
+ static VALUE _native_grab_gvl_and_raise(DDTRACE_UNUSED VALUE _self, VALUE exception_class, VALUE test_message, VALUE test_message_arg, VALUE release_gvl) {
84
+ ENFORCE_TYPE(test_message, T_STRING);
85
+
86
+ struct trigger_grab_gvl_and_raise_arguments args;
87
+
88
+ args.exception_class = exception_class;
89
+ args.test_message = StringValueCStr(test_message);
90
+ args.test_message_arg = test_message_arg != Qnil ? NUM2INT(test_message_arg) : -1;
91
+
92
+ if (RTEST(release_gvl)) {
93
+ rb_thread_call_without_gvl(trigger_grab_gvl_and_raise, &args, NULL, NULL);
94
+ } else {
95
+ grab_gvl_and_raise(args.exception_class, "%s", args.test_message);
96
+ }
97
+
98
+ rb_raise(rb_eRuntimeError, "Failed to raise exception in _native_grab_gvl_and_raise; this should never happen");
99
+ }
100
+
101
+ static void *trigger_grab_gvl_and_raise(void *trigger_args) {
102
+ struct trigger_grab_gvl_and_raise_arguments *args = (struct trigger_grab_gvl_and_raise_arguments *) trigger_args;
103
+
104
+ if (args->test_message_arg >= 0) {
105
+ grab_gvl_and_raise(args->exception_class, "%s%d", args->test_message, args->test_message_arg);
106
+ } else {
107
+ grab_gvl_and_raise(args->exception_class, "%s", args->test_message);
108
+ }
109
+
110
+ return NULL;
111
+ }
112
+
113
+ struct trigger_grab_gvl_and_raise_syserr_arguments {
114
+ int syserr_errno;
115
+ char *test_message;
116
+ int test_message_arg;
117
+ };
118
+
119
+ static VALUE _native_grab_gvl_and_raise_syserr(DDTRACE_UNUSED VALUE _self, VALUE syserr_errno, VALUE test_message, VALUE test_message_arg, VALUE release_gvl) {
120
+ ENFORCE_TYPE(test_message, T_STRING);
121
+
122
+ struct trigger_grab_gvl_and_raise_syserr_arguments args;
123
+
124
+ args.syserr_errno = NUM2INT(syserr_errno);
125
+ args.test_message = StringValueCStr(test_message);
126
+ args.test_message_arg = test_message_arg != Qnil ? NUM2INT(test_message_arg) : -1;
127
+
128
+ if (RTEST(release_gvl)) {
129
+ rb_thread_call_without_gvl(trigger_grab_gvl_and_raise_syserr, &args, NULL, NULL);
130
+ } else {
131
+ grab_gvl_and_raise_syserr(args.syserr_errno, "%s", args.test_message);
132
+ }
133
+
134
+ rb_raise(rb_eRuntimeError, "Failed to raise exception in _native_grab_gvl_and_raise_syserr; this should never happen");
135
+ }
136
+
137
+ static void *trigger_grab_gvl_and_raise_syserr(void *trigger_args) {
138
+ struct trigger_grab_gvl_and_raise_syserr_arguments *args = (struct trigger_grab_gvl_and_raise_syserr_arguments *) trigger_args;
139
+
140
+ if (args->test_message_arg >= 0) {
141
+ grab_gvl_and_raise_syserr(args->syserr_errno, "%s%d", args->test_message, args->test_message_arg);
142
+ } else {
143
+ grab_gvl_and_raise_syserr(args->syserr_errno, "%s", args->test_message);
144
+ }
145
+
146
+ return NULL;
147
+ }
148
+
44
149
  static VALUE _native_ddtrace_rb_ractor_main_p(DDTRACE_UNUSED VALUE _self) {
45
150
  return ddtrace_rb_ractor_main_p() ? Qtrue : Qfalse;
46
151
  }
152
+
153
+ static VALUE _native_is_current_thread_holding_the_gvl(DDTRACE_UNUSED VALUE _self) {
154
+ return ((bool) testing_is_current_thread_holding_the_gvl(NULL)) ? Qtrue : Qfalse;
155
+ }
156
+
157
+ static VALUE _native_release_gvl_and_call_is_current_thread_holding_the_gvl(DDTRACE_UNUSED VALUE _self) {
158
+ return ((bool) rb_thread_call_without_gvl(testing_is_current_thread_holding_the_gvl, NULL, NULL, NULL)) ? Qtrue : Qfalse;
159
+ }
160
+
161
+ static void *testing_is_current_thread_holding_the_gvl(DDTRACE_UNUSED void *_unused) {
162
+ return (void *) is_current_thread_holding_the_gvl();
163
+ }
164
+
165
+ static VALUE _native_install_holding_the_gvl_signal_handler(DDTRACE_UNUSED VALUE _self) {
166
+ install_sigprof_signal_handler(holding_the_gvl_signal_handler, "holding_the_gvl_signal_handler");
167
+ return Qtrue;
168
+ }
169
+
170
+ static pthread_mutex_t holding_the_gvl_signal_handler_mutex = PTHREAD_MUTEX_INITIALIZER;
171
+ static pthread_cond_t holding_the_gvl_signal_handler_executed = PTHREAD_COND_INITIALIZER;
172
+ static VALUE holding_the_gvl_signal_handler_result[3];
173
+
174
+ // Ruby VM API that is exported but not present in the header files. Only used by holding_the_gvl_signal_handler below and SHOULD NOT
175
+ // be used in any other situation. See the comments on is_current_thread_holding_the_gvl for details.
176
+ int ruby_thread_has_gvl_p(void);
177
+
178
+ static void holding_the_gvl_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext) {
179
+ pthread_mutex_lock(&holding_the_gvl_signal_handler_mutex);
180
+
181
+ VALUE test_executed = Qtrue;
182
+ VALUE ruby_thread_has_gvl_p_result = ruby_thread_has_gvl_p() ? Qtrue : Qfalse;
183
+ VALUE is_current_thread_holding_the_gvl_result = is_current_thread_holding_the_gvl() ? Qtrue : Qfalse;
184
+
185
+ holding_the_gvl_signal_handler_result[0] = test_executed;
186
+ holding_the_gvl_signal_handler_result[1] = ruby_thread_has_gvl_p_result;
187
+ holding_the_gvl_signal_handler_result[2] = is_current_thread_holding_the_gvl_result;
188
+
189
+ pthread_cond_broadcast(&holding_the_gvl_signal_handler_executed);
190
+ pthread_mutex_unlock(&holding_the_gvl_signal_handler_mutex);
191
+ }
192
+
193
+ static VALUE _native_trigger_holding_the_gvl_signal_handler_on(DDTRACE_UNUSED VALUE _self, VALUE background_thread) {
194
+ holding_the_gvl_signal_handler_result[0] = Qfalse;
195
+ holding_the_gvl_signal_handler_result[1] = Qfalse;
196
+ holding_the_gvl_signal_handler_result[2] = Qfalse;
197
+
198
+ rb_nativethread_id_t thread = pthread_id_for(background_thread);
199
+
200
+ ENFORCE_SUCCESS_GVL(pthread_mutex_lock(&holding_the_gvl_signal_handler_mutex));
201
+
202
+ // We keep trying for ~5 seconds (500 x 10ms) to try to avoid any flakiness if the test machine is a bit slow
203
+ for (int tries = 0; holding_the_gvl_signal_handler_result[0] == Qfalse && tries < 500; tries++) {
204
+ pthread_kill(thread, SIGPROF);
205
+
206
+ // pthread_cond_timedwait is simply awful -- the deadline is based on wall-clock using a struct timespec, so we need
207
+ // all of the below complexity just to tell it "timeout is 10ms". The % limit dance below is needed because the
208
+ // `tv_nsec` part of a timespec can't go over the limit.
209
+ struct timespec deadline;
210
+ clock_gettime(CLOCK_REALTIME, &deadline);
211
+
212
+ unsigned int timeout_ns = MILLIS_AS_NS(10);
213
+ unsigned int tv_nsec_limit = SECONDS_AS_NS(1);
214
+ if ((deadline.tv_nsec + timeout_ns) < tv_nsec_limit) {
215
+ deadline.tv_nsec += timeout_ns;
216
+ } else {
217
+ deadline.tv_nsec = (deadline.tv_nsec + timeout_ns) % tv_nsec_limit;
218
+ deadline.tv_sec++;
219
+ }
220
+
221
+ int error = pthread_cond_timedwait(&holding_the_gvl_signal_handler_executed, &holding_the_gvl_signal_handler_mutex, &deadline);
222
+ if (error && error != ETIMEDOUT) ENFORCE_SUCCESS_GVL(error);
223
+ }
224
+
225
+ ENFORCE_SUCCESS_GVL(pthread_mutex_unlock(&holding_the_gvl_signal_handler_mutex));
226
+
227
+ replace_sigprof_signal_handler_with_empty_handler(holding_the_gvl_signal_handler);
228
+
229
+ if (holding_the_gvl_signal_handler_result[0] == Qfalse) rb_raise(rb_eRuntimeError, "Could not signal background_thread");
230
+
231
+ VALUE result = rb_hash_new();
232
+ rb_hash_aset(result, ID2SYM(rb_intern("ruby_thread_has_gvl_p")), holding_the_gvl_signal_handler_result[1]);
233
+ rb_hash_aset(result, ID2SYM(rb_intern("is_current_thread_holding_the_gvl")), holding_the_gvl_signal_handler_result[2]);
234
+ return result;
235
+ }
236
+
237
+ static VALUE _native_enforce_success(DDTRACE_UNUSED VALUE _self, VALUE syserr_errno, VALUE with_gvl) {
238
+ if (RTEST(with_gvl)) {
239
+ ENFORCE_SUCCESS_GVL(NUM2INT(syserr_errno));
240
+ } else {
241
+ rb_thread_call_without_gvl(trigger_enforce_success, (void *) (intptr_t) NUM2INT(syserr_errno), NULL, NULL);
242
+ }
243
+
244
+ return Qtrue;
245
+ }
246
+
247
+ static void *trigger_enforce_success(void *trigger_args) {
248
+ intptr_t syserr_errno = (intptr_t) trigger_args;
249
+ ENFORCE_SUCCESS_NO_GVL(syserr_errno);
250
+ return NULL;
251
+ }
@@ -1,4 +1,8 @@
1
+ #include <ruby.h>
2
+ #include <ruby/thread.h>
3
+
1
4
  #include "ruby_helpers.h"
5
+ #include "private_vm_api_access.h"
2
6
 
3
7
  void raise_unexpected_type(
4
8
  VALUE value,
@@ -22,3 +26,85 @@ void raise_unexpected_type(
22
26
  )
23
27
  );
24
28
  }
29
+
30
+ #define MAX_RAISE_MESSAGE_SIZE 256
31
+
32
+ struct raise_arguments {
33
+ VALUE exception_class;
34
+ char exception_message[MAX_RAISE_MESSAGE_SIZE];
35
+ };
36
+
37
+ static void *trigger_raise(void *raise_arguments) {
38
+ struct raise_arguments *args = (struct raise_arguments *) raise_arguments;
39
+ rb_raise(args->exception_class, "%s", args->exception_message);
40
+ }
41
+
42
+ void grab_gvl_and_raise(VALUE exception_class, const char *format_string, ...) {
43
+ struct raise_arguments args;
44
+
45
+ args.exception_class = exception_class;
46
+
47
+ va_list format_string_arguments;
48
+ va_start(format_string_arguments, format_string);
49
+ vsnprintf(args.exception_message, MAX_RAISE_MESSAGE_SIZE, format_string, format_string_arguments);
50
+
51
+ if (is_current_thread_holding_the_gvl()) {
52
+ rb_raise(
53
+ rb_eRuntimeError,
54
+ "grab_gvl_and_raise called by thread holding the global VM lock. exception_message: '%s'",
55
+ args.exception_message
56
+ );
57
+ }
58
+
59
+ rb_thread_call_with_gvl(trigger_raise, &args);
60
+
61
+ rb_bug("[DDTRACE] Unexpected: Reached the end of grab_gvl_and_raise while raising '%s'\n", args.exception_message);
62
+ }
63
+
64
+ struct syserr_raise_arguments {
65
+ int syserr_errno;
66
+ char exception_message[MAX_RAISE_MESSAGE_SIZE];
67
+ };
68
+
69
+ static void *trigger_syserr_raise(void *syserr_raise_arguments) {
70
+ struct syserr_raise_arguments *args = (struct syserr_raise_arguments *) syserr_raise_arguments;
71
+ rb_syserr_fail(args->syserr_errno, args->exception_message);
72
+ }
73
+
74
+ void grab_gvl_and_raise_syserr(int syserr_errno, const char *format_string, ...) {
75
+ struct syserr_raise_arguments args;
76
+
77
+ args.syserr_errno = syserr_errno;
78
+
79
+ va_list format_string_arguments;
80
+ va_start(format_string_arguments, format_string);
81
+ vsnprintf(args.exception_message, MAX_RAISE_MESSAGE_SIZE, format_string, format_string_arguments);
82
+
83
+ if (is_current_thread_holding_the_gvl()) {
84
+ rb_raise(
85
+ rb_eRuntimeError,
86
+ "grab_gvl_and_raise_syserr called by thread holding the global VM lock. syserr_errno: %d, exception_message: '%s'",
87
+ syserr_errno,
88
+ args.exception_message
89
+ );
90
+ }
91
+
92
+ rb_thread_call_with_gvl(trigger_syserr_raise, &args);
93
+
94
+ rb_bug("[DDTRACE] Unexpected: Reached the end of grab_gvl_and_raise_syserr while raising '%s'\n", args.exception_message);
95
+ }
96
+
97
+ void raise_syserr(
98
+ int syserr_errno,
99
+ bool have_gvl,
100
+ const char *expression,
101
+ const char *file,
102
+ int line,
103
+ const char *function_name
104
+ ) {
105
+ if (have_gvl) {
106
+ rb_exc_raise(rb_syserr_new_str(syserr_errno, rb_sprintf("Failure returned by '%s' at %s:%d:in `%s'", expression, file, line, function_name)));
107
+ } else {
108
+ grab_gvl_and_raise_syserr(syserr_errno, "Failure returned by '%s' at %s:%d:in `%s'", expression, file, line, function_name);
109
+ }
110
+ }
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include <ruby.h>
4
+ #include <stdbool.h>
4
5
 
5
6
  #include "helpers.h"
6
7
 
@@ -57,11 +58,32 @@ NORETURN(void raise_unexpected_type(
57
58
  const char *type_name,
58
59
  const char *file,
59
60
  int line,
60
- const char* function_name
61
+ const char *function_name
61
62
  ));
62
63
 
63
- // This API is exported as a public symbol by the VM BUT the function header is not defined in any public header, so we
64
- // repeat it here to be able to use in our code.
65
- //
66
- // Queries if the current thread is the owner of the global VM lock.
67
- int ruby_thread_has_gvl_p(void);
64
+ #define VALUE_COUNT(array) (sizeof(array) / sizeof(VALUE))
65
+
66
+ NORETURN(
67
+ void grab_gvl_and_raise(VALUE exception_class, const char *format_string, ...)
68
+ __attribute__ ((format (printf, 2, 3)));
69
+ );
70
+ NORETURN(
71
+ void grab_gvl_and_raise_syserr(int syserr_errno, const char *format_string, ...)
72
+ __attribute__ ((format (printf, 2, 3)));
73
+ );
74
+
75
+ #define ENFORCE_SUCCESS_GVL(expression) ENFORCE_SUCCESS_HELPER(expression, true)
76
+ #define ENFORCE_SUCCESS_NO_GVL(expression) ENFORCE_SUCCESS_HELPER(expression, false)
77
+
78
+ #define ENFORCE_SUCCESS_HELPER(expression, have_gvl) \
79
+ { int result_syserr_errno = expression; if (RB_UNLIKELY(result_syserr_errno)) raise_syserr(result_syserr_errno, have_gvl, ADD_QUOTES(expression), __FILE__, __LINE__, __func__); }
80
+
81
+ // Called by ENFORCE_SUCCESS_HELPER; should not be used directly
82
+ NORETURN(void raise_syserr(
83
+ int syserr_errno,
84
+ bool have_gvl,
85
+ const char *expression,
86
+ const char *file,
87
+ int line,
88
+ const char *function_name
89
+ ));
@@ -5,6 +5,9 @@
5
5
 
6
6
  #include "helpers.h"
7
7
  #include "setup_signal_handler.h"
8
+ #include "ruby_helpers.h"
9
+
10
+ // Used by Collectors::CpuAndWallTimeWorker to setup SIGPROF signal handlers used for cpu/wall-time profiling.
8
11
 
9
12
  static void install_sigprof_signal_handler_internal(
10
13
  void (*signal_handler_function)(int, siginfo_t *, void *),
@@ -88,9 +91,25 @@ void remove_sigprof_signal_handler(void) {
88
91
  if (sigaction(SIGPROF, &signal_handler_config, NULL) != 0) rb_sys_fail("Failure while removing the signal handler");
89
92
  }
90
93
 
94
+ static void toggle_sigprof_signal_handler_for_current_thread(int action) {
95
+ sigset_t signals_to_toggle;
96
+ sigemptyset(&signals_to_toggle);
97
+ sigaddset(&signals_to_toggle, SIGPROF);
98
+ int error = pthread_sigmask(action, &signals_to_toggle, NULL);
99
+ if (error) rb_exc_raise(rb_syserr_new_str(error, rb_sprintf("Unexpected failure in pthread_sigmask, action=%d", action)));
100
+ }
101
+
91
102
  void block_sigprof_signal_handler_from_running_in_current_thread(void) {
92
- sigset_t signals_to_block;
93
- sigemptyset(&signals_to_block);
94
- sigaddset(&signals_to_block, SIGPROF);
95
- pthread_sigmask(SIG_BLOCK, &signals_to_block, NULL);
103
+ toggle_sigprof_signal_handler_for_current_thread(SIG_BLOCK);
104
+ }
105
+
106
+ void unblock_sigprof_signal_handler_from_running_in_current_thread(void) {
107
+ toggle_sigprof_signal_handler_for_current_thread(SIG_UNBLOCK);
108
+ }
109
+
110
+ VALUE is_sigprof_blocked_in_current_thread(void) {
111
+ sigset_t current_signals;
112
+ sigemptyset(&current_signals);
113
+ ENFORCE_SUCCESS_GVL(pthread_sigmask(0, NULL, &current_signals));
114
+ return sigismember(&current_signals, SIGPROF) ? Qtrue : Qfalse;
96
115
  }
@@ -1,7 +1,11 @@
1
1
  #pragma once
2
2
 
3
+ #include <signal.h>
4
+
3
5
  void empty_signal_handler(DDTRACE_UNUSED int _signal, DDTRACE_UNUSED siginfo_t *_info, DDTRACE_UNUSED void *_ucontext);
4
6
  void install_sigprof_signal_handler(void (*signal_handler_function)(int, siginfo_t *, void *), const char *handler_pretty_name);
5
7
  void replace_sigprof_signal_handler_with_empty_handler(void (*expected_existing_handler)(int, siginfo_t *, void *));
6
8
  void remove_sigprof_signal_handler(void);
7
9
  void block_sigprof_signal_handler_from_running_in_current_thread(void);
10
+ void unblock_sigprof_signal_handler_from_running_in_current_thread(void);
11
+ VALUE is_sigprof_blocked_in_current_thread(void);