datadog 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -2
  3. data/ext/datadog_profiling_loader/extconf.rb +15 -15
  4. data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
  5. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
  6. data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
  7. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +113 -43
  8. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +49 -26
  9. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
  10. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
  11. data/ext/datadog_profiling_native_extension/collectors_stack.c +49 -37
  12. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  13. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +81 -19
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
  15. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +110 -0
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +57 -0
  17. data/ext/datadog_profiling_native_extension/extconf.rb +65 -60
  18. data/ext/datadog_profiling_native_extension/heap_recorder.c +34 -6
  19. data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
  20. data/ext/datadog_profiling_native_extension/helpers.h +6 -17
  21. data/ext/datadog_profiling_native_extension/http_transport.c +3 -3
  22. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
  23. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
  24. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -172
  25. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +64 -138
  26. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +17 -11
  27. data/ext/datadog_profiling_native_extension/profiling.c +0 -2
  28. data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
  29. data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
  30. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
  31. data/ext/datadog_profiling_native_extension/stack_recorder.c +14 -2
  32. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
  33. data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
  34. data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
  35. data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +19 -6
  36. data/ext/libdatadog_api/datadog_ruby_common.c +110 -0
  37. data/ext/libdatadog_api/datadog_ruby_common.h +57 -0
  38. data/ext/libdatadog_api/extconf.rb +108 -0
  39. data/ext/libdatadog_api/macos_development.md +26 -0
  40. data/ext/libdatadog_extconf_helpers.rb +130 -0
  41. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +49 -0
  42. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +73 -0
  43. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +68 -0
  44. data/lib/datadog/appsec/contrib/graphql/integration.rb +41 -0
  45. data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
  46. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
  47. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  48. data/lib/datadog/appsec/processor/actions.rb +1 -1
  49. data/lib/datadog/appsec/response.rb +15 -1
  50. data/lib/datadog/appsec.rb +1 -0
  51. data/lib/datadog/core/configuration/components.rb +14 -12
  52. data/lib/datadog/core/configuration/settings.rb +54 -7
  53. data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
  54. data/lib/datadog/core/crashtracking/component.rb +111 -0
  55. data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
  56. data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
  57. data/lib/datadog/core/telemetry/component.rb +49 -2
  58. data/lib/datadog/core/telemetry/emitter.rb +9 -11
  59. data/lib/datadog/core/telemetry/event.rb +32 -1
  60. data/lib/datadog/core/telemetry/ext.rb +1 -0
  61. data/lib/datadog/core/telemetry/http/adapters/net.rb +10 -12
  62. data/lib/datadog/core/telemetry/http/ext.rb +3 -0
  63. data/lib/datadog/core/telemetry/http/transport.rb +38 -9
  64. data/lib/datadog/core/telemetry/logging.rb +35 -0
  65. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  66. data/lib/datadog/kit/appsec/events.rb +2 -4
  67. data/lib/datadog/opentelemetry/sdk/span_processor.rb +10 -0
  68. data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
  69. data/lib/datadog/profiling/collectors/code_provenance.rb +7 -7
  70. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +17 -17
  71. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
  72. data/lib/datadog/profiling/collectors/info.rb +3 -3
  73. data/lib/datadog/profiling/collectors/thread_context.rb +4 -2
  74. data/lib/datadog/profiling/component.rb +69 -91
  75. data/lib/datadog/profiling/exporter.rb +3 -3
  76. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
  77. data/lib/datadog/profiling/ext.rb +21 -21
  78. data/lib/datadog/profiling/flush.rb +1 -1
  79. data/lib/datadog/profiling/http_transport.rb +8 -6
  80. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  81. data/lib/datadog/profiling/preload.rb +1 -1
  82. data/lib/datadog/profiling/profiler.rb +5 -8
  83. data/lib/datadog/profiling/scheduler.rb +31 -25
  84. data/lib/datadog/profiling/tag_builder.rb +2 -2
  85. data/lib/datadog/profiling/tasks/exec.rb +5 -5
  86. data/lib/datadog/profiling/tasks/setup.rb +16 -35
  87. data/lib/datadog/profiling.rb +4 -5
  88. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -0
  89. data/lib/datadog/tracing/contrib/ext.rb +14 -0
  90. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +1 -1
  91. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +4 -1
  92. data/lib/datadog/tracing/contrib/lograge/patcher.rb +16 -0
  93. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  94. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
  95. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  96. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
  97. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
  98. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  99. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
  100. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
  101. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
  102. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
  103. data/lib/datadog/tracing/metadata/errors.rb +9 -1
  104. data/lib/datadog/tracing/metadata/ext.rb +4 -0
  105. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  106. data/lib/datadog/tracing/span.rb +9 -2
  107. data/lib/datadog/tracing/span_event.rb +41 -0
  108. data/lib/datadog/tracing/span_operation.rb +6 -2
  109. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
  110. data/lib/datadog/version.rb +1 -1
  111. metadata +28 -10
  112. data/lib/datadog/profiling/crashtracker.rb +0 -91
  113. data/lib/datadog/profiling/ext/forking.rb +0 -98
@@ -1,11 +1,16 @@
1
1
  # rubocop:disable Style/StderrPuts
2
2
  # rubocop:disable Style/GlobalVars
3
3
 
4
- require_relative 'native_extension_helpers'
4
+ require_relative "native_extension_helpers"
5
+ require_relative "../libdatadog_extconf_helpers"
5
6
 
6
7
  SKIPPED_REASON_FILE = "#{__dir__}/skipped_reason.txt".freeze
7
8
  # Not a problem if the file doesn't exist or we can't delete it
8
- File.delete(SKIPPED_REASON_FILE) rescue nil
9
+ begin
10
+ File.delete(SKIPPED_REASON_FILE)
11
+ rescue
12
+ nil
13
+ end
9
14
 
10
15
  def skip_building_extension!(reason)
11
16
  fail_install_if_missing_extension =
@@ -24,13 +29,13 @@ def skip_building_extension!(reason)
24
29
  )
25
30
 
26
31
  if fail_install_if_missing_extension
27
- require 'mkmf'
32
+ require "mkmf"
28
33
  Logging.message(
29
- '[datadog] Failure cause: ' \
34
+ "[datadog] Failure cause: " \
30
35
  "#{Datadog::Profiling::NativeExtensionHelpers::Supported.render_skipped_reason_file(**reason)}\n"
31
36
  )
32
37
  else
33
- File.write('Makefile', 'all install clean: # dummy makefile that does nothing')
38
+ File.write("Makefile", "all install clean: # dummy makefile that does nothing")
34
39
  end
35
40
 
36
41
  exit
@@ -63,17 +68,17 @@ $stderr.puts(
63
68
 
64
69
  # NOTE: we MUST NOT require 'mkmf' before we check the #skip_building_extension? because the require triggers checks
65
70
  # that may fail on an environment not properly setup for building Ruby extensions.
66
- require 'mkmf'
71
+ require "mkmf"
67
72
 
68
73
  Logging.message("[datadog] Using compiler:\n")
69
- xsystem("#{CONFIG['CC']} -v")
74
+ xsystem("#{CONFIG["CC"]} -v")
70
75
  Logging.message("[datadog] End of compiler information\n")
71
76
 
72
77
  # mkmf on modern Rubies actually has an append_cflags that does something similar
73
78
  # (see https://github.com/ruby/ruby/pull/5760), but as usual we need a bit more boilerplate to deal with legacy Rubies
74
79
  def add_compiler_flag(flag)
75
80
  if try_cflags(flag)
76
- $CFLAGS << ' ' << flag
81
+ $CFLAGS << " " << flag
77
82
  else
78
83
  $stderr.puts("WARNING: '#{flag}' not accepted by compiler, skipping it")
79
84
  end
@@ -81,23 +86,23 @@ end
81
86
 
82
87
  # Because we can't control what compiler versions our customers use, shipping with -Werror by default is a no-go.
83
88
  # But we can enable it in CI, so that we quickly spot any new warnings that just got introduced.
84
- add_compiler_flag '-Werror' if ENV['DATADOG_GEM_CI'] == 'true'
89
+ add_compiler_flag "-Werror" if ENV["DATADOG_GEM_CI"] == "true"
85
90
 
86
91
  # Older gcc releases may not default to C99 and we need to ask for this. This is also used:
87
92
  # * by upstream Ruby -- search for gnu99 in the codebase
88
93
  # * by msgpack, another datadog gem dependency
89
94
  # (https://github.com/msgpack/msgpack-ruby/blob/18ce08f6d612fe973843c366ac9a0b74c4e50599/ext/msgpack/extconf.rb#L8)
90
- add_compiler_flag '-std=gnu99'
95
+ add_compiler_flag "-std=gnu99"
91
96
 
92
97
  # Gets really noisy when we include the MJIT header, let's omit it (TODO: Use #pragma GCC diagnostic instead?)
93
- add_compiler_flag '-Wno-unused-function'
98
+ add_compiler_flag "-Wno-unused-function"
94
99
 
95
100
  # Allow defining variables at any point in a function
96
- add_compiler_flag '-Wno-declaration-after-statement'
101
+ add_compiler_flag "-Wno-declaration-after-statement"
97
102
 
98
103
  # If we forget to include a Ruby header, the function call may still appear to work, but then
99
104
  # cause a segfault later. Let's ensure that never happens.
100
- add_compiler_flag '-Werror-implicit-function-declaration'
105
+ add_compiler_flag "-Werror-implicit-function-declaration"
101
106
 
102
107
  # The native extension is not intended to expose any symbols/functions for other native libraries to use;
103
108
  # the sole exception being `Init_datadog_profiling_native_extension` which needs to be visible for Ruby to call it when
@@ -105,22 +110,22 @@ add_compiler_flag '-Werror-implicit-function-declaration'
105
110
  #
106
111
  # By setting this compiler flag, we tell it to assume that everything is private unless explicitly stated.
107
112
  # For more details see https://gcc.gnu.org/wiki/Visibility
108
- add_compiler_flag '-fvisibility=hidden'
113
+ add_compiler_flag "-fvisibility=hidden"
109
114
 
110
115
  # Avoid legacy C definitions
111
- add_compiler_flag '-Wold-style-definition'
116
+ add_compiler_flag "-Wold-style-definition"
112
117
 
113
118
  # Enable all other compiler warnings
114
- add_compiler_flag '-Wall'
115
- add_compiler_flag '-Wextra'
119
+ add_compiler_flag "-Wall"
120
+ add_compiler_flag "-Wextra"
116
121
 
117
- if ENV['DDTRACE_DEBUG']
118
- $defs << '-DDD_DEBUG'
119
- CONFIG['optflags'] = '-O0'
120
- CONFIG['debugflags'] = '-ggdb3'
122
+ if ENV["DDTRACE_DEBUG"] == "true"
123
+ $defs << "-DDD_DEBUG"
124
+ CONFIG["optflags"] = "-O0"
125
+ CONFIG["debugflags"] = "-ggdb3"
121
126
  end
122
127
 
123
- if RUBY_PLATFORM.include?('linux')
128
+ if RUBY_PLATFORM.include?("linux")
124
129
  # Supposedly, the correct way to do this is
125
130
  # ```
126
131
  # have_library 'pthread'
@@ -128,77 +133,77 @@ if RUBY_PLATFORM.include?('linux')
128
133
  # ```
129
134
  # but it's slower to build
130
135
  # so instead we just assume that we have the function we need on Linux, and nowhere else
131
- $defs << '-DHAVE_PTHREAD_GETCPUCLOCKID'
136
+ $defs << "-DHAVE_PTHREAD_GETCPUCLOCKID"
137
+
138
+ # Not available on macOS
139
+ $defs << "-DHAVE_CLOCK_MONOTONIC_COARSE"
132
140
  end
133
141
 
134
- have_func 'malloc_stats'
142
+ have_func "malloc_stats"
135
143
 
136
144
  # On older Rubies, rb_postponed_job_preregister/rb_postponed_job_trigger did not exist
137
- $defs << '-DNO_POSTPONED_TRIGGER' if RUBY_VERSION < '3.3'
145
+ $defs << "-DNO_POSTPONED_TRIGGER" if RUBY_VERSION < "3.3"
138
146
 
139
147
  # On older Rubies, M:N threads were not available
140
- $defs << '-DNO_MN_THREADS_AVAILABLE' if RUBY_VERSION < '3.3'
148
+ $defs << "-DNO_MN_THREADS_AVAILABLE" if RUBY_VERSION < "3.3"
141
149
 
142
150
  # On older Rubies, we did not need to include the ractor header (this was built into the MJIT header)
143
- $defs << '-DNO_RACTOR_HEADER_INCLUDE' if RUBY_VERSION < '3.3'
151
+ $defs << "-DNO_RACTOR_HEADER_INCLUDE" if RUBY_VERSION < "3.3"
144
152
 
145
153
  # On older Rubies, some of the Ractor internal APIs were directly accessible
146
- $defs << '-DUSE_RACTOR_INTERNAL_APIS_DIRECTLY' if RUBY_VERSION < '3.3'
154
+ $defs << "-DUSE_RACTOR_INTERNAL_APIS_DIRECTLY" if RUBY_VERSION < "3.3"
147
155
 
148
156
  # On older Rubies, there was no struct rb_native_thread. See private_vm_api_acccess.c for details.
149
- $defs << '-DNO_RB_NATIVE_THREAD' if RUBY_VERSION < '3.2'
157
+ $defs << "-DNO_RB_NATIVE_THREAD" if RUBY_VERSION < "3.2"
150
158
 
151
159
  # On older Rubies, there was no struct rb_thread_sched (it was struct rb_global_vm_lock_struct)
152
- $defs << '-DNO_RB_THREAD_SCHED' if RUBY_VERSION < '3.2'
160
+ $defs << "-DNO_RB_THREAD_SCHED" if RUBY_VERSION < "3.2"
153
161
 
154
162
  # On older Rubies, the first_lineno inside a location was a VALUE and not a int (https://github.com/ruby/ruby/pull/6430)
155
- $defs << '-DNO_INT_FIRST_LINENO' if RUBY_VERSION < '3.2'
163
+ $defs << "-DNO_INT_FIRST_LINENO" if RUBY_VERSION < "3.2"
156
164
 
157
165
  # On older Rubies, "pop" was not a primitive operation
158
- $defs << '-DNO_PRIMITIVE_POP' if RUBY_VERSION < '3.2'
166
+ $defs << "-DNO_PRIMITIVE_POP" if RUBY_VERSION < "3.2"
159
167
 
160
168
  # On older Rubies, there was no tid member in the internal thread structure
161
- $defs << '-DNO_THREAD_TID' if RUBY_VERSION < '3.1'
169
+ $defs << "-DNO_THREAD_TID" if RUBY_VERSION < "3.1"
162
170
 
163
171
  # On older Rubies, there was no jit_return member on the rb_control_frame_t struct
164
- $defs << '-DNO_JIT_RETURN' if RUBY_VERSION < '3.1'
172
+ $defs << "-DNO_JIT_RETURN" if RUBY_VERSION < "3.1"
165
173
 
166
174
  # On older Rubies, rb_gc_force_recycle allowed to free objects in a way that
167
175
  # would be invisible to free tracepoints, finalizers and without cleaning
168
176
  # obj_to_id_tbl mappings.
169
- $defs << '-DHAVE_WORKING_RB_GC_FORCE_RECYCLE' if RUBY_VERSION < '3.1'
170
-
171
- # On older Rubies, we need to use a backported version of this function. See private_vm_api_access.h for details.
172
- $defs << '-DUSE_BACKPORTED_RB_PROFILE_FRAME_METHOD_NAME' if RUBY_VERSION < '3'
177
+ $defs << "-DHAVE_WORKING_RB_GC_FORCE_RECYCLE" if RUBY_VERSION < "3.1"
173
178
 
174
179
  # On older Rubies, there are no Ractors
175
- $defs << '-DNO_RACTORS' if RUBY_VERSION < '3'
180
+ $defs << "-DNO_RACTORS" if RUBY_VERSION < "3"
176
181
 
177
182
  # On older Rubies, rb_imemo_name did not exist
178
- $defs << '-DNO_IMEMO_NAME' if RUBY_VERSION < '3'
183
+ $defs << "-DNO_IMEMO_NAME" if RUBY_VERSION < "3"
179
184
 
180
185
  # On older Rubies, objects would not move
181
- $defs << '-DNO_T_MOVED' if RUBY_VERSION < '2.7'
186
+ $defs << "-DNO_T_MOVED" if RUBY_VERSION < "2.7"
182
187
 
183
188
  # On older Rubies, there was no RUBY_SEEN_OBJ_ID flag
184
- $defs << '-DNO_SEEN_OBJ_ID_FLAG' if RUBY_VERSION < '2.7'
189
+ $defs << "-DNO_SEEN_OBJ_ID_FLAG" if RUBY_VERSION < "2.7"
185
190
 
186
191
  # On older Rubies, rb_global_vm_lock_struct did not include the owner field
187
- $defs << '-DNO_GVL_OWNER' if RUBY_VERSION < '2.6'
192
+ $defs << "-DNO_GVL_OWNER" if RUBY_VERSION < "2.6"
188
193
 
189
194
  # On older Rubies, there was no thread->invoke_arg
190
- $defs << '-DNO_THREAD_INVOKE_ARG' if RUBY_VERSION < '2.6'
195
+ $defs << "-DNO_THREAD_INVOKE_ARG" if RUBY_VERSION < "2.6"
191
196
 
192
197
  # If we got here, libdatadog is available and loaded
193
- ENV['PKG_CONFIG_PATH'] = "#{ENV['PKG_CONFIG_PATH']}:#{Libdatadog.pkgconfig_folder}"
194
- Logging.message("[datadog] PKG_CONFIG_PATH set to #{ENV['PKG_CONFIG_PATH'].inspect}\n")
198
+ ENV["PKG_CONFIG_PATH"] = "#{ENV["PKG_CONFIG_PATH"]}:#{Libdatadog.pkgconfig_folder}"
199
+ Logging.message("[datadog] PKG_CONFIG_PATH set to #{ENV["PKG_CONFIG_PATH"].inspect}\n")
195
200
  $stderr.puts("Using libdatadog #{Libdatadog::VERSION} from #{Libdatadog.pkgconfig_folder}")
196
201
 
197
- unless pkg_config('datadog_profiling_with_rpath')
202
+ unless pkg_config("datadog_profiling_with_rpath")
198
203
  Logging.message("[datadog] Ruby detected the pkg-config command is #{$PKGCONFIG.inspect}\n")
199
204
 
200
205
  skip_building_extension!(
201
- if Datadog::Profiling::NativeExtensionHelpers::Supported.pkg_config_missing?
206
+ if Datadog::LibdatadogExtconfHelpers.pkg_config_missing?
202
207
  Datadog::Profiling::NativeExtensionHelpers::Supported::PKG_CONFIG_IS_MISSING
203
208
  else
204
209
  # Less specific error message
@@ -207,7 +212,7 @@ unless pkg_config('datadog_profiling_with_rpath')
207
212
  )
208
213
  end
209
214
 
210
- unless have_type('atomic_int', ['stdatomic.h'])
215
+ unless have_type("atomic_int", ["stdatomic.h"])
211
216
  skip_building_extension!(Datadog::Profiling::NativeExtensionHelpers::Supported::COMPILER_ATOMIC_MISSING)
212
217
  end
213
218
 
@@ -215,8 +220,8 @@ end
215
220
  # The extremely excessive escaping around ORIGIN below seems to be correct and was determined after a lot of
216
221
  # experimentation. We need to get these special characters across a lot of tools untouched...
217
222
  extra_relative_rpaths = [
218
- Datadog::Profiling::NativeExtensionHelpers.libdatadog_folder_relative_to_native_lib_folder,
219
- *Datadog::Profiling::NativeExtensionHelpers.libdatadog_folder_relative_to_ruby_extensions_folders,
223
+ Datadog::LibdatadogExtconfHelpers.libdatadog_folder_relative_to_native_lib_folder(current_folder: __dir__),
224
+ *Datadog::LibdatadogExtconfHelpers.libdatadog_folder_relative_to_ruby_extensions_folders,
220
225
  ]
221
226
  extra_relative_rpaths.each { |folder| $LDFLAGS += " -Wl,-rpath,$$$\\\\{ORIGIN\\}/#{folder.to_str}" }
222
227
  Logging.message("[datadog] After pkg-config $LDFLAGS were set to: #{$LDFLAGS.inspect}\n")
@@ -237,8 +242,8 @@ if Datadog::Profiling::NativeExtensionHelpers::CAN_USE_MJIT_HEADER
237
242
  # use the MJIT header.
238
243
  # Finally, the `COMMON_HEADERS` conflict with the MJIT header so we need to temporarily disable them for this check.
239
244
  original_common_headers = MakeMakefile::COMMON_HEADERS
240
- MakeMakefile::COMMON_HEADERS = ''.freeze
241
- unless have_macro('RUBY_MJIT_H', mjit_header_file_name)
245
+ MakeMakefile::COMMON_HEADERS = "".freeze
246
+ unless have_macro("RUBY_MJIT_H", mjit_header_file_name)
242
247
  skip_building_extension!(Datadog::Profiling::NativeExtensionHelpers::Supported::COMPILATION_BROKEN)
243
248
  end
244
249
  MakeMakefile::COMMON_HEADERS = original_common_headers
@@ -250,7 +255,7 @@ if Datadog::Profiling::NativeExtensionHelpers::CAN_USE_MJIT_HEADER
250
255
 
251
256
  # Warn on unused parameters to functions. Use `DDTRACE_UNUSED` to mark things as known-to-not-be-used.
252
257
  # See the comment on the same flag below for why this is done last.
253
- add_compiler_flag '-Wunused-parameter'
258
+ add_compiler_flag "-Wunused-parameter"
254
259
 
255
260
  create_makefile EXTENSION_NAME
256
261
  else
@@ -261,23 +266,23 @@ else
261
266
 
262
267
  create_header
263
268
 
264
- require 'debase/ruby_core_source'
265
- dir_config('ruby') # allow user to pass in non-standard core include directory
269
+ require "debase/ruby_core_source"
270
+ dir_config("ruby") # allow user to pass in non-standard core include directory
266
271
 
267
272
  Debase::RubyCoreSource
268
273
  .create_makefile_with_core(
269
274
  proc do
270
275
  headers_available =
271
- have_header('vm_core.h') &&
272
- have_header('iseq.h') &&
273
- (RUBY_VERSION < '3.3' || have_header('ractor_core.h'))
276
+ have_header("vm_core.h") &&
277
+ have_header("iseq.h") &&
278
+ (RUBY_VERSION < "3.3" || have_header("ractor_core.h"))
274
279
 
275
280
  if headers_available
276
281
  # Warn on unused parameters to functions. Use `DDTRACE_UNUSED` to mark things as known-to-not-be-used.
277
282
  # This is added as late as possible because in some Rubies we support (e.g. 3.3), adding this flag before
278
283
  # checking if internal VM headers are available causes those checks to fail because of this warning (and not
279
284
  # because the headers are not available.)
280
- add_compiler_flag '-Wunused-parameter'
285
+ add_compiler_flag "-Wunused-parameter"
281
286
  end
282
287
 
283
288
  headers_available
@@ -166,6 +166,12 @@ struct heap_recorder {
166
166
  size_t objects_frozen;
167
167
  } stats_last_update;
168
168
  };
169
+
170
+ struct end_heap_allocation_args {
171
+ struct heap_recorder *heap_recorder;
172
+ ddog_prof_Slice_Location locations;
173
+ };
174
+
169
175
  static heap_record* get_or_create_heap_record(heap_recorder*, ddog_prof_Slice_Location);
170
176
  static void cleanup_heap_record_if_unused(heap_recorder*, heap_record*);
171
177
  static void on_committed_object_record_cleanup(heap_recorder *heap_recorder, object_record *record);
@@ -176,6 +182,7 @@ static int st_object_records_iterate(st_data_t, st_data_t, st_data_t);
176
182
  static int st_object_records_debug(st_data_t key, st_data_t value, st_data_t extra);
177
183
  static int update_object_record_entry(st_data_t*, st_data_t*, st_data_t, int);
178
184
  static void commit_recording(heap_recorder*, heap_record*, recording);
185
+ static VALUE end_heap_allocation_recording(VALUE end_heap_allocation_args);
179
186
 
180
187
  // ==========================
181
188
  // Heap Recorder External API
@@ -219,7 +226,7 @@ void heap_recorder_free(heap_recorder *heap_recorder) {
219
226
  st_foreach(heap_recorder->heap_records, st_heap_record_entry_free, 0);
220
227
  st_free_table(heap_recorder->heap_records);
221
228
 
222
- if (heap_recorder->active_recording.object_record != NULL) {
229
+ if (heap_recorder->active_recording.object_record != NULL && heap_recorder->active_recording.object_record != &SKIPPED_RECORD) {
223
230
  // If there's a partial object record, clean it up as well
224
231
  object_record_free(heap_recorder->active_recording.object_record);
225
232
  }
@@ -340,9 +347,28 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj
340
347
  };
341
348
  }
342
349
 
343
- void end_heap_allocation_recording(struct heap_recorder *heap_recorder, ddog_prof_Slice_Location locations) {
350
+ // end_heap_allocation_recording_with_rb_protect gets called while the stack_recorder is holding one of the profile
351
+ // locks. To enable us to correctly unlock the profile on exception, we wrap the call to end_heap_allocation_recording
352
+ // with an rb_protect.
353
+ __attribute__((warn_unused_result))
354
+ int end_heap_allocation_recording_with_rb_protect(struct heap_recorder *heap_recorder, ddog_prof_Slice_Location locations) {
355
+ int exception_state;
356
+ struct end_heap_allocation_args end_heap_allocation_args = {
357
+ .heap_recorder = heap_recorder,
358
+ .locations = locations,
359
+ };
360
+ rb_protect(end_heap_allocation_recording, (VALUE) &end_heap_allocation_args, &exception_state);
361
+ return exception_state;
362
+ }
363
+
364
+ static VALUE end_heap_allocation_recording(VALUE end_heap_allocation_args) {
365
+ struct end_heap_allocation_args *args = (struct end_heap_allocation_args *) end_heap_allocation_args;
366
+
367
+ struct heap_recorder *heap_recorder = args->heap_recorder;
368
+ ddog_prof_Slice_Location locations = args->locations;
369
+
344
370
  if (heap_recorder == NULL) {
345
- return;
371
+ return Qnil;
346
372
  }
347
373
 
348
374
  recording active_recording = heap_recorder->active_recording;
@@ -356,15 +382,16 @@ void end_heap_allocation_recording(struct heap_recorder *heap_recorder, ddog_pro
356
382
  // data required for committing though.
357
383
  heap_recorder->active_recording = (recording) {0};
358
384
 
359
- if (active_recording.object_record == &SKIPPED_RECORD) {
360
- // special marker when we decided to skip due to sampling
361
- return;
385
+ if (active_recording.object_record == &SKIPPED_RECORD) { // special marker when we decided to skip due to sampling
386
+ return Qnil;
362
387
  }
363
388
 
364
389
  heap_record *heap_record = get_or_create_heap_record(heap_recorder, locations);
365
390
 
366
391
  // And then commit the new allocation.
367
392
  commit_recording(heap_recorder, heap_record, active_recording);
393
+
394
+ return Qnil;
368
395
  }
369
396
 
370
397
  void heap_recorder_prepare_iteration(heap_recorder *heap_recorder) {
@@ -815,6 +842,7 @@ VALUE object_record_inspect(object_record *record) {
815
842
  if (!ruby_ref_from_id(LONG2NUM(record->obj_id), &ref)) {
816
843
  rb_str_catf(inspect, "object=<invalid>");
817
844
  } else {
845
+ rb_str_catf(inspect, "value=%p ", (void *) ref);
818
846
  VALUE ruby_inspect = ruby_safe_inspect(ref);
819
847
  if (ruby_inspect != Qnil) {
820
848
  rb_str_catf(inspect, "object=%"PRIsVALUE, ruby_inspect);
@@ -114,7 +114,9 @@ void start_heap_allocation_recording(heap_recorder *heap_recorder, VALUE new_obj
114
114
  // @param locations The stacktrace representing the location of the allocation.
115
115
  //
116
116
  // WARN: It is illegal to call this without previously having called ::start_heap_allocation_recording.
117
- void end_heap_allocation_recording(heap_recorder *heap_recorder, ddog_prof_Slice_Location locations);
117
+ // WARN: This method rescues exceptions with `rb_protect`, returning the exception state integer for the caller to handle.
118
+ __attribute__((warn_unused_result))
119
+ int end_heap_allocation_recording_with_rb_protect(heap_recorder *heap_recorder, ddog_prof_Slice_Location locations);
118
120
 
119
121
  // Update the heap recorder to reflect the latest state of the VM and prepare internal structures
120
122
  // for efficient iteration.
@@ -2,22 +2,11 @@
2
2
 
3
3
  #include <stdint.h>
4
4
 
5
- // Used to mark symbols to be exported to the outside of the extension.
6
- // Consider very carefully before tagging a function with this.
7
- #define DDTRACE_EXPORT __attribute__ ((visibility ("default")))
8
-
9
- // Used to mark function arguments that are deliberately left unused
10
- #ifdef __GNUC__
11
- #define DDTRACE_UNUSED __attribute__((unused))
12
- #else
13
- #define DDTRACE_UNUSED
14
- #endif
15
-
16
5
  // @ivoanjo: After trying to read through https://stackoverflow.com/questions/3437404/min-and-max-in-c I decided I
17
6
  // don't like C and I just implemented this as a function.
18
- inline static uint64_t uint64_max_of(uint64_t a, uint64_t b) { return a > b ? a : b; }
19
- inline static uint64_t uint64_min_of(uint64_t a, uint64_t b) { return a > b ? b : a; }
20
- inline static long long_max_of(long a, long b) { return a > b ? a : b; }
21
- inline static long long_min_of(long a, long b) { return a > b ? b : a; }
22
- inline static double double_max_of(double a, double b) { return a > b ? a : b; }
23
- inline static double double_min_of(double a, double b) { return a > b ? b : a; }
7
+ static inline uint64_t uint64_max_of(uint64_t a, uint64_t b) { return a > b ? a : b; }
8
+ static inline uint64_t uint64_min_of(uint64_t a, uint64_t b) { return a > b ? b : a; }
9
+ static inline long long_max_of(long a, long b) { return a > b ? a : b; }
10
+ static inline long long_min_of(long a, long b) { return a > b ? b : a; }
11
+ static inline double double_max_of(double a, double b) { return a > b ? a : b; }
12
+ static inline double double_min_of(double a, double b) { return a > b ? b : a; }
@@ -21,7 +21,7 @@ struct call_exporter_without_gvl_arguments {
21
21
  bool send_ran;
22
22
  };
23
23
 
24
- inline static ddog_ByteSlice byte_slice_from_ruby_string(VALUE string);
24
+ static inline ddog_ByteSlice byte_slice_from_ruby_string(VALUE string);
25
25
  static VALUE _native_validate_exporter(VALUE self, VALUE exporter_configuration);
26
26
  static ddog_prof_Exporter_NewResult create_exporter(VALUE exporter_configuration, VALUE tags_as_array);
27
27
  static VALUE handle_exporter_failure(ddog_prof_Exporter_NewResult exporter_result);
@@ -53,11 +53,11 @@ void http_transport_init(VALUE profiling_module) {
53
53
  ok_symbol = ID2SYM(rb_intern_const("ok"));
54
54
  error_symbol = ID2SYM(rb_intern_const("error"));
55
55
 
56
- library_version_string = ddtrace_version();
56
+ library_version_string = datadog_gem_version();
57
57
  rb_global_variable(&library_version_string);
58
58
  }
59
59
 
60
- inline static ddog_ByteSlice byte_slice_from_ruby_string(VALUE string) {
60
+ static inline ddog_ByteSlice byte_slice_from_ruby_string(VALUE string) {
61
61
  ENFORCE_TYPE(string, T_STRING);
62
62
  ddog_ByteSlice byte_slice = {.ptr = (uint8_t *) StringValuePtr(string), .len = RSTRING_LEN(string)};
63
63
  return byte_slice;
@@ -2,8 +2,6 @@
2
2
 
3
3
  #include <ruby.h>
4
4
 
5
- static VALUE log_failure_to_process_tag(VALUE err_details);
6
-
7
5
  const char *ruby_value_type_to_string(enum ruby_value_type type) {
8
6
  return ruby_value_type_to_char_slice(type).ptr;
9
7
  }
@@ -62,87 +60,3 @@ size_t read_ddogerr_string_and_drop(ddog_Error *error, char *string, size_t capa
62
60
  ddog_Error_drop(error);
63
61
  return error_msg_size;
64
62
  }
65
-
66
- __attribute__((warn_unused_result))
67
- ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration) {
68
- ENFORCE_TYPE(exporter_configuration, T_ARRAY);
69
-
70
- VALUE exporter_working_mode = rb_ary_entry(exporter_configuration, 0);
71
- ENFORCE_TYPE(exporter_working_mode, T_SYMBOL);
72
- ID working_mode = SYM2ID(exporter_working_mode);
73
-
74
- ID agentless_id = rb_intern("agentless");
75
- ID agent_id = rb_intern("agent");
76
-
77
- if (working_mode != agentless_id && working_mode != agent_id) {
78
- rb_raise(rb_eArgError, "Failed to initialize transport: Unexpected working mode, expected :agentless or :agent");
79
- }
80
-
81
- if (working_mode == agentless_id) {
82
- VALUE site = rb_ary_entry(exporter_configuration, 1);
83
- VALUE api_key = rb_ary_entry(exporter_configuration, 2);
84
- ENFORCE_TYPE(site, T_STRING);
85
- ENFORCE_TYPE(api_key, T_STRING);
86
-
87
- return ddog_prof_Endpoint_agentless(char_slice_from_ruby_string(site), char_slice_from_ruby_string(api_key));
88
- } else { // agent_id
89
- VALUE base_url = rb_ary_entry(exporter_configuration, 1);
90
- ENFORCE_TYPE(base_url, T_STRING);
91
-
92
- return ddog_prof_Endpoint_agent(char_slice_from_ruby_string(base_url));
93
- }
94
- }
95
-
96
- __attribute__((warn_unused_result))
97
- ddog_Vec_Tag convert_tags(VALUE tags_as_array) {
98
- ENFORCE_TYPE(tags_as_array, T_ARRAY);
99
-
100
- long tags_count = RARRAY_LEN(tags_as_array);
101
- ddog_Vec_Tag tags = ddog_Vec_Tag_new();
102
-
103
- for (long i = 0; i < tags_count; i++) {
104
- VALUE name_value_pair = rb_ary_entry(tags_as_array, i);
105
-
106
- if (!RB_TYPE_P(name_value_pair, T_ARRAY)) {
107
- ddog_Vec_Tag_drop(tags);
108
- ENFORCE_TYPE(name_value_pair, T_ARRAY);
109
- }
110
-
111
- // Note: We can index the array without checking its size first because rb_ary_entry returns Qnil if out of bounds
112
- VALUE tag_name = rb_ary_entry(name_value_pair, 0);
113
- VALUE tag_value = rb_ary_entry(name_value_pair, 1);
114
-
115
- if (!(RB_TYPE_P(tag_name, T_STRING) && RB_TYPE_P(tag_value, T_STRING))) {
116
- ddog_Vec_Tag_drop(tags);
117
- ENFORCE_TYPE(tag_name, T_STRING);
118
- ENFORCE_TYPE(tag_value, T_STRING);
119
- }
120
-
121
- ddog_Vec_Tag_PushResult push_result =
122
- ddog_Vec_Tag_push(&tags, char_slice_from_ruby_string(tag_name), char_slice_from_ruby_string(tag_value));
123
-
124
- if (push_result.tag == DDOG_VEC_TAG_PUSH_RESULT_ERR) {
125
- // libdatadog validates tags and may catch invalid tags that ddtrace didn't actually catch.
126
- // We warn users about such tags, and then just ignore them.
127
-
128
- int exception_state;
129
- rb_protect(log_failure_to_process_tag, get_error_details_and_drop(&push_result.err), &exception_state);
130
-
131
- // Since we are calling into Ruby code, it may raise an exception. Ensure that dynamically-allocated tags
132
- // get cleaned before propagating the exception.
133
- if (exception_state) {
134
- ddog_Vec_Tag_drop(tags);
135
- rb_jump_tag(exception_state); // "Re-raise" exception
136
- }
137
- }
138
- }
139
-
140
- return tags;
141
- }
142
-
143
- static VALUE log_failure_to_process_tag(VALUE err_details) {
144
- VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
145
- VALUE logger = rb_funcall(datadog_module, rb_intern("logger"), 0);
146
-
147
- return rb_funcall(logger, rb_intern("warn"), 1, rb_sprintf("Failed to add tag to profiling request: %"PRIsVALUE, err_details));
148
- }
@@ -3,27 +3,10 @@
3
3
  #include <datadog/profiling.h>
4
4
  #include "ruby_helpers.h"
5
5
 
6
- inline static ddog_CharSlice char_slice_from_ruby_string(VALUE string) {
7
- ENFORCE_TYPE(string, T_STRING);
8
- ddog_CharSlice char_slice = {.ptr = StringValuePtr(string), .len = RSTRING_LEN(string)};
9
- return char_slice;
10
- }
11
-
12
- inline static VALUE ruby_string_from_vec_u8(ddog_Vec_U8 string) {
6
+ static inline VALUE ruby_string_from_vec_u8(ddog_Vec_U8 string) {
13
7
  return rb_str_new((char *) string.ptr, string.len);
14
8
  }
15
9
 
16
- inline static VALUE ruby_string_from_error(const ddog_Error *error) {
17
- ddog_CharSlice char_slice = ddog_Error_message(error);
18
- return rb_str_new(char_slice.ptr, char_slice.len);
19
- }
20
-
21
- inline static VALUE get_error_details_and_drop(ddog_Error *error) {
22
- VALUE result = ruby_string_from_error(error);
23
- ddog_Error_drop(error);
24
- return result;
25
- }
26
-
27
10
  // Utility function to be able to extract an error cstring from a ddog_Error.
28
11
  // Returns the amount of characters written to string (which are necessarily
29
12
  // bounded by capacity - 1 since the string will be null-terminated).
@@ -37,10 +20,6 @@ ddog_CharSlice ruby_value_type_to_char_slice(enum ruby_value_type type);
37
20
 
38
21
  // Returns a dynamically allocated string from the provided char slice.
39
22
  // WARN: The returned string must be explicitly freed with ruby_xfree.
40
- inline static char* string_from_char_slice(ddog_CharSlice slice) {
23
+ static inline char* string_from_char_slice(ddog_CharSlice slice) {
41
24
  return ruby_strndup(slice.ptr, slice.len);
42
25
  }
43
-
44
- ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration);
45
-
46
- ddog_Vec_Tag convert_tags(VALUE tags_as_array);