datadog 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -2
  3. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +9 -1
  4. data/ext/datadog_profiling_loader/extconf.rb +14 -26
  5. data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
  6. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
  7. data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
  8. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +257 -69
  9. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +53 -28
  10. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
  11. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
  12. data/ext/datadog_profiling_native_extension/collectors_stack.c +136 -81
  13. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +661 -48
  15. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +10 -1
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +83 -0
  17. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +53 -0
  18. data/ext/datadog_profiling_native_extension/extconf.rb +91 -69
  19. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +50 -0
  20. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +75 -0
  21. data/ext/datadog_profiling_native_extension/heap_recorder.c +54 -12
  22. data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
  23. data/ext/datadog_profiling_native_extension/helpers.h +6 -17
  24. data/ext/datadog_profiling_native_extension/http_transport.c +41 -9
  25. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
  26. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
  27. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -172
  28. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +116 -139
  29. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +20 -11
  30. data/ext/datadog_profiling_native_extension/profiling.c +1 -3
  31. data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
  32. data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
  33. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
  34. data/ext/datadog_profiling_native_extension/stack_recorder.c +14 -2
  35. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
  36. data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
  37. data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
  38. data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +37 -22
  39. data/ext/libdatadog_api/datadog_ruby_common.c +83 -0
  40. data/ext/libdatadog_api/datadog_ruby_common.h +53 -0
  41. data/ext/libdatadog_api/extconf.rb +108 -0
  42. data/ext/libdatadog_api/macos_development.md +26 -0
  43. data/ext/libdatadog_extconf_helpers.rb +130 -0
  44. data/lib/datadog/appsec/assets/waf_rules/recommended.json +2184 -108
  45. data/lib/datadog/appsec/assets/waf_rules/strict.json +1430 -2
  46. data/lib/datadog/appsec/component.rb +29 -8
  47. data/lib/datadog/appsec/configuration/settings.rb +2 -2
  48. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +1 -0
  49. data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +21 -0
  50. data/lib/datadog/appsec/contrib/devise/patcher.rb +12 -2
  51. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +35 -0
  52. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +109 -0
  53. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +71 -0
  54. data/lib/datadog/appsec/contrib/graphql/integration.rb +54 -0
  55. data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
  56. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
  57. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +3 -6
  58. data/lib/datadog/appsec/event.rb +1 -1
  59. data/lib/datadog/appsec/processor/actions.rb +1 -1
  60. data/lib/datadog/appsec/processor/rule_loader.rb +3 -1
  61. data/lib/datadog/appsec/processor/rule_merger.rb +33 -15
  62. data/lib/datadog/appsec/processor.rb +36 -37
  63. data/lib/datadog/appsec/rate_limiter.rb +25 -40
  64. data/lib/datadog/appsec/remote.rb +7 -3
  65. data/lib/datadog/appsec/response.rb +15 -1
  66. data/lib/datadog/appsec.rb +3 -2
  67. data/lib/datadog/core/configuration/components.rb +18 -15
  68. data/lib/datadog/core/configuration/settings.rb +135 -9
  69. data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
  70. data/lib/datadog/core/crashtracking/component.rb +111 -0
  71. data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
  72. data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
  73. data/lib/datadog/core/environment/execution.rb +5 -5
  74. data/lib/datadog/core/metrics/client.rb +7 -0
  75. data/lib/datadog/core/rate_limiter.rb +183 -0
  76. data/lib/datadog/core/remote/client/capabilities.rb +4 -3
  77. data/lib/datadog/core/remote/component.rb +4 -2
  78. data/lib/datadog/core/remote/negotiation.rb +4 -4
  79. data/lib/datadog/core/remote/tie.rb +2 -0
  80. data/lib/datadog/core/runtime/metrics.rb +1 -1
  81. data/lib/datadog/core/telemetry/component.rb +51 -2
  82. data/lib/datadog/core/telemetry/emitter.rb +9 -11
  83. data/lib/datadog/core/telemetry/event.rb +37 -1
  84. data/lib/datadog/core/telemetry/ext.rb +1 -0
  85. data/lib/datadog/core/telemetry/http/adapters/net.rb +10 -12
  86. data/lib/datadog/core/telemetry/http/ext.rb +3 -0
  87. data/lib/datadog/core/telemetry/http/transport.rb +38 -9
  88. data/lib/datadog/core/telemetry/logger.rb +51 -0
  89. data/lib/datadog/core/telemetry/logging.rb +71 -0
  90. data/lib/datadog/core/telemetry/request.rb +13 -1
  91. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  92. data/lib/datadog/core/utils/time.rb +12 -0
  93. data/lib/datadog/di/code_tracker.rb +168 -0
  94. data/lib/datadog/di/configuration/settings.rb +163 -0
  95. data/lib/datadog/di/configuration.rb +11 -0
  96. data/lib/datadog/di/error.rb +31 -0
  97. data/lib/datadog/di/extensions.rb +16 -0
  98. data/lib/datadog/di/probe.rb +133 -0
  99. data/lib/datadog/di/probe_builder.rb +41 -0
  100. data/lib/datadog/di/redactor.rb +188 -0
  101. data/lib/datadog/di/serializer.rb +193 -0
  102. data/lib/datadog/di.rb +14 -0
  103. data/lib/datadog/kit/appsec/events.rb +2 -4
  104. data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -0
  105. data/lib/datadog/opentelemetry/sdk/span_processor.rb +10 -0
  106. data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
  107. data/lib/datadog/profiling/collectors/code_provenance.rb +7 -7
  108. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +28 -26
  109. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
  110. data/lib/datadog/profiling/collectors/info.rb +15 -6
  111. data/lib/datadog/profiling/collectors/thread_context.rb +30 -2
  112. data/lib/datadog/profiling/component.rb +89 -95
  113. data/lib/datadog/profiling/exporter.rb +3 -3
  114. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
  115. data/lib/datadog/profiling/ext.rb +21 -21
  116. data/lib/datadog/profiling/flush.rb +1 -1
  117. data/lib/datadog/profiling/http_transport.rb +14 -7
  118. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  119. data/lib/datadog/profiling/preload.rb +1 -1
  120. data/lib/datadog/profiling/profiler.rb +5 -8
  121. data/lib/datadog/profiling/scheduler.rb +33 -25
  122. data/lib/datadog/profiling/stack_recorder.rb +3 -0
  123. data/lib/datadog/profiling/tag_builder.rb +2 -2
  124. data/lib/datadog/profiling/tasks/exec.rb +5 -5
  125. data/lib/datadog/profiling/tasks/setup.rb +16 -35
  126. data/lib/datadog/profiling.rb +4 -5
  127. data/lib/datadog/single_step_instrument.rb +12 -0
  128. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -12
  129. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -0
  130. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +78 -0
  131. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +33 -0
  132. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +2 -0
  133. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +4 -0
  134. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +3 -1
  135. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +4 -1
  136. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +5 -1
  137. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +5 -0
  138. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
  139. data/lib/datadog/tracing/contrib/ext.rb +14 -0
  140. data/lib/datadog/tracing/contrib/faraday/middleware.rb +9 -0
  141. data/lib/datadog/tracing/contrib/grape/endpoint.rb +19 -0
  142. data/lib/datadog/tracing/contrib/graphql/patcher.rb +9 -12
  143. data/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +3 -3
  144. data/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +3 -3
  145. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +14 -10
  146. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +10 -4
  147. data/lib/datadog/tracing/contrib/http/instrumentation.rb +18 -15
  148. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -5
  149. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +1 -14
  150. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +5 -0
  151. data/lib/datadog/tracing/contrib/httprb/patcher.rb +1 -14
  152. data/lib/datadog/tracing/contrib/lograge/patcher.rb +15 -0
  153. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
  154. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  155. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
  156. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +13 -6
  157. data/lib/datadog/tracing/contrib/patcher.rb +2 -1
  158. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  159. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
  160. data/lib/datadog/tracing/contrib/presto/patcher.rb +1 -13
  161. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
  162. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  163. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
  164. data/lib/datadog/tracing/contrib/rack/middlewares.rb +27 -0
  165. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -0
  166. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +4 -0
  167. data/lib/datadog/tracing/contrib/stripe/request.rb +3 -2
  168. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
  169. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
  170. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
  171. data/lib/datadog/tracing/distributed/propagation.rb +7 -0
  172. data/lib/datadog/tracing/metadata/errors.rb +9 -1
  173. data/lib/datadog/tracing/metadata/ext.rb +6 -0
  174. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  175. data/lib/datadog/tracing/remote.rb +5 -2
  176. data/lib/datadog/tracing/sampling/matcher.rb +6 -1
  177. data/lib/datadog/tracing/sampling/rate_sampler.rb +1 -1
  178. data/lib/datadog/tracing/sampling/rule.rb +2 -0
  179. data/lib/datadog/tracing/sampling/rule_sampler.rb +9 -5
  180. data/lib/datadog/tracing/sampling/span/ext.rb +1 -1
  181. data/lib/datadog/tracing/sampling/span/rule.rb +2 -2
  182. data/lib/datadog/tracing/span.rb +9 -2
  183. data/lib/datadog/tracing/span_event.rb +41 -0
  184. data/lib/datadog/tracing/span_operation.rb +6 -2
  185. data/lib/datadog/tracing/trace_operation.rb +26 -2
  186. data/lib/datadog/tracing/tracer.rb +14 -12
  187. data/lib/datadog/tracing/transport/http/client.rb +1 -0
  188. data/lib/datadog/tracing/transport/io/client.rb +1 -0
  189. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
  190. data/lib/datadog/tracing/workers/trace_writer.rb +1 -1
  191. data/lib/datadog/tracing/workers.rb +1 -1
  192. data/lib/datadog/version.rb +1 -1
  193. metadata +46 -11
  194. data/lib/datadog/profiling/crashtracker.rb +0 -91
  195. data/lib/datadog/profiling/ext/forking.rb +0 -98
  196. data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
@@ -1,15 +1,25 @@
1
1
  #include <ruby.h>
2
- #include <datadog/common.h>
3
- #include <libdatadog_helpers.h>
2
+ #include <datadog/crashtracker.h>
3
+
4
+ #include "datadog_ruby_common.h"
4
5
 
5
6
  static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);
6
7
  static VALUE _native_stop(DDTRACE_UNUSED VALUE _self);
8
+ static void crashtracker_init(VALUE crashtracking_module);
7
9
 
8
10
  // Used to report Ruby VM crashes.
9
11
  // Once initialized, segfaults will be reported automatically using libdatadog.
10
12
 
11
- void crashtracker_init(VALUE profiling_module) {
12
- VALUE crashtracker_class = rb_define_class_under(profiling_module, "Crashtracker", rb_cObject);
13
+ void DDTRACE_EXPORT Init_libdatadog_api(void) {
14
+ VALUE datadog_module = rb_define_module("Datadog");
15
+ VALUE core_module = rb_define_module_under(datadog_module, "Core");
16
+ VALUE crashtracking_module = rb_define_module_under(core_module, "Crashtracking");
17
+
18
+ crashtracker_init(crashtracking_module);
19
+ }
20
+
21
+ void crashtracker_init(VALUE crashtracking_module) {
22
+ VALUE crashtracker_class = rb_define_class_under(crashtracking_module, "Component", rb_cObject);
13
23
 
14
24
  rb_define_singleton_method(crashtracker_class, "_native_start_or_update_on_fork", _native_start_or_update_on_fork, -1);
15
25
  rb_define_singleton_method(crashtracker_class, "_native_stop", _native_stop, 0);
@@ -18,8 +28,9 @@ void crashtracker_init(VALUE profiling_module) {
18
28
  static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self) {
19
29
  VALUE options;
20
30
  rb_scan_args(argc, argv, "0:", &options);
31
+ if (options == Qnil) options = rb_hash_new();
21
32
 
22
- VALUE exporter_configuration = rb_hash_fetch(options, ID2SYM(rb_intern("exporter_configuration")));
33
+ VALUE agent_base_url = rb_hash_fetch(options, ID2SYM(rb_intern("agent_base_url")));
23
34
  VALUE path_to_crashtracking_receiver_binary = rb_hash_fetch(options, ID2SYM(rb_intern("path_to_crashtracking_receiver_binary")));
24
35
  VALUE ld_library_path = rb_hash_fetch(options, ID2SYM(rb_intern("ld_library_path")));
25
36
  VALUE tags_as_array = rb_hash_fetch(options, ID2SYM(rb_intern("tags_as_array")));
@@ -29,7 +40,7 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
29
40
  VALUE start_action = ID2SYM(rb_intern("start"));
30
41
  VALUE update_on_fork_action = ID2SYM(rb_intern("update_on_fork"));
31
42
 
32
- ENFORCE_TYPE(exporter_configuration, T_ARRAY);
43
+ ENFORCE_TYPE(agent_base_url, T_STRING);
33
44
  ENFORCE_TYPE(tags_as_array, T_ARRAY);
34
45
  ENFORCE_TYPE(path_to_crashtracking_receiver_binary, T_STRING);
35
46
  ENFORCE_TYPE(ld_library_path, T_STRING);
@@ -38,14 +49,14 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
38
49
 
39
50
  if (action != start_action && action != update_on_fork_action) rb_raise(rb_eArgError, "Unexpected action: %+"PRIsVALUE, action);
40
51
 
41
- VALUE version = ddtrace_version();
42
- ddog_prof_Endpoint endpoint = endpoint_from(exporter_configuration);
52
+ VALUE version = datadog_gem_version();
43
53
 
44
- // Tags are heap-allocated, so after here we can't raise exceptions otherwise we'll leak this memory
54
+ // Tags and endpoint are heap-allocated, so after here we can't raise exceptions otherwise we'll leak this memory
45
55
  // Start of exception-free zone to prevent leaks {{
56
+ ddog_Endpoint *endpoint = ddog_endpoint_from_url(char_slice_from_ruby_string(agent_base_url));
46
57
  ddog_Vec_Tag tags = convert_tags(tags_as_array);
47
58
 
48
- ddog_prof_CrashtrackerConfiguration config = {
59
+ ddog_crasht_Config config = {
49
60
  .additional_files = {},
50
61
  // The Ruby VM already uses an alt stack to detect stack overflows so the crash handler must not overwrite it.
51
62
  //
@@ -57,23 +68,26 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
57
68
  // "Process.kill('SEGV', Process.pid)" gets run.
58
69
  .create_alt_stack = false,
59
70
  .endpoint = endpoint,
60
- .resolve_frames = DDOG_PROF_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER,
71
+ .resolve_frames = DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER,
61
72
  .timeout_secs = FIX2INT(upload_timeout_seconds),
73
+ // Waits for crash tracker to finish reporting the issue before letting the Ruby process die; see
74
+ // https://github.com/DataDog/libdatadog/pull/477 for details
75
+ .wait_for_receiver = true,
62
76
  };
63
77
 
64
- ddog_prof_CrashtrackerMetadata metadata = {
65
- .profiling_library_name = DDOG_CHARSLICE_C("dd-trace-rb"),
66
- .profiling_library_version = char_slice_from_ruby_string(version),
78
+ ddog_crasht_Metadata metadata = {
79
+ .library_name = DDOG_CHARSLICE_C("dd-trace-rb"),
80
+ .library_version = char_slice_from_ruby_string(version),
67
81
  .family = DDOG_CHARSLICE_C("ruby"),
68
82
  .tags = &tags,
69
83
  };
70
84
 
71
- ddog_prof_EnvVar ld_library_path_env = {
85
+ ddog_crasht_EnvVar ld_library_path_env = {
72
86
  .key = DDOG_CHARSLICE_C("LD_LIBRARY_PATH"),
73
87
  .val = char_slice_from_ruby_string(ld_library_path),
74
88
  };
75
89
 
76
- ddog_prof_CrashtrackerReceiverConfig receiver_config = {
90
+ ddog_crasht_ReceiverConfig receiver_config = {
77
91
  .args = {},
78
92
  .env = {.ptr = &ld_library_path_env, .len = 1},
79
93
  .path_to_receiver_binary = char_slice_from_ruby_string(path_to_crashtracking_receiver_binary),
@@ -81,16 +95,17 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
81
95
  .optional_stdout_filename = {},
82
96
  };
83
97
 
84
- ddog_prof_CrashtrackerResult result =
98
+ ddog_crasht_Result result =
85
99
  action == start_action ?
86
- ddog_prof_Crashtracker_init(config, receiver_config, metadata) :
87
- ddog_prof_Crashtracker_update_on_fork(config, receiver_config, metadata);
100
+ ddog_crasht_init_with_receiver(config, receiver_config, metadata) :
101
+ ddog_crasht_update_on_fork(config, receiver_config, metadata);
88
102
 
89
103
  // Clean up before potentially raising any exceptions
90
104
  ddog_Vec_Tag_drop(tags);
105
+ ddog_endpoint_drop(endpoint);
91
106
  // }} End of exception-free zone to prevent leaks
92
107
 
93
- if (result.tag == DDOG_PROF_CRASHTRACKER_RESULT_ERR) {
108
+ if (result.tag == DDOG_CRASHT_RESULT_ERR) {
94
109
  rb_raise(rb_eRuntimeError, "Failed to start/update the crash tracker: %"PRIsVALUE, get_error_details_and_drop(&result.err));
95
110
  }
96
111
 
@@ -98,9 +113,9 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
98
113
  }
99
114
 
100
115
  static VALUE _native_stop(DDTRACE_UNUSED VALUE _self) {
101
- ddog_prof_CrashtrackerResult result = ddog_prof_Crashtracker_shutdown();
116
+ ddog_crasht_Result result = ddog_crasht_shutdown();
102
117
 
103
- if (result.tag == DDOG_PROF_CRASHTRACKER_RESULT_ERR) {
118
+ if (result.tag == DDOG_CRASHT_RESULT_ERR) {
104
119
  rb_raise(rb_eRuntimeError, "Failed to stop the crash tracker: %"PRIsVALUE, get_error_details_and_drop(&result.err));
105
120
  }
106
121
 
@@ -0,0 +1,83 @@
1
+ #include "datadog_ruby_common.h"
2
+
3
+ // IMPORTANT: Currently this file is copy-pasted between extensions. Make sure to update all versions when doing any change!
4
+
5
+ void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name) {
6
+ rb_exc_raise(
7
+ rb_exc_new_str(
8
+ rb_eTypeError,
9
+ rb_sprintf("wrong argument %"PRIsVALUE" for '%s' (expected a %s) at %s:%d:in `%s'",
10
+ rb_inspect(value),
11
+ value_name,
12
+ type_name,
13
+ file,
14
+ line,
15
+ function_name
16
+ )
17
+ )
18
+ );
19
+ }
20
+
21
+ VALUE datadog_gem_version(void) {
22
+ VALUE ddtrace_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
23
+ ENFORCE_TYPE(ddtrace_module, T_MODULE);
24
+ VALUE version_module = rb_const_get(ddtrace_module, rb_intern("VERSION"));
25
+ ENFORCE_TYPE(version_module, T_MODULE);
26
+ VALUE version_string = rb_const_get(version_module, rb_intern("STRING"));
27
+ ENFORCE_TYPE(version_string, T_STRING);
28
+ return version_string;
29
+ }
30
+
31
+ static VALUE log_failure_to_process_tag(VALUE err_details) {
32
+ VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
33
+ VALUE logger = rb_funcall(datadog_module, rb_intern("logger"), 0);
34
+
35
+ return rb_funcall(logger, rb_intern("warn"), 1, rb_sprintf("Failed to convert tag: %"PRIsVALUE, err_details));
36
+ }
37
+
38
+ __attribute__((warn_unused_result))
39
+ ddog_Vec_Tag convert_tags(VALUE tags_as_array) {
40
+ ENFORCE_TYPE(tags_as_array, T_ARRAY);
41
+
42
+ long tags_count = RARRAY_LEN(tags_as_array);
43
+ ddog_Vec_Tag tags = ddog_Vec_Tag_new();
44
+
45
+ for (long i = 0; i < tags_count; i++) {
46
+ VALUE name_value_pair = rb_ary_entry(tags_as_array, i);
47
+
48
+ if (!RB_TYPE_P(name_value_pair, T_ARRAY)) {
49
+ ddog_Vec_Tag_drop(tags);
50
+ ENFORCE_TYPE(name_value_pair, T_ARRAY);
51
+ }
52
+
53
+ // Note: We can index the array without checking its size first because rb_ary_entry returns Qnil if out of bounds
54
+ VALUE tag_name = rb_ary_entry(name_value_pair, 0);
55
+ VALUE tag_value = rb_ary_entry(name_value_pair, 1);
56
+
57
+ if (!(RB_TYPE_P(tag_name, T_STRING) && RB_TYPE_P(tag_value, T_STRING))) {
58
+ ddog_Vec_Tag_drop(tags);
59
+ ENFORCE_TYPE(tag_name, T_STRING);
60
+ ENFORCE_TYPE(tag_value, T_STRING);
61
+ }
62
+
63
+ ddog_Vec_Tag_PushResult push_result =
64
+ ddog_Vec_Tag_push(&tags, char_slice_from_ruby_string(tag_name), char_slice_from_ruby_string(tag_value));
65
+
66
+ if (push_result.tag == DDOG_VEC_TAG_PUSH_RESULT_ERR) {
67
+ // libdatadog validates tags and may catch invalid tags that ddtrace didn't actually catch.
68
+ // We warn users about such tags, and then just ignore them.
69
+
70
+ int exception_state;
71
+ rb_protect(log_failure_to_process_tag, get_error_details_and_drop(&push_result.err), &exception_state);
72
+
73
+ // Since we are calling into Ruby code, it may raise an exception. Ensure that dynamically-allocated tags
74
+ // get cleaned before propagating the exception.
75
+ if (exception_state) {
76
+ ddog_Vec_Tag_drop(tags);
77
+ rb_jump_tag(exception_state); // "Re-raise" exception
78
+ }
79
+ }
80
+ }
81
+
82
+ return tags;
83
+ }
@@ -0,0 +1,53 @@
1
+ #pragma once
2
+
3
+ // IMPORTANT: Currently this file is copy-pasted between extensions. Make sure to update all versions when doing any change!
4
+
5
+ #include <ruby.h>
6
+ #include <datadog/profiling.h>
7
+
8
+ // Used to mark symbols to be exported to the outside of the extension.
9
+ // Consider very carefully before tagging a function with this.
10
+ #define DDTRACE_EXPORT __attribute__ ((visibility ("default")))
11
+
12
+ // Used to mark function arguments that are deliberately left unused
13
+ #ifdef __GNUC__
14
+ #define DDTRACE_UNUSED __attribute__((unused))
15
+ #else
16
+ #define DDTRACE_UNUSED
17
+ #endif
18
+
19
+ #define ADD_QUOTES_HELPER(x) #x
20
+ #define ADD_QUOTES(x) ADD_QUOTES_HELPER(x)
21
+
22
+ // Ruby has a Check_Type(value, type) that is roughly equivalent to this BUT Ruby's version is rather cryptic when it fails
23
+ // e.g. "wrong argument type nil (expected String)". This is a replacement that prints more information to help debugging.
24
+ #define ENFORCE_TYPE(value, type) \
25
+ { if (RB_UNLIKELY(!RB_TYPE_P(value, type))) raise_unexpected_type(value, ADD_QUOTES(value), ADD_QUOTES(type), __FILE__, __LINE__, __func__); }
26
+
27
+ #define ENFORCE_BOOLEAN(value) \
28
+ { if (RB_UNLIKELY(value != Qtrue && value != Qfalse)) raise_unexpected_type(value, ADD_QUOTES(value), "true or false", __FILE__, __LINE__, __func__); }
29
+
30
+ NORETURN(void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name));
31
+
32
+ // Helper to retrieve Datadog::VERSION::STRING
33
+ VALUE datadog_gem_version(void);
34
+
35
+ static inline ddog_CharSlice char_slice_from_ruby_string(VALUE string) {
36
+ ENFORCE_TYPE(string, T_STRING);
37
+ ddog_CharSlice char_slice = {.ptr = RSTRING_PTR(string), .len = RSTRING_LEN(string)};
38
+ return char_slice;
39
+ }
40
+
41
+ __attribute__((warn_unused_result))
42
+ ddog_Vec_Tag convert_tags(VALUE tags_as_array);
43
+
44
+ static inline VALUE ruby_string_from_error(const ddog_Error *error) {
45
+ ddog_CharSlice char_slice = ddog_Error_message(error);
46
+ return rb_str_new(char_slice.ptr, char_slice.len);
47
+ }
48
+
49
+ static inline VALUE get_error_details_and_drop(ddog_Error *error) {
50
+ VALUE result = ruby_string_from_error(error);
51
+ ddog_Error_drop(error);
52
+ return result;
53
+ }
@@ -0,0 +1,108 @@
1
+ # rubocop:disable Style/StderrPuts
2
+ # rubocop:disable Style/GlobalVars
3
+
4
+ require 'rubygems'
5
+ require_relative '../libdatadog_extconf_helpers'
6
+
7
+ def skip_building_extension!(reason)
8
+ $stderr.puts(
9
+ "WARN: Skipping build of libdatadog_api (#{reason}). Some functionality will not be available."
10
+ )
11
+
12
+ fail_install_if_missing_extension = ENV['DD_FAIL_INSTALL_IF_MISSING_EXTENSION'].to_s.strip.downcase == 'true'
13
+
14
+ if fail_install_if_missing_extension
15
+ require 'mkmf'
16
+ Logging.message("[datadog] Failure cause: #{reason}")
17
+ else
18
+ File.write('Makefile', 'all install clean: # dummy makefile that does nothing')
19
+ end
20
+
21
+ exit
22
+ end
23
+
24
+ if ENV['DD_NO_EXTENSION'].to_s.strip.downcase == 'true'
25
+ skip_building_extension!('the `DD_NO_EXTENSION` environment variable is/was set to `true` during installation')
26
+ end
27
+ skip_building_extension!('current Ruby VM is not supported') if RUBY_ENGINE != 'ruby'
28
+ skip_building_extension!('Microsoft Windows is not supported') if Gem.win_platform?
29
+ skip_building_extension!('issue setting up `libdatadog` gem') if Datadog::LibdatadogExtconfHelpers.libdatadog_issue?
30
+
31
+ require 'mkmf'
32
+
33
+ # Because we can't control what compiler versions our customers use, shipping with -Werror by default is a no-go.
34
+ # But we can enable it in CI, so that we quickly spot any new warnings that just got introduced.
35
+ append_cflags '-Werror' if ENV['DATADOG_GEM_CI'] == 'true'
36
+
37
+ # Older gcc releases may not default to C99 and we need to ask for this. This is also used:
38
+ # * by upstream Ruby -- search for gnu99 in the codebase
39
+ # * by msgpack, another datadog gem dependency
40
+ # (https://github.com/msgpack/msgpack-ruby/blob/18ce08f6d612fe973843c366ac9a0b74c4e50599/ext/msgpack/extconf.rb#L8)
41
+ append_cflags '-std=gnu99'
42
+
43
+ # Allow defining variables at any point in a function
44
+ append_cflags '-Wno-declaration-after-statement'
45
+
46
+ # If we forget to include a Ruby header, the function call may still appear to work, but then
47
+ # cause a segfault later. Let's ensure that never happens.
48
+ append_cflags '-Werror-implicit-function-declaration'
49
+
50
+ # Warn on unused parameters to functions. Use `DDTRACE_UNUSED` to mark things as known-to-not-be-used.
51
+ append_cflags '-Wunused-parameter'
52
+
53
+ # The native extension is not intended to expose any symbols/functions for other native libraries to use;
54
+ # the sole exception being `Init_libdatadog_api` which needs to be visible for Ruby to call it when
55
+ # it `dlopen`s the library.
56
+ #
57
+ # By setting this compiler flag, we tell it to assume that everything is private unless explicitly stated.
58
+ # For more details see https://gcc.gnu.org/wiki/Visibility
59
+ append_cflags '-fvisibility=hidden'
60
+
61
+ # Avoid legacy C definitions
62
+ append_cflags '-Wold-style-definition'
63
+
64
+ # Enable all other compiler warnings
65
+ append_cflags '-Wall'
66
+ append_cflags '-Wextra'
67
+
68
+ if ENV['DDTRACE_DEBUG'] == 'true'
69
+ $defs << '-DDD_DEBUG'
70
+ CONFIG['optflags'] = '-O0'
71
+ CONFIG['debugflags'] = '-ggdb3'
72
+ end
73
+
74
+ # If we got here, libdatadog is available and loaded
75
+ ENV['PKG_CONFIG_PATH'] = "#{ENV['PKG_CONFIG_PATH']}:#{Libdatadog.pkgconfig_folder}"
76
+ Logging.message("[datadog] PKG_CONFIG_PATH set to #{ENV['PKG_CONFIG_PATH'].inspect}\n")
77
+ $stderr.puts("Using libdatadog #{Libdatadog::VERSION} from #{Libdatadog.pkgconfig_folder}")
78
+
79
+ unless pkg_config('datadog_profiling_with_rpath')
80
+ Logging.message("[datadog] Ruby detected the pkg-config command is #{$PKGCONFIG.inspect}\n")
81
+
82
+ if Datadog::LibdatadogExtconfHelpers.pkg_config_missing?
83
+ skip_building_extension!('the `pkg-config` system tool is missing')
84
+ else
85
+ skip_building_extension!('there was a problem in setting up the `libdatadog` dependency')
86
+ end
87
+ end
88
+
89
+ # See comments on the helper methods being used for why we need to additionally set this.
90
+ # The extremely excessive escaping around ORIGIN below seems to be correct and was determined after a lot of
91
+ # experimentation. We need to get these special characters across a lot of tools untouched...
92
+ extra_relative_rpaths = [
93
+ Datadog::LibdatadogExtconfHelpers.libdatadog_folder_relative_to_native_lib_folder(current_folder: __dir__),
94
+ *Datadog::LibdatadogExtconfHelpers.libdatadog_folder_relative_to_ruby_extensions_folders,
95
+ ]
96
+ extra_relative_rpaths.each { |folder| $LDFLAGS += " -Wl,-rpath,$$$\\\\{ORIGIN\\}/#{folder.to_str}" }
97
+ Logging.message("[datadog] After pkg-config $LDFLAGS were set to: #{$LDFLAGS.inspect}\n")
98
+
99
+ # Tag the native extension library with the Ruby version and Ruby platform.
100
+ # This makes it easier for development (avoids "oops I forgot to rebuild when I switched my Ruby") and ensures that
101
+ # the wrong library is never loaded.
102
+ # When requiring, we need to use the exact same string, including the version and the platform.
103
+ EXTENSION_NAME = "libdatadog_api.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}".freeze
104
+
105
+ create_makefile(EXTENSION_NAME)
106
+
107
+ # rubocop:enable Style/GlobalVars
108
+ # rubocop:enable Style/StderrPuts
@@ -0,0 +1,26 @@
1
+ # Developing on macOS
2
+
3
+ As of this writing (August 2024), the libdatadog builds on rubygems.org only support Linux.
4
+
5
+ We don't officially support using libdatadog for Ruby on other platforms yet, but it is possible to use it for local development on macOS.
6
+ (**Note that you don't need these instructions if you develop inside docker.**)
7
+
8
+ Here's how you can do so:
9
+
10
+ 1. [Install rust](https://www.rust-lang.org/tools/install)
11
+ 2. Install `cbindgen`: `cargo install cbindgen`
12
+ 3. Clone [libdatadog](https://github.com/datadog/libdatadog)
13
+ 4. Create a folder for building into based on your ruby platform:
14
+
15
+ ```
16
+ export DD_RUBY_PLATFORM=`ruby -e 'puts Gem::Platform.local.to_s'`
17
+ mkdir -p my-libdatadog-build/$DD_RUBY_PLATFORM
18
+ ```
19
+
20
+ 5. Build libdatadog into this folder: `./build-profiling-ffi.sh my-libdatadog-build/$DD_RUBY_PLATFORM`
21
+ 6. Tell the Ruby where to find libdatadog: `export LIBDATADOG_VENDOR_OVERRIDE=/full/path/to/my-libdatadog-build/` (Notice no platform here)
22
+ 7. Run `bundle exec rake clean compile`
23
+
24
+ If you additionally want to run the profiler test suite, also remember to `export DD_PROFILING_MACOS_TESTING=true` and re-run `rake clean compile`.
25
+
26
+ These instructions can quickly get outdated, so feel free to open an issue if they're not working (and/or ping @ivoanjo).
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems'
4
+ require 'pathname'
5
+
6
+ module Datadog
7
+ # Contains a bunch of shared helpers that get used during building of extensions that link to libdatadog
8
+ module LibdatadogExtconfHelpers
9
+ # Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
10
+ # may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
11
+ LIBDATADOG_VERSION = '~> 12.0.0.1.0'
12
+
13
+ # Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
14
+ # libdatadog are moved after the extension gets compiled.
15
+ #
16
+ # Because the libdatadog native library is installed on a non-standard system path, in order for it to be
17
+ # found by the system dynamic linker (e.g. what takes care of dlopen(), which is used to load
18
+ # native extensions), we need to add a "runpath" -- a list of folders to search for libdatadog.
19
+ #
20
+ # This runpath gets hardcoded at native library linking time. You can look at it using the `readelf` tool in
21
+ # Linux: e.g. `readelf -d datadog_profiling_native_extension.2.7.3_x86_64-linux.so`.
22
+ #
23
+ # In older versions of the datadog gem, we only set as runpath an absolute path to libdatadog.
24
+ # (This gets set automatically by the call
25
+ # to `pkg_config('datadog_profiling_with_rpath')` in `extconf.rb`). This worked fine as long as libdatadog was **NOT**
26
+ # moved from the folder it was present at datadog gem installation/linking time.
27
+ #
28
+ # Unfortunately, environments such as Heroku and AWS Elastic Beanstalk move gems around in the filesystem after
29
+ # installation. Thus, the profiling native extension could not be loaded in these environments
30
+ # (see https://github.com/DataDog/dd-trace-rb/issues/2067) because libdatadog could not be found.
31
+ #
32
+ # To workaround this issue, this method computes the **relative** path between the folder where
33
+ # native extensions are going to be installed and the folder where libdatadog is installed, and returns it
34
+ # to be set as an additional runpath. (Yes, you can set multiple runpath folders to be searched).
35
+ #
36
+ # This way, if both gems are moved together (and it turns out that they are in these environments),
37
+ # the relative path can still be traversed to find libdatadog.
38
+ #
39
+ # This is incredibly awful, and it's kinda bizarre how it's not possible to just find these paths at runtime
40
+ # and set them correctly; rather than needing to set stuff at linking-time and then praying to $deity that
41
+ # weird moves don't happen.
42
+ #
43
+ # As a curiosity, `LD_LIBRARY_PATH` can be used to influence the folders that get searched but **CANNOT BE
44
+ # SET DYNAMICALLY**, e.g. it needs to be set at the start of the process (Ruby VM) and thus it's not something
45
+ # we could setup when doing a `require`.
46
+ #
47
+ def self.libdatadog_folder_relative_to_native_lib_folder(
48
+ current_folder:,
49
+ libdatadog_pkgconfig_folder: Libdatadog.pkgconfig_folder
50
+ )
51
+ return unless libdatadog_pkgconfig_folder
52
+
53
+ native_lib_folder = "#{current_folder}/../../lib/"
54
+ libdatadog_lib_folder = "#{libdatadog_pkgconfig_folder}/../"
55
+
56
+ Pathname.new(libdatadog_lib_folder).relative_path_from(Pathname.new(native_lib_folder)).to_s
57
+ end
58
+
59
+ # In https://github.com/DataDog/dd-trace-rb/pull/3582 we got a report of a customer for which the native extension
60
+ # only got installed into the extensions folder.
61
+ #
62
+ # But then this fix was not enough to fully get them moving because then they started to see the issue from
63
+ # https://github.com/DataDog/dd-trace-rb/issues/2067 / https://github.com/DataDog/dd-trace-rb/pull/2125 :
64
+ #
65
+ # > Profiling was requested but is not supported, profiling disabled: There was an error loading the profiling
66
+ # > native extension due to 'RuntimeError Failure to load datadog_profiling_native_extension.3.2.2_x86_64-linux
67
+ # > due to libdatadog_profiling.so: cannot open shared object file: No such file or directory
68
+ #
69
+ # The problem is that when loading the native extension from the extensions directory, the relative rpath we add
70
+ # with the #libdatadog_folder_relative_to_native_lib_folder helper above is not correct, we need to add a relative
71
+ # rpath to the extensions directory.
72
+ #
73
+ # So how do we find the full path where the native extension is placed?
74
+ # * From https://github.com/ruby/ruby/blob/83f02d42e0a3c39661dc99c049ab9a70ff227d5b/lib/bundler/runtime.rb#L166
75
+ # `extension_dirs = Dir["#{Gem.dir}/extensions/*/*/*"] + Dir["#{Gem.dir}/bundler/gems/extensions/*/*/*"]`
76
+ # we get that's in one of two fixed subdirectories of `Gem.dir`
77
+ # * From https://github.com/ruby/ruby/blob/83f02d42e0a3c39661dc99c049ab9a70ff227d5b/lib/rubygems/basic_specification.rb#L111-L115
78
+ # we get the structure of the subdirectory (platform/extension_api_version/gem_and_version)
79
+ #
80
+ # Thus, `Gem.dir` of `/var/app/current/vendor/bundle/ruby/3.2.0` becomes (for instance)
81
+ # `/var/app/current/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/datadog-2.0.0/` or
82
+ # `/var/app/current/vendor/bundle/ruby/3.2.0/bundler/gems/extensions/x86_64-linux/3.2.0/datadog-2.0.0/`
83
+ #
84
+ # We then compute the relative path between these folders and the libdatadog folder, and use that as a relative path.
85
+ def self.libdatadog_folder_relative_to_ruby_extensions_folders(
86
+ gem_dir: Gem.dir,
87
+ libdatadog_pkgconfig_folder: Libdatadog.pkgconfig_folder
88
+ )
89
+ return unless libdatadog_pkgconfig_folder
90
+
91
+ # For the purposes of calculating a folder relative to the other, we don't actually NEED to fill in the
92
+ # platform, extension_api_version and gem version. We're basically just after how many folders it is deep from
93
+ # the Gem.dir.
94
+ expected_ruby_extensions_folders = [
95
+ "#{gem_dir}/extensions/platform/extension_api_version/datadog_version/",
96
+ "#{gem_dir}/bundler/gems/extensions/platform/extension_api_version/datadog_version/",
97
+ ]
98
+ libdatadog_lib_folder = "#{libdatadog_pkgconfig_folder}/../"
99
+
100
+ expected_ruby_extensions_folders.map do |folder|
101
+ Pathname.new(libdatadog_lib_folder).relative_path_from(Pathname.new(folder)).to_s
102
+ end
103
+ end
104
+
105
+ # mkmf sets $PKGCONFIG after the `pkg_config` gets used in extconf.rb. When `pkg_config` is unsuccessful, we use
106
+ # this helper to decide if we can show more specific error message vs a generic "something went wrong".
107
+ def self.pkg_config_missing?(command: $PKGCONFIG) # rubocop:disable Style/GlobalVars
108
+ pkg_config_available = command && xsystem("#{command} --version")
109
+
110
+ pkg_config_available != true
111
+ end
112
+
113
+ def self.try_loading_libdatadog
114
+ gem 'libdatadog', LIBDATADOG_VERSION
115
+ require 'libdatadog'
116
+ nil
117
+ rescue Exception => e # rubocop:disable Lint/RescueException
118
+ if block_given?
119
+ yield e
120
+ else
121
+ e
122
+ end
123
+ end
124
+
125
+ def self.libdatadog_issue?
126
+ try_loading_libdatadog { |_exception| return true }
127
+ Libdatadog.pkgconfig_folder.nil?
128
+ end
129
+ end
130
+ end