datadog 2.2.0 → 2.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +51 -2
- data/ext/datadog_profiling_loader/extconf.rb +15 -15
- data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
- data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
- data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +113 -43
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +49 -26
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
- data/ext/datadog_profiling_native_extension/collectors_stack.c +49 -37
- data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +81 -19
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +110 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +57 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +65 -60
- data/ext/datadog_profiling_native_extension/heap_recorder.c +34 -6
- data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
- data/ext/datadog_profiling_native_extension/helpers.h +6 -17
- data/ext/datadog_profiling_native_extension/http_transport.c +3 -3
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -172
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +64 -138
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +17 -11
- data/ext/datadog_profiling_native_extension/profiling.c +0 -2
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +14 -2
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
- data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
- data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
- data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +19 -6
- data/ext/libdatadog_api/datadog_ruby_common.c +110 -0
- data/ext/libdatadog_api/datadog_ruby_common.h +57 -0
- data/ext/libdatadog_api/extconf.rb +108 -0
- data/ext/libdatadog_api/macos_development.md +26 -0
- data/ext/libdatadog_extconf_helpers.rb +130 -0
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +49 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +73 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +68 -0
- data/lib/datadog/appsec/contrib/graphql/integration.rb +41 -0
- data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
- data/lib/datadog/appsec/processor/actions.rb +1 -1
- data/lib/datadog/appsec/response.rb +15 -1
- data/lib/datadog/appsec.rb +1 -0
- data/lib/datadog/core/configuration/components.rb +14 -12
- data/lib/datadog/core/configuration/settings.rb +54 -7
- data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
- data/lib/datadog/core/crashtracking/component.rb +111 -0
- data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
- data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
- data/lib/datadog/core/telemetry/component.rb +49 -2
- data/lib/datadog/core/telemetry/emitter.rb +9 -11
- data/lib/datadog/core/telemetry/event.rb +32 -1
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- data/lib/datadog/core/telemetry/http/adapters/net.rb +10 -12
- data/lib/datadog/core/telemetry/http/ext.rb +3 -0
- data/lib/datadog/core/telemetry/http/transport.rb +38 -9
- data/lib/datadog/core/telemetry/logging.rb +35 -0
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
- data/lib/datadog/kit/appsec/events.rb +2 -4
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +10 -0
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +7 -7
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +17 -17
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
- data/lib/datadog/profiling/collectors/info.rb +3 -3
- data/lib/datadog/profiling/collectors/thread_context.rb +4 -2
- data/lib/datadog/profiling/component.rb +69 -91
- data/lib/datadog/profiling/exporter.rb +3 -3
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
- data/lib/datadog/profiling/ext.rb +21 -21
- data/lib/datadog/profiling/flush.rb +1 -1
- data/lib/datadog/profiling/http_transport.rb +8 -6
- data/lib/datadog/profiling/load_native_extension.rb +5 -5
- data/lib/datadog/profiling/preload.rb +1 -1
- data/lib/datadog/profiling/profiler.rb +5 -8
- data/lib/datadog/profiling/scheduler.rb +31 -25
- data/lib/datadog/profiling/tag_builder.rb +2 -2
- data/lib/datadog/profiling/tasks/exec.rb +5 -5
- data/lib/datadog/profiling/tasks/setup.rb +16 -35
- data/lib/datadog/profiling.rb +4 -5
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -0
- data/lib/datadog/tracing/contrib/ext.rb +14 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +1 -1
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +4 -1
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +16 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
- data/lib/datadog/tracing/metadata/errors.rb +9 -1
- data/lib/datadog/tracing/metadata/ext.rb +4 -0
- data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
- data/lib/datadog/tracing/span.rb +9 -2
- data/lib/datadog/tracing/span_event.rb +41 -0
- data/lib/datadog/tracing/span_operation.rb +6 -2
- data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
- data/lib/datadog/version.rb +1 -1
- metadata +28 -10
- data/lib/datadog/profiling/crashtracker.rb +0 -91
- data/lib/datadog/profiling/ext/forking.rb +0 -98
@@ -1,123 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rubygems'
|
4
|
-
require 'pathname'
|
5
|
-
|
6
3
|
module Datadog
|
7
4
|
module Profiling
|
8
5
|
# Helpers for extconf.rb
|
9
6
|
module NativeExtensionHelpers
|
10
7
|
# Can be set when customers want to skip compiling the native extension entirely
|
11
|
-
ENV_NO_EXTENSION =
|
8
|
+
ENV_NO_EXTENSION = "DD_PROFILING_NO_EXTENSION"
|
12
9
|
# Can be set to force rubygems to fail gem installation when profiling extension could not be built
|
13
|
-
ENV_FAIL_INSTALL_IF_MISSING_EXTENSION =
|
10
|
+
ENV_FAIL_INSTALL_IF_MISSING_EXTENSION = "DD_PROFILING_FAIL_INSTALL_IF_MISSING_EXTENSION"
|
14
11
|
|
15
12
|
# The MJIT header was introduced on 2.6 and removed on 3.3; for other Rubies we rely on debase-ruby_core_source
|
16
|
-
CAN_USE_MJIT_HEADER = RUBY_VERSION.start_with?(
|
17
|
-
|
18
|
-
LIBDATADOG_VERSION = '~> 10.0.0.1.0'
|
13
|
+
CAN_USE_MJIT_HEADER = RUBY_VERSION.start_with?("2.6", "2.7", "3.0.", "3.1.", "3.2.")
|
19
14
|
|
20
15
|
def self.fail_install_if_missing_extension?
|
21
|
-
ENV[ENV_FAIL_INSTALL_IF_MISSING_EXTENSION].to_s.strip.downcase ==
|
22
|
-
end
|
23
|
-
|
24
|
-
# Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
|
25
|
-
# libdatadog are moved after the extension gets compiled.
|
26
|
-
#
|
27
|
-
# Because the libddpprof native library is installed on a non-standard system path, in order for it to be
|
28
|
-
# found by the system dynamic linker (e.g. what takes care of dlopen(), which is used to load the profiling
|
29
|
-
# native extension), we need to add a "runpath" -- a list of folders to search for libdatadog.
|
30
|
-
#
|
31
|
-
# This runpath gets hardcoded at native library linking time. You can look at it using the `readelf` tool in
|
32
|
-
# Linux: e.g. `readelf -d datadog_profiling_native_extension.2.7.3_x86_64-linux.so`.
|
33
|
-
#
|
34
|
-
# In older versions of the datadog gem, we only set as runpath an absolute path to libdatadog.
|
35
|
-
# (This gets set automatically by the call
|
36
|
-
# to `pkg_config('datadog_profiling_with_rpath')` in `extconf.rb`). This worked fine as long as libdatadog was **NOT**
|
37
|
-
# moved from the folder it was present at datadog gem installation/linking time.
|
38
|
-
#
|
39
|
-
# Unfortunately, environments such as Heroku and AWS Elastic Beanstalk move gems around in the filesystem after
|
40
|
-
# installation. Thus, the profiling native extension could not be loaded in these environments
|
41
|
-
# (see https://github.com/DataDog/dd-trace-rb/issues/2067) because libdatadog could not be found.
|
42
|
-
#
|
43
|
-
# To workaround this issue, this method computes the **relative** path between the folder where the profiling
|
44
|
-
# native extension is going to be installed and the folder where libdatadog is installed, and returns it
|
45
|
-
# to be set as an additional runpath. (Yes, you can set multiple runpath folders to be searched).
|
46
|
-
#
|
47
|
-
# This way, if both gems are moved together (and it turns out that they are in these environments),
|
48
|
-
# the relative path can still be traversed to find libdatadog.
|
49
|
-
#
|
50
|
-
# This is incredibly awful, and it's kinda bizarre how it's not possible to just find these paths at runtime
|
51
|
-
# and set them correctly; rather than needing to set stuff at linking-time and then praying to $deity that
|
52
|
-
# weird moves don't happen.
|
53
|
-
#
|
54
|
-
# As a curiosity, `LD_LIBRARY_PATH` can be used to influence the folders that get searched but **CANNOT BE
|
55
|
-
# SET DYNAMICALLY**, e.g. it needs to be set at the start of the process (Ruby VM) and thus it's not something
|
56
|
-
# we could setup when doing a `require`.
|
57
|
-
#
|
58
|
-
def self.libdatadog_folder_relative_to_native_lib_folder(
|
59
|
-
current_folder: __dir__,
|
60
|
-
libdatadog_pkgconfig_folder: Libdatadog.pkgconfig_folder
|
61
|
-
)
|
62
|
-
return unless libdatadog_pkgconfig_folder
|
63
|
-
|
64
|
-
profiling_native_lib_folder = "#{current_folder}/../../lib/"
|
65
|
-
libdatadog_lib_folder = "#{libdatadog_pkgconfig_folder}/../"
|
66
|
-
|
67
|
-
Pathname.new(libdatadog_lib_folder).relative_path_from(Pathname.new(profiling_native_lib_folder)).to_s
|
68
|
-
end
|
69
|
-
|
70
|
-
# In https://github.com/DataDog/dd-trace-rb/pull/3582 we got a report of a customer for which the native extension
|
71
|
-
# only got installed into the extensions folder.
|
72
|
-
#
|
73
|
-
# But then this fix was not enough to fully get them moving because then they started to see the issue from
|
74
|
-
# https://github.com/DataDog/dd-trace-rb/issues/2067 / https://github.com/DataDog/dd-trace-rb/pull/2125 :
|
75
|
-
#
|
76
|
-
# > Profiling was requested but is not supported, profiling disabled: There was an error loading the profiling
|
77
|
-
# > native extension due to 'RuntimeError Failure to load datadog_profiling_native_extension.3.2.2_x86_64-linux
|
78
|
-
# > due to libdatadog_profiling.so: cannot open shared object file: No such file or directory
|
79
|
-
#
|
80
|
-
# The problem is that when loading the native extension from the extensions directory, the relative rpath we add
|
81
|
-
# with the #libdatadog_folder_relative_to_native_lib_folder helper above is not correct, we need to add a relative
|
82
|
-
# rpath to the extensions directory.
|
83
|
-
#
|
84
|
-
# So how do we find the full path where the native extension is placed?
|
85
|
-
# * From https://github.com/ruby/ruby/blob/83f02d42e0a3c39661dc99c049ab9a70ff227d5b/lib/bundler/runtime.rb#L166
|
86
|
-
# `extension_dirs = Dir["#{Gem.dir}/extensions/*/*/*"] + Dir["#{Gem.dir}/bundler/gems/extensions/*/*/*"]`
|
87
|
-
# we get that's in one of two fixed subdirectories of `Gem.dir`
|
88
|
-
# * From https://github.com/ruby/ruby/blob/83f02d42e0a3c39661dc99c049ab9a70ff227d5b/lib/rubygems/basic_specification.rb#L111-L115
|
89
|
-
# we get the structure of the subdirectory (platform/extension_api_version/gem_and_version)
|
90
|
-
#
|
91
|
-
# Thus, `Gem.dir` of `/var/app/current/vendor/bundle/ruby/3.2.0` becomes (for instance)
|
92
|
-
# `/var/app/current/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/datadog-2.0.0/` or
|
93
|
-
# `/var/app/current/vendor/bundle/ruby/3.2.0/bundler/gems/extensions/x86_64-linux/3.2.0/datadog-2.0.0/`
|
94
|
-
#
|
95
|
-
# We then compute the relative path between these folders and the libdatadog folder, and use that as a relative path.
|
96
|
-
def self.libdatadog_folder_relative_to_ruby_extensions_folders(
|
97
|
-
gem_dir: Gem.dir,
|
98
|
-
libdatadog_pkgconfig_folder: Libdatadog.pkgconfig_folder
|
99
|
-
)
|
100
|
-
return unless libdatadog_pkgconfig_folder
|
101
|
-
|
102
|
-
# For the purposes of calculating a folder relative to the other, we don't actually NEED to fill in the
|
103
|
-
# platform, extension_api_version and gem version. We're basically just after how many folders it is deep from
|
104
|
-
# the Gem.dir.
|
105
|
-
expected_ruby_extensions_folders = [
|
106
|
-
"#{gem_dir}/extensions/platform/extension_api_version/datadog_version/",
|
107
|
-
"#{gem_dir}/bundler/gems/extensions/platform/extension_api_version/datadog_version/",
|
108
|
-
]
|
109
|
-
libdatadog_lib_folder = "#{libdatadog_pkgconfig_folder}/../"
|
110
|
-
|
111
|
-
expected_ruby_extensions_folders.map do |folder|
|
112
|
-
Pathname.new(libdatadog_lib_folder).relative_path_from(Pathname.new(folder)).to_s
|
113
|
-
end
|
16
|
+
ENV[ENV_FAIL_INSTALL_IF_MISSING_EXTENSION].to_s.strip.downcase == "true"
|
114
17
|
end
|
115
18
|
|
116
19
|
# Used to check if profiler is supported, including user-visible clear messages explaining why their
|
117
20
|
# system may not be supported.
|
118
21
|
module Supported
|
119
22
|
private_class_method def self.explain_issue(*reason, suggested:)
|
120
|
-
{
|
23
|
+
{reason: reason, suggested: suggested}
|
121
24
|
end
|
122
25
|
|
123
26
|
def self.supported?
|
@@ -143,16 +46,16 @@ module Datadog
|
|
143
46
|
outcome =
|
144
47
|
if fail_install
|
145
48
|
[
|
146
|
-
|
49
|
+
"Failing installation immediately because the ",
|
147
50
|
"`#{ENV_FAIL_INSTALL_IF_MISSING_EXTENSION}` environment variable is set",
|
148
|
-
|
149
|
-
|
150
|
-
|
51
|
+
"to `true`.",
|
52
|
+
"When contacting support, please include the <mkmf.log> file that is shown ",
|
53
|
+
"below.",
|
151
54
|
]
|
152
55
|
else
|
153
56
|
[
|
154
|
-
|
155
|
-
|
57
|
+
"The Datadog Continuous Profiler will not be available,",
|
58
|
+
"but all other datadog features will work fine!",
|
156
59
|
]
|
157
60
|
end
|
158
61
|
|
@@ -170,22 +73,14 @@ module Datadog
|
|
170
73
|
|
171
74
|
# This will be saved in a file to later be presented while operating the gem
|
172
75
|
def self.render_skipped_reason_file(reason:, suggested:)
|
173
|
-
[*reason, *suggested].join(
|
174
|
-
end
|
175
|
-
|
176
|
-
# mkmf sets $PKGCONFIG after the `pkg_config` gets used in extconf.rb. When `pkg_config` is unsuccessful, we use
|
177
|
-
# this helper to decide if we can show more specific error message vs a generic "something went wrong".
|
178
|
-
def self.pkg_config_missing?(command: $PKGCONFIG) # rubocop:disable Style/GlobalVars
|
179
|
-
pkg_config_available = command && xsystem("#{command} --version")
|
180
|
-
|
181
|
-
pkg_config_available != true
|
76
|
+
[*reason, *suggested].join(" ")
|
182
77
|
end
|
183
78
|
|
184
79
|
CONTACT_SUPPORT = [
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
80
|
+
"For help solving this issue, please contact Datadog support at",
|
81
|
+
"<https://docs.datadoghq.com/help/>.",
|
82
|
+
"You can also check out the Continuous Profiler troubleshooting page at",
|
83
|
+
"<https://dtdg.co/ruby-profiler-troubleshooting>."
|
189
84
|
].freeze
|
190
85
|
|
191
86
|
GET_IN_TOUCH = [
|
@@ -193,83 +88,83 @@ module Datadog
|
|
193
88
|
].freeze
|
194
89
|
|
195
90
|
UPGRADE_RUBY = [
|
196
|
-
|
91
|
+
"Upgrade to a modern Ruby to enable profiling for your app."
|
197
92
|
].freeze
|
198
93
|
|
199
94
|
# Validation for this check is done in extconf.rb because it relies on mkmf
|
200
95
|
FAILED_TO_CONFIGURE_LIBDATADOG = explain_issue(
|
201
|
-
|
96
|
+
"there was a problem in setting up the `libdatadog` dependency.",
|
202
97
|
suggested: CONTACT_SUPPORT,
|
203
98
|
)
|
204
99
|
|
205
100
|
# Validation for this check is done in extconf.rb because it relies on mkmf
|
206
101
|
COMPILATION_BROKEN = explain_issue(
|
207
|
-
|
208
|
-
|
102
|
+
"compilation of the Ruby VM just-in-time header failed.",
|
103
|
+
"Your C compiler or Ruby VM just-in-time compiler seem to be broken.",
|
209
104
|
suggested: CONTACT_SUPPORT,
|
210
105
|
)
|
211
106
|
|
212
107
|
# Validation for this check is done in extconf.rb because it relies on mkmf
|
213
108
|
PKG_CONFIG_IS_MISSING = explain_issue(
|
214
109
|
# ----------------------------------------------------------------------------+
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
110
|
+
"the `pkg-config` system tool is missing.",
|
111
|
+
"This issue can usually be fixed by installing one of the following:",
|
112
|
+
"the `pkg-config` package on Homebrew and Debian/Ubuntu-based Linux;",
|
113
|
+
"the `pkgconf` package on Arch and Alpine-based Linux;",
|
114
|
+
"the `pkgconf-pkg-config` package on Fedora/Red Hat-based Linux.",
|
115
|
+
"(Tip: When fixing this, ensure `pkg-config` is installed **before**",
|
116
|
+
"running `bundle install`, and remember to clear any installed gems cache).",
|
222
117
|
suggested: CONTACT_SUPPORT,
|
223
118
|
)
|
224
119
|
|
225
120
|
# Validation for this check is done in extconf.rb because it relies on mkmf
|
226
121
|
COMPILER_ATOMIC_MISSING = explain_issue(
|
227
|
-
|
228
|
-
|
229
|
-
|
122
|
+
"your C compiler is missing support for the <stdatomic.h> header.",
|
123
|
+
"This issue can usually be fixed by upgrading to a later version of your",
|
124
|
+
"operating system image or compiler.",
|
230
125
|
suggested: CONTACT_SUPPORT,
|
231
126
|
)
|
232
127
|
|
233
128
|
private_class_method def self.disabled_via_env?
|
234
129
|
report_disabled = [
|
235
|
-
|
236
|
-
|
130
|
+
"If you needed to use this, please tell us why on",
|
131
|
+
"<https://github.com/DataDog/dd-trace-rb/issues/new> so we can fix it :)",
|
237
132
|
].freeze
|
238
133
|
|
239
134
|
disabled_via_env = explain_issue(
|
240
|
-
|
241
|
-
|
135
|
+
"the `DD_PROFILING_NO_EXTENSION` environment variable is/was set to",
|
136
|
+
"`true` during installation.",
|
242
137
|
suggested: report_disabled,
|
243
138
|
)
|
244
139
|
|
245
|
-
return unless ENV[ENV_NO_EXTENSION].to_s.strip.downcase ==
|
140
|
+
return unless ENV[ENV_NO_EXTENSION].to_s.strip.downcase == "true"
|
246
141
|
|
247
142
|
disabled_via_env
|
248
143
|
end
|
249
144
|
|
250
145
|
private_class_method def self.on_jruby?
|
251
146
|
jruby_not_supported = explain_issue(
|
252
|
-
|
147
|
+
"JRuby is not supported by the Datadog Continuous Profiler.",
|
253
148
|
suggested: GET_IN_TOUCH,
|
254
149
|
)
|
255
150
|
|
256
|
-
jruby_not_supported if RUBY_ENGINE ==
|
151
|
+
jruby_not_supported if RUBY_ENGINE == "jruby"
|
257
152
|
end
|
258
153
|
|
259
154
|
private_class_method def self.on_truffleruby?
|
260
155
|
truffleruby_not_supported = explain_issue(
|
261
|
-
|
156
|
+
"TruffleRuby is not supported by the datadog gem.",
|
262
157
|
suggested: GET_IN_TOUCH,
|
263
158
|
)
|
264
159
|
|
265
|
-
truffleruby_not_supported if RUBY_ENGINE ==
|
160
|
+
truffleruby_not_supported if RUBY_ENGINE == "truffleruby"
|
266
161
|
end
|
267
162
|
|
268
163
|
# See https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#microsoft-windows-support for current
|
269
164
|
# state of Windows support in the datadog gem.
|
270
165
|
private_class_method def self.on_windows?
|
271
166
|
windows_not_supported = explain_issue(
|
272
|
-
|
167
|
+
"Microsoft Windows is not supported by the Datadog Continuous Profiler.",
|
273
168
|
suggested: GET_IN_TOUCH,
|
274
169
|
)
|
275
170
|
|
@@ -278,72 +173,66 @@ module Datadog
|
|
278
173
|
|
279
174
|
private_class_method def self.on_macos?
|
280
175
|
macos_not_supported = explain_issue(
|
281
|
-
|
176
|
+
"macOS is currently not supported by the Datadog Continuous Profiler.",
|
282
177
|
suggested: GET_IN_TOUCH,
|
283
178
|
)
|
284
179
|
# For development only; not supported otherwise
|
285
|
-
macos_testing_override = ENV[
|
180
|
+
macos_testing_override = ENV["DD_PROFILING_MACOS_TESTING"] == "true"
|
286
181
|
|
287
|
-
macos_not_supported if RUBY_PLATFORM.include?(
|
182
|
+
macos_not_supported if RUBY_PLATFORM.include?("darwin") && !macos_testing_override
|
288
183
|
end
|
289
184
|
|
290
185
|
private_class_method def self.on_unknown_os?
|
291
186
|
unknown_os_not_supported = explain_issue(
|
292
|
-
|
187
|
+
"your operating system is not supported by the Datadog Continuous Profiler.",
|
293
188
|
suggested: GET_IN_TOUCH,
|
294
189
|
)
|
295
190
|
|
296
|
-
unknown_os_not_supported unless RUBY_PLATFORM.include?(
|
191
|
+
unknown_os_not_supported unless RUBY_PLATFORM.include?("darwin") || RUBY_PLATFORM.include?("linux")
|
297
192
|
end
|
298
193
|
|
299
194
|
private_class_method def self.on_unsupported_cpu_arch?
|
300
195
|
architecture_not_supported = explain_issue(
|
301
|
-
|
196
|
+
"your CPU architecture is not supported by the Datadog Continuous Profiler.",
|
302
197
|
suggested: GET_IN_TOUCH,
|
303
198
|
)
|
304
199
|
|
305
|
-
architecture_not_supported unless RUBY_PLATFORM.start_with?(
|
200
|
+
architecture_not_supported unless RUBY_PLATFORM.start_with?("x86_64", "aarch64", "arm64")
|
306
201
|
end
|
307
202
|
|
308
203
|
# On some Rubies, we require the mjit header to be present. If Ruby was installed without MJIT support, we also skip
|
309
204
|
# building the extension.
|
310
205
|
private_class_method def self.expected_to_use_mjit_but_mjit_is_disabled?
|
311
206
|
ruby_without_mjit = explain_issue(
|
312
|
-
|
313
|
-
|
314
|
-
|
207
|
+
"your Ruby has been compiled without JIT support (--disable-jit-support).",
|
208
|
+
"The profiling native extension requires a Ruby compiled with JIT support,",
|
209
|
+
"even if the JIT is not in use by the application itself.",
|
315
210
|
suggested: CONTACT_SUPPORT,
|
316
211
|
)
|
317
212
|
|
318
|
-
ruby_without_mjit if CAN_USE_MJIT_HEADER && RbConfig::CONFIG[
|
213
|
+
ruby_without_mjit if CAN_USE_MJIT_HEADER && RbConfig::CONFIG["MJIT_SUPPORT"] != "yes"
|
319
214
|
end
|
320
215
|
|
321
216
|
private_class_method def self.libdatadog_not_available?
|
322
|
-
|
323
|
-
gem 'libdatadog', LIBDATADOG_VERSION
|
324
|
-
require 'libdatadog'
|
325
|
-
nil
|
326
|
-
# rubocop:disable Lint/RescueException
|
327
|
-
rescue Exception => e
|
217
|
+
Datadog::LibdatadogExtconfHelpers.try_loading_libdatadog do |exception|
|
328
218
|
explain_issue(
|
329
|
-
|
330
|
-
|
331
|
-
*
|
332
|
-
*Array(
|
333
|
-
|
219
|
+
"there was an exception during loading of the `libdatadog` gem:",
|
220
|
+
exception.class.name,
|
221
|
+
*exception.message.split("\n"),
|
222
|
+
*Array(exception.backtrace),
|
223
|
+
".",
|
334
224
|
suggested: CONTACT_SUPPORT,
|
335
225
|
)
|
336
226
|
end
|
337
|
-
# rubocop:enable Lint/RescueException
|
338
227
|
end
|
339
228
|
|
340
229
|
private_class_method def self.libdatadog_not_usable?
|
341
230
|
no_binaries_for_current_platform = explain_issue(
|
342
|
-
|
343
|
-
|
231
|
+
"the `libdatadog` gem installed on your system is missing binaries for your",
|
232
|
+
"platform variant.",
|
344
233
|
"(Your platform: `#{Libdatadog.current_platform}`)",
|
345
|
-
|
346
|
-
"`#{Libdatadog.available_binaries.join(
|
234
|
+
"(Available binaries:",
|
235
|
+
"`#{Libdatadog.available_binaries.join("`, `")}`)",
|
347
236
|
suggested: CONTACT_SUPPORT,
|
348
237
|
)
|
349
238
|
|
@@ -311,7 +311,7 @@ VALUE thread_name_for(VALUE thread) {
|
|
311
311
|
// with diagnostic stuff. See https://nelkinda.com/blog/suppress-warnings-in-gcc-and-clang/#d11e364 for details.
|
312
312
|
#pragma GCC diagnostic push
|
313
313
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
314
|
-
inline
|
314
|
+
static inline int
|
315
315
|
calc_pos(const rb_iseq_t *iseq, const VALUE *pc, int *lineno, int *node_id)
|
316
316
|
{
|
317
317
|
VM_ASSERT(iseq);
|
@@ -364,7 +364,7 @@ calc_pos(const rb_iseq_t *iseq, const VALUE *pc, int *lineno, int *node_id)
|
|
364
364
|
// Copyright (C) 1993-2012 Yukihiro Matsumoto
|
365
365
|
// to support our custom rb_profile_frames (see below)
|
366
366
|
// Modifications: None
|
367
|
-
inline
|
367
|
+
static inline int
|
368
368
|
calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
|
369
369
|
{
|
370
370
|
int lineno;
|
@@ -376,8 +376,8 @@ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
|
|
376
376
|
// Copyright (C) 1993-2012 Yukihiro Matsumoto
|
377
377
|
// Modifications:
|
378
378
|
// * Renamed rb_profile_frames => ddtrace_rb_profile_frames
|
379
|
-
// * Add thread argument
|
380
|
-
// * Add is_ruby_frame argument
|
379
|
+
// * Add thread argument (this is now upstream, actually!)
|
380
|
+
// * Add frame_flags.is_ruby_frame argument
|
381
381
|
// * Removed `if (lines)` tests -- require/assume that like `buff`, `lines` is always specified
|
382
382
|
// * Skip dummy frame that shows up in main thread
|
383
383
|
// * Add `end_cfp == NULL` and `end_cfp <= cfp` safety checks. These are used in a bunch of places in
|
@@ -392,6 +392,9 @@ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
|
|
392
392
|
// was called from.
|
393
393
|
// * Imported fix from https://github.com/ruby/ruby/pull/7116 to avoid sampling threads that are still being created
|
394
394
|
// * Imported fix from https://github.com/ruby/ruby/pull/8415 to avoid potential crash when using YJIT.
|
395
|
+
// * Add frame_flags.same_frame and logic to skip redoing work if the buffer already contains the same data we're collecting
|
396
|
+
// * Skipped use of rb_callable_method_entry_t (cme) for Ruby frames as it doesn't impact us.
|
397
|
+
// * Imported fix from https://github.com/ruby/ruby/pull/8280 to keep us closer to upstream
|
395
398
|
//
|
396
399
|
// What is rb_profile_frames?
|
397
400
|
// `rb_profile_frames` is a Ruby VM debug API added for use by profilers for sampling the stack trace of a Ruby thread.
|
@@ -421,8 +424,7 @@ calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
|
|
421
424
|
// and friends). We've found quite a few situations where the data from rb_profile_frames and the reference APIs
|
422
425
|
// disagree, and quite a few of them seem oversights/bugs (speculation from my part) rather than deliberate
|
423
426
|
// decisions.
|
424
|
-
int ddtrace_rb_profile_frames(VALUE thread, int start, int limit,
|
425
|
-
{
|
427
|
+
int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, frame_info *stack_buffer) {
|
426
428
|
int i;
|
427
429
|
// Modified from upstream: Instead of using `GET_EC` to collect info from the current thread,
|
428
430
|
// support sampling any thread (including the current) passed as an argument
|
@@ -466,7 +468,7 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, VALUE *buff, i
|
|
466
468
|
// See comment on `record_placeholder_stack_in_native_code` for a full explanation of what this means (and why we don't just return 0)
|
467
469
|
if (end_cfp <= cfp) return PLACEHOLDER_STACK_IN_NATIVE_CODE;
|
468
470
|
|
469
|
-
for (i=0; i<limit && cfp != end_cfp;) {
|
471
|
+
for (i=0; i<limit && cfp != end_cfp; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) {
|
470
472
|
if (cfp->iseq && !cfp->pc) {
|
471
473
|
// Fix: Do nothing -- this frame should not be used
|
472
474
|
//
|
@@ -479,164 +481,88 @@ int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, VALUE *buff, i
|
|
479
481
|
continue;
|
480
482
|
}
|
481
483
|
|
482
|
-
|
483
|
-
|
484
|
+
stack_buffer[i].same_frame =
|
485
|
+
stack_buffer[i].is_ruby_frame &&
|
486
|
+
stack_buffer[i].as.ruby_frame.iseq == (VALUE) cfp->iseq &&
|
487
|
+
stack_buffer[i].as.ruby_frame.caching_pc == cfp->pc;
|
484
488
|
|
485
|
-
if (
|
486
|
-
|
487
|
-
|
488
|
-
// "when sampling an eval/instance eval inside an object" spec.
|
489
|
-
//
|
490
|
-
// Longer note:
|
491
|
-
// When a frame is a ruby frame (VM_FRAME_RUBYFRAME_P above), we can get information about it
|
492
|
-
// by introspecting both the callable method entry, as well as the iseq directly.
|
493
|
-
// Often they match... but sometimes they provide different info (as in the "iseq for an eval" situation
|
494
|
-
// here).
|
495
|
-
// If my reading of vm_backtrace.c is correct, the actual Ruby stack trace API **never** uses the
|
496
|
-
// callable method entry for Ruby frames, but only for VM_METHOD_TYPE_CFUNC (see `backtrace_each` method
|
497
|
-
// on that file).
|
498
|
-
// So... why does `rb_profile_frames` do something different? Is it a bug? Is it because it exposes
|
499
|
-
// more information than the Ruby stack frame API?
|
500
|
-
// As a final note, the `backtracie` gem (https://github.com/ivoanjo/backtracie) can be used to introspect
|
501
|
-
// the full metadata provided by both the callable method entry as well as the iseq, and is really useful
|
502
|
-
// to debug and learn more about these differences.
|
503
|
-
cfp->iseq->body->type != ISEQ_TYPE_EVAL) {
|
504
|
-
buff[i] = (VALUE)cme;
|
505
|
-
}
|
506
|
-
else {
|
507
|
-
buff[i] = (VALUE)cfp->iseq;
|
489
|
+
if (stack_buffer[i].same_frame) { // Nothing to do, buffer already contains this frame
|
490
|
+
i++;
|
491
|
+
continue;
|
508
492
|
}
|
509
493
|
|
494
|
+
// dd-trace-rb NOTE:
|
495
|
+
// Upstream Ruby has code here to retrieve the rb_callable_method_entry_t (cme) and in some cases to use it
|
496
|
+
// instead of the iseq.
|
497
|
+
// In practice, they are usually the same; the difference is that when you have e.g. block, one gets you a
|
498
|
+
// reference to the block, and the other to the method containing the block.
|
499
|
+
// This would be important if we used `rb_profile_frame_label` and wanted the "block in foo" label instead
|
500
|
+
// of just "foo". But we're currently using `rb_profile_frame_base_label` which I believe is always the same
|
501
|
+
// between the rb_callable_method_entry_t and the iseq. Thus, to simplify a bit our logic and reduce a bit
|
502
|
+
// the overhead, we always use the iseq here.
|
503
|
+
//
|
504
|
+
// @ivoanjo: I've left the upstream Ruby code commented out below for reference, so it's more obvious that
|
505
|
+
// we're diverging, and we can easily compare and experiment with the upstream version in the future.
|
506
|
+
//
|
507
|
+
// cme = rb_vm_frame_method_entry(cfp);
|
508
|
+
|
509
|
+
// if (cme && cme->def->type == VM_METHOD_TYPE_ISEQ &&
|
510
|
+
// // Fix: Do not use callable method entry when iseq is for an eval.
|
511
|
+
// // TL;DR: This fix is needed for us to match the Ruby reference API information in the
|
512
|
+
// // "when sampling an eval/instance eval inside an object" spec.
|
513
|
+
// cfp->iseq->body->type != ISEQ_TYPE_EVAL) {
|
514
|
+
// buff[i] = (VALUE)cme;
|
515
|
+
// }
|
516
|
+
// else {
|
517
|
+
stack_buffer[i].as.ruby_frame.iseq = (VALUE)cfp->iseq;
|
518
|
+
stack_buffer[i].as.ruby_frame.caching_pc = (void *) cfp->pc;
|
519
|
+
// }
|
520
|
+
|
510
521
|
// The topmost frame may not have an updated PC because the JIT
|
511
522
|
// may not have set one. The JIT compiler will update the PC
|
512
523
|
// before entering a new function (so that `caller` will work),
|
513
524
|
// so only the topmost frame could possibly have an out of date PC
|
514
525
|
#ifndef NO_JIT_RETURN
|
515
526
|
if (cfp == top && cfp->jit_return) {
|
516
|
-
|
527
|
+
stack_buffer[i].as.ruby_frame.line = 0;
|
517
528
|
} else {
|
518
|
-
|
529
|
+
stack_buffer[i].as.ruby_frame.line = calc_lineno(cfp->iseq, cfp->pc);
|
519
530
|
}
|
520
531
|
#else // Ruby < 3.1
|
521
|
-
|
532
|
+
stack_buffer[i].as.ruby_frame.line = calc_lineno(cfp->iseq, cfp->pc);
|
522
533
|
#endif
|
523
534
|
|
524
|
-
|
535
|
+
stack_buffer[i].is_ruby_frame = true;
|
525
536
|
i++;
|
526
537
|
}
|
527
538
|
else {
|
528
539
|
cme = rb_vm_frame_method_entry(cfp);
|
529
540
|
if (cme && cme->def->type == VM_METHOD_TYPE_CFUNC) {
|
530
|
-
|
531
|
-
|
532
|
-
|
541
|
+
if (start > 0) {
|
542
|
+
start--;
|
543
|
+
continue;
|
544
|
+
}
|
545
|
+
|
546
|
+
stack_buffer[i].same_frame =
|
547
|
+
!stack_buffer[i].is_ruby_frame &&
|
548
|
+
stack_buffer[i].as.native_frame.caching_cme == (VALUE) cme;
|
549
|
+
|
550
|
+
if (stack_buffer[i].same_frame) { // Nothing to do, buffer already contains this frame
|
551
|
+
i++;
|
552
|
+
continue;
|
553
|
+
}
|
554
|
+
|
555
|
+
stack_buffer[i].as.native_frame.caching_cme = (VALUE)cme;
|
556
|
+
stack_buffer[i].as.native_frame.method_id = cme->def->original_id;
|
557
|
+
stack_buffer[i].is_ruby_frame = false;
|
533
558
|
i++;
|
534
559
|
}
|
535
560
|
}
|
536
|
-
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
537
561
|
}
|
538
562
|
|
539
563
|
return i;
|
540
564
|
}
|
541
565
|
|
542
|
-
#ifdef USE_BACKPORTED_RB_PROFILE_FRAME_METHOD_NAME
|
543
|
-
|
544
|
-
// Taken from upstream vm_backtrace.c at commit 5f10bd634fb6ae8f74a4ea730176233b0ca96954 (March 2022, Ruby 3.2 trunk)
|
545
|
-
// Copyright (C) 1993-2012 Yukihiro Matsumoto
|
546
|
-
// to support our custom rb_profile_frame_method_name (see below)
|
547
|
-
// Modifications: None
|
548
|
-
static VALUE
|
549
|
-
id2str(ID id)
|
550
|
-
{
|
551
|
-
VALUE str = rb_id2str(id);
|
552
|
-
if (!str) return Qnil;
|
553
|
-
return str;
|
554
|
-
}
|
555
|
-
#define rb_id2str(id) id2str(id)
|
556
|
-
|
557
|
-
// Taken from upstream vm_backtrace.c at commit 5f10bd634fb6ae8f74a4ea730176233b0ca96954 (March 2022, Ruby 3.2 trunk)
|
558
|
-
// Copyright (C) 1993-2012 Yukihiro Matsumoto
|
559
|
-
// to support our custom rb_profile_frame_method_name (see below)
|
560
|
-
// Modifications: None
|
561
|
-
static const rb_iseq_t *
|
562
|
-
frame2iseq(VALUE frame)
|
563
|
-
{
|
564
|
-
if (NIL_P(frame)) return NULL;
|
565
|
-
|
566
|
-
if (RB_TYPE_P(frame, T_IMEMO)) {
|
567
|
-
switch (imemo_type(frame)) {
|
568
|
-
case imemo_iseq:
|
569
|
-
return (const rb_iseq_t *)frame;
|
570
|
-
case imemo_ment:
|
571
|
-
{
|
572
|
-
const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
|
573
|
-
switch (cme->def->type) {
|
574
|
-
case VM_METHOD_TYPE_ISEQ:
|
575
|
-
return cme->def->body.iseq.iseqptr;
|
576
|
-
default:
|
577
|
-
return NULL;
|
578
|
-
}
|
579
|
-
}
|
580
|
-
default:
|
581
|
-
break;
|
582
|
-
}
|
583
|
-
}
|
584
|
-
rb_bug("frame2iseq: unreachable");
|
585
|
-
}
|
586
|
-
|
587
|
-
// Taken from upstream vm_backtrace.c at commit 5f10bd634fb6ae8f74a4ea730176233b0ca96954 (March 2022, Ruby 3.2 trunk)
|
588
|
-
// Copyright (C) 1993-2012 Yukihiro Matsumoto
|
589
|
-
// to support our custom rb_profile_frame_method_name (see below)
|
590
|
-
// Modifications: None
|
591
|
-
static const rb_callable_method_entry_t *
|
592
|
-
cframe(VALUE frame)
|
593
|
-
{
|
594
|
-
if (NIL_P(frame)) return NULL;
|
595
|
-
|
596
|
-
if (RB_TYPE_P(frame, T_IMEMO)) {
|
597
|
-
switch (imemo_type(frame)) {
|
598
|
-
case imemo_ment:
|
599
|
-
{
|
600
|
-
const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
|
601
|
-
switch (cme->def->type) {
|
602
|
-
case VM_METHOD_TYPE_CFUNC:
|
603
|
-
return cme;
|
604
|
-
default:
|
605
|
-
return NULL;
|
606
|
-
}
|
607
|
-
}
|
608
|
-
default:
|
609
|
-
return NULL;
|
610
|
-
}
|
611
|
-
}
|
612
|
-
|
613
|
-
return NULL;
|
614
|
-
}
|
615
|
-
|
616
|
-
// Taken from upstream vm_backtrace.c at commit 5f10bd634fb6ae8f74a4ea730176233b0ca96954 (March 2022, Ruby 3.2 trunk)
|
617
|
-
// Copyright (C) 1993-2012 Yukihiro Matsumoto
|
618
|
-
//
|
619
|
-
// Ruby 3.0 finally added support for showing CFUNC frames (frames for methods written using native code)
|
620
|
-
// in stack traces gathered via `rb_profile_frames` (https://github.com/ruby/ruby/pull/3299).
|
621
|
-
// To access this information on older Rubies, beyond using our custom `ddtrace_rb_profile_frames` above, we also need
|
622
|
-
// to backport the Ruby 3.0+ version of `rb_profile_frame_method_name`.
|
623
|
-
//
|
624
|
-
// Modifications:
|
625
|
-
// * Renamed rb_profile_frame_method_name => ddtrace_rb_profile_frame_method_name
|
626
|
-
VALUE
|
627
|
-
ddtrace_rb_profile_frame_method_name(VALUE frame)
|
628
|
-
{
|
629
|
-
const rb_callable_method_entry_t *cme = cframe(frame);
|
630
|
-
if (cme) {
|
631
|
-
ID mid = cme->def->original_id;
|
632
|
-
return id2str(mid);
|
633
|
-
}
|
634
|
-
const rb_iseq_t *iseq = frame2iseq(frame);
|
635
|
-
return iseq ? rb_iseq_method_name(iseq) : Qnil;
|
636
|
-
}
|
637
|
-
|
638
|
-
#endif // USE_BACKPORTED_RB_PROFILE_FRAME_METHOD_NAME
|
639
|
-
|
640
566
|
// Support code for older Rubies that cannot use the MJIT header
|
641
567
|
#ifndef RUBY_MJIT_HEADER
|
642
568
|
|