datadog 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative 'trace/span'
|
4
4
|
require_relative '../../tracing/span_link'
|
5
|
+
require_relative '../../tracing/span_event'
|
5
6
|
require_relative '../../tracing/trace_digest'
|
6
7
|
|
7
8
|
module Datadog
|
@@ -31,6 +32,15 @@ module Datadog
|
|
31
32
|
#
|
32
33
|
# @param [Span] span the {Span} that just ended.
|
33
34
|
def on_finish(span)
|
35
|
+
unless span.events.nil?
|
36
|
+
span.datadog_span.span_events = span.events.map do |event|
|
37
|
+
Datadog::Tracing::SpanEvent.new(
|
38
|
+
event.name,
|
39
|
+
attributes: event.attributes,
|
40
|
+
time_unix_nano: event.timestamp
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
34
44
|
span.datadog_span.finish(ns_to_time(span.end_timestamp))
|
35
45
|
end
|
36
46
|
|
@@ -15,6 +15,29 @@ module Datadog
|
|
15
15
|
res
|
16
16
|
end
|
17
17
|
|
18
|
+
# Record an exception during the execution of this span. Multiple exceptions
|
19
|
+
# can be recorded on a span.
|
20
|
+
#
|
21
|
+
# @param [Exception] exception The exception to recorded
|
22
|
+
# @param [optional Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}]
|
23
|
+
# attributes One or more key:value pairs, where the keys must be
|
24
|
+
# strings and the values may be (array of) string, boolean or numeric
|
25
|
+
# type.
|
26
|
+
#
|
27
|
+
# @return [void]
|
28
|
+
def record_exception(exception, attributes: nil)
|
29
|
+
res = super
|
30
|
+
if (span = datadog_span)
|
31
|
+
# Sets the exception attributes as span error tags. The values in the attribute hash MUST
|
32
|
+
# take precedence over the type, message and stacktrace inferred from the exception object
|
33
|
+
type = attributes&.[]('exception.type') || exception.class.to_s
|
34
|
+
message = attributes&.[]('exception.message') || exception.message
|
35
|
+
stacktrace = attributes&.[]('exception.stacktrace') || exception.full_message(highlight: false, order: :top)
|
36
|
+
span.set_error_tags([type, message, stacktrace])
|
37
|
+
end
|
38
|
+
res
|
39
|
+
end
|
40
|
+
|
18
41
|
# `alias` performed to match {OpenTelemetry::SDK::Trace::Span} aliasing upstream
|
19
42
|
alias []= set_attribute
|
20
43
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "set"
|
4
|
+
require "json"
|
5
5
|
|
6
6
|
module Datadog
|
7
7
|
module Profiling
|
@@ -14,7 +14,7 @@ module Datadog
|
|
14
14
|
#
|
15
15
|
# This class acts both as a collector (collecting data) as well as a recorder (records/serializes it)
|
16
16
|
class CodeProvenance
|
17
|
-
def initialize(standard_library_path: RbConfig::CONFIG.fetch(
|
17
|
+
def initialize(standard_library_path: RbConfig::CONFIG.fetch("rubylibdir"))
|
18
18
|
@libraries_by_name = {}
|
19
19
|
@libraries_by_path = {}
|
20
20
|
@seen_files = Set.new
|
@@ -22,8 +22,8 @@ module Datadog
|
|
22
22
|
|
23
23
|
record_library(
|
24
24
|
Library.new(
|
25
|
-
kind:
|
26
|
-
name:
|
25
|
+
kind: "standard library",
|
26
|
+
name: "stdlib",
|
27
27
|
version: RUBY_VERSION,
|
28
28
|
path: standard_library_path,
|
29
29
|
)
|
@@ -79,7 +79,7 @@ module Datadog
|
|
79
79
|
loaded_specs.each do |spec|
|
80
80
|
next if libraries_by_name.key?(spec.name)
|
81
81
|
|
82
|
-
record_library(Library.new(kind:
|
82
|
+
record_library(Library.new(kind: "library", name: spec.name, version: spec.version, path: spec.gem_dir))
|
83
83
|
recorded_library = true
|
84
84
|
end
|
85
85
|
|
@@ -119,7 +119,7 @@ module Datadog
|
|
119
119
|
end
|
120
120
|
|
121
121
|
def to_json(arg = nil)
|
122
|
-
{
|
122
|
+
{kind: @kind, name: @name, version: @version, paths: @paths}.to_json(arg)
|
123
123
|
end
|
124
124
|
|
125
125
|
def path
|
@@ -21,6 +21,7 @@ module Datadog
|
|
21
21
|
thread_context_collector:,
|
22
22
|
dynamic_sampling_rate_overhead_target_percentage:,
|
23
23
|
allocation_profiling_enabled:,
|
24
|
+
allocation_counting_enabled:,
|
24
25
|
# **NOTE**: This should only be used for testing; disabling the dynamic sampling rate will increase the
|
25
26
|
# profiler overhead!
|
26
27
|
dynamic_sampling_rate_enabled: true,
|
@@ -29,7 +30,7 @@ module Datadog
|
|
29
30
|
)
|
30
31
|
unless dynamic_sampling_rate_enabled
|
31
32
|
Datadog.logger.warn(
|
32
|
-
|
33
|
+
"Profiling dynamic sampling rate disabled. This should only be used for testing, and will increase overhead!"
|
33
34
|
)
|
34
35
|
end
|
35
36
|
|
@@ -42,6 +43,7 @@ module Datadog
|
|
42
43
|
dynamic_sampling_rate_enabled,
|
43
44
|
dynamic_sampling_rate_overhead_target_percentage,
|
44
45
|
allocation_profiling_enabled,
|
46
|
+
allocation_counting_enabled,
|
45
47
|
skip_idle_samples_for_testing,
|
46
48
|
)
|
47
49
|
@worker_thread = nil
|
@@ -54,27 +56,25 @@ module Datadog
|
|
54
56
|
|
55
57
|
def start(on_failure_proc: nil)
|
56
58
|
@start_stop_mutex.synchronize do
|
57
|
-
return if @worker_thread
|
59
|
+
return if @worker_thread&.alive?
|
58
60
|
|
59
61
|
Datadog.logger.debug { "Starting thread for: #{self}" }
|
60
62
|
|
61
63
|
@idle_sampling_helper.start
|
62
64
|
|
63
65
|
@worker_thread = Thread.new do
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
on_failure_proc&.call
|
77
|
-
end
|
66
|
+
Thread.current.name = self.class.name
|
67
|
+
|
68
|
+
self.class._native_sampling_loop(self)
|
69
|
+
|
70
|
+
Datadog.logger.debug("CpuAndWallTimeWorker thread stopping cleanly")
|
71
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
72
|
+
@failure_exception = e
|
73
|
+
Datadog.logger.warn(
|
74
|
+
"CpuAndWallTimeWorker thread error. " \
|
75
|
+
"Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
|
76
|
+
)
|
77
|
+
on_failure_proc&.call
|
78
78
|
end
|
79
79
|
@worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
|
80
80
|
@worker_thread.thread_variable_set(:fork_safe, true)
|
@@ -85,7 +85,7 @@ module Datadog
|
|
85
85
|
|
86
86
|
def stop
|
87
87
|
@start_stop_mutex.synchronize do
|
88
|
-
Datadog.logger.debug(
|
88
|
+
Datadog.logger.debug("Requesting CpuAndWallTimeWorker thread shut down")
|
89
89
|
|
90
90
|
@idle_sampling_helper.stop
|
91
91
|
|
@@ -21,7 +21,7 @@ module Datadog
|
|
21
21
|
|
22
22
|
def start
|
23
23
|
@start_stop_mutex.synchronize do
|
24
|
-
return if @worker_thread
|
24
|
+
return if @worker_thread&.alive?
|
25
25
|
|
26
26
|
Datadog.logger.debug { "Starting thread for: #{self}" }
|
27
27
|
|
@@ -30,19 +30,17 @@ module Datadog
|
|
30
30
|
self.class._native_reset(self)
|
31
31
|
|
32
32
|
@worker_thread = Thread.new do
|
33
|
-
|
34
|
-
Thread.current.name = self.class.name
|
33
|
+
Thread.current.name = self.class.name
|
35
34
|
|
36
|
-
|
35
|
+
self.class._native_idle_sampling_loop(self)
|
37
36
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
37
|
+
Datadog.logger.debug("IdleSamplingHelper thread stopping cleanly")
|
38
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
39
|
+
@failure_exception = e
|
40
|
+
Datadog.logger.warn(
|
41
|
+
"IdleSamplingHelper thread error. " \
|
42
|
+
"Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
|
43
|
+
)
|
46
44
|
end
|
47
45
|
@worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
|
48
46
|
@worker_thread.thread_variable_set(:fork_safe, true)
|
@@ -53,7 +51,7 @@ module Datadog
|
|
53
51
|
|
54
52
|
def stop
|
55
53
|
@start_stop_mutex.synchronize do
|
56
|
-
Datadog.logger.debug(
|
54
|
+
Datadog.logger.debug("Requesting IdleSamplingHelper thread shut down")
|
57
55
|
|
58
56
|
return unless @worker_thread
|
59
57
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "set"
|
4
|
+
require "time"
|
5
5
|
|
6
6
|
module Datadog
|
7
7
|
module Profiling
|
@@ -61,7 +61,7 @@ module Datadog
|
|
61
61
|
|
62
62
|
def collect_profiler_info(settings)
|
63
63
|
unless @profiler_info
|
64
|
-
lib_datadog_gem = ::Gem.loaded_specs[
|
64
|
+
lib_datadog_gem = ::Gem.loaded_specs["libdatadog"]
|
65
65
|
@profiler_info = {
|
66
66
|
# TODO: If profiling is extracted and its version diverges from the datadog gem, this is inaccurate.
|
67
67
|
# Update if this ever occurs.
|
@@ -48,12 +48,14 @@ module Datadog
|
|
48
48
|
private
|
49
49
|
|
50
50
|
def safely_extract_context_key_from(tracer)
|
51
|
-
|
51
|
+
return unless tracer
|
52
|
+
|
53
|
+
provider = tracer.respond_to?(:provider) && tracer.provider
|
52
54
|
|
53
55
|
return unless provider
|
54
56
|
|
55
57
|
context = provider.instance_variable_get(:@context)
|
56
|
-
context
|
58
|
+
context&.instance_variable_get(:@key)
|
57
59
|
end
|
58
60
|
end
|
59
61
|
end
|
@@ -8,7 +8,7 @@ module Datadog
|
|
8
8
|
# * Code Hotspots panel in the trace viewer, as well as scoping a profile down to a span
|
9
9
|
# * Endpoint aggregation in the profiler UX, including normalization (resource per endpoint call)
|
10
10
|
def self.build_profiler_component(settings:, agent_settings:, optional_tracer:) # rubocop:disable Metrics/MethodLength
|
11
|
-
return [nil, {
|
11
|
+
return [nil, {profiling_enabled: false}] unless settings.profiling.enabled
|
12
12
|
|
13
13
|
# Workaround for weird dependency direction: the Core::Configuration::Components class currently has a
|
14
14
|
# dependency on individual products, in this case the Profiler.
|
@@ -27,9 +27,9 @@ module Datadog
|
|
27
27
|
# On the other hand, if datadog/core is loaded by a different product and no general `require 'datadog'` is
|
28
28
|
# done, then profiling may not be loaded, and thus to avoid this issue we do a require here (which is a
|
29
29
|
# no-op if profiling is already loaded).
|
30
|
-
require_relative
|
30
|
+
require_relative "../profiling"
|
31
31
|
|
32
|
-
return [nil, {
|
32
|
+
return [nil, {profiling_enabled: false}] unless Profiling.supported?
|
33
33
|
|
34
34
|
# Activate forking extensions
|
35
35
|
Profiling::Tasks::Setup.new.run
|
@@ -47,7 +47,7 @@ module Datadog
|
|
47
47
|
upload_period_seconds = [60, settings.profiling.advanced.upload_period_seconds].max
|
48
48
|
|
49
49
|
recorder = Datadog::Profiling::StackRecorder.new(
|
50
|
-
cpu_time_enabled: RUBY_PLATFORM.include?(
|
50
|
+
cpu_time_enabled: RUBY_PLATFORM.include?("linux"), # Only supported on Linux currently
|
51
51
|
alloc_samples_enabled: allocation_profiling_enabled,
|
52
52
|
heap_samples_enabled: heap_profiling_enabled,
|
53
53
|
heap_size_enabled: heap_size_profiling_enabled,
|
@@ -61,6 +61,7 @@ module Datadog
|
|
61
61
|
thread_context_collector: thread_context_collector,
|
62
62
|
dynamic_sampling_rate_overhead_target_percentage: overhead_target_percentage,
|
63
63
|
allocation_profiling_enabled: allocation_profiling_enabled,
|
64
|
+
allocation_counting_enabled: settings.profiling.advanced.allocation_counting_enabled,
|
64
65
|
)
|
65
66
|
|
66
67
|
internal_metadata = {
|
@@ -72,14 +73,13 @@ module Datadog
|
|
72
73
|
exporter = build_profiler_exporter(settings, recorder, worker, internal_metadata: internal_metadata)
|
73
74
|
transport = build_profiler_transport(settings, agent_settings)
|
74
75
|
scheduler = Profiling::Scheduler.new(exporter: exporter, transport: transport, interval: upload_period_seconds)
|
75
|
-
|
76
|
-
profiler = Profiling::Profiler.new(worker: worker, scheduler: scheduler, optional_crashtracker: crashtracker)
|
76
|
+
profiler = Profiling::Profiler.new(worker: worker, scheduler: scheduler)
|
77
77
|
|
78
78
|
if dir_interruption_workaround_enabled?(settings, no_signals_workaround_enabled)
|
79
79
|
Datadog::Profiling::Ext::DirMonkeyPatches.apply!
|
80
80
|
end
|
81
81
|
|
82
|
-
[profiler, {
|
82
|
+
[profiler, {profiling_enabled: true}]
|
83
83
|
end
|
84
84
|
|
85
85
|
private_class_method def self.build_thread_context_collector(settings, recorder, optional_tracer, timeline_enabled)
|
@@ -116,28 +116,6 @@ module Datadog
|
|
116
116
|
)
|
117
117
|
end
|
118
118
|
|
119
|
-
private_class_method def self.build_crashtracker(settings, transport)
|
120
|
-
return unless settings.profiling.advanced.experimental_crash_tracking_enabled
|
121
|
-
|
122
|
-
# By default, the transport is an instance of HttpTransport, which validates the configuration and makes
|
123
|
-
# it available for us to use here.
|
124
|
-
# But we support overriding the transport with a user-specific one, which may e.g. write stuff to a file,
|
125
|
-
# and thus can't really provide a valid configuration to talk to a Datadog agent. Thus, in this situation,
|
126
|
-
# we can't use the crashtracker, even if enabled.
|
127
|
-
unless transport.respond_to?(:exporter_configuration)
|
128
|
-
Datadog.logger.warn(
|
129
|
-
'Cannot enable profiling crash tracking as a custom settings.profiling.exporter.transport is configured'
|
130
|
-
)
|
131
|
-
return
|
132
|
-
end
|
133
|
-
|
134
|
-
Datadog::Profiling::Crashtracker.new(
|
135
|
-
exporter_configuration: transport.exporter_configuration,
|
136
|
-
tags: Datadog::Profiling::TagBuilder.call(settings: settings),
|
137
|
-
upload_timeout_seconds: settings.profiling.upload.timeout_seconds,
|
138
|
-
)
|
139
|
-
end
|
140
|
-
|
141
119
|
private_class_method def self.enable_gc_profiling?(settings)
|
142
120
|
return false unless settings.profiling.advanced.gc_enabled
|
143
121
|
|
@@ -146,19 +124,19 @@ module Datadog
|
|
146
124
|
# that causes a segmentation fault during garbage collection of Ractors
|
147
125
|
# (https://bugs.ruby-lang.org/issues/18464). We don't allow enabling gc profiling on such Rubies.
|
148
126
|
# This bug is fixed on Ruby versions 3.1.4, 3.2.3 and 3.3.0.
|
149
|
-
if RUBY_VERSION.start_with?(
|
150
|
-
(RUBY_VERSION.start_with?(
|
151
|
-
(RUBY_VERSION.start_with?(
|
127
|
+
if RUBY_VERSION.start_with?("3.0.") ||
|
128
|
+
(RUBY_VERSION.start_with?("3.1.") && RUBY_VERSION < "3.1.4") ||
|
129
|
+
(RUBY_VERSION.start_with?("3.2.") && RUBY_VERSION < "3.2.3")
|
152
130
|
Datadog.logger.warn(
|
153
|
-
"Current Ruby version (#{RUBY_VERSION}) has a VM bug where enabling GC profiling would cause "\
|
154
|
-
|
131
|
+
"Current Ruby version (#{RUBY_VERSION}) has a VM bug where enabling GC profiling would cause " \
|
132
|
+
"crashes (https://bugs.ruby-lang.org/issues/18464). GC profiling has been disabled."
|
155
133
|
)
|
156
134
|
return false
|
157
|
-
elsif RUBY_VERSION.start_with?(
|
135
|
+
elsif RUBY_VERSION.start_with?("3.")
|
158
136
|
Datadog.logger.debug(
|
159
|
-
|
160
|
-
|
161
|
-
|
137
|
+
"In all known versions of Ruby 3.x, using Ractors may result in GC profiling unexpectedly " \
|
138
|
+
"stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your " \
|
139
|
+
"application stability or performance. This does not happen if Ractors are not used."
|
162
140
|
)
|
163
141
|
end
|
164
142
|
|
@@ -182,11 +160,11 @@ module Datadog
|
|
182
160
|
# Ruby 3.2.0 to 3.2.2 have a bug in the newobj tracepoint (https://bugs.ruby-lang.org/issues/19482,
|
183
161
|
# https://github.com/ruby/ruby/pull/7464) that makes this crash in any configuration. This bug is
|
184
162
|
# fixed on Ruby versions 3.2.3 and 3.3.0.
|
185
|
-
if RUBY_VERSION.start_with?(
|
163
|
+
if RUBY_VERSION.start_with?("3.2.") && RUBY_VERSION < "3.2.3"
|
186
164
|
Datadog.logger.warn(
|
187
|
-
|
188
|
-
|
189
|
-
|
165
|
+
"Allocation profiling is not supported in Ruby versions 3.2.0, 3.2.1 and 3.2.2 and will be forcibly " \
|
166
|
+
"disabled. This is due to a VM bug that can lead to crashes (https://bugs.ruby-lang.org/issues/19482). " \
|
167
|
+
"Other Ruby versions do not suffer from this issue."
|
190
168
|
)
|
191
169
|
return false
|
192
170
|
end
|
@@ -196,26 +174,26 @@ module Datadog
|
|
196
174
|
# that causes a segmentation fault during garbage collection of Ractors
|
197
175
|
# (https://bugs.ruby-lang.org/issues/18464). We don't recommend using this feature on such Rubies.
|
198
176
|
# This bug is fixed on Ruby versions 3.1.4, 3.2.3 and 3.3.0.
|
199
|
-
if RUBY_VERSION.start_with?(
|
200
|
-
(RUBY_VERSION.start_with?(
|
201
|
-
(RUBY_VERSION.start_with?(
|
177
|
+
if RUBY_VERSION.start_with?("3.0.") ||
|
178
|
+
(RUBY_VERSION.start_with?("3.1.") && RUBY_VERSION < "3.1.4") ||
|
179
|
+
(RUBY_VERSION.start_with?("3.2.") && RUBY_VERSION < "3.2.3")
|
202
180
|
Datadog.logger.warn(
|
203
|
-
"Current Ruby version (#{RUBY_VERSION}) has a VM bug where enabling allocation profiling while using "\
|
204
|
-
|
205
|
-
|
181
|
+
"Current Ruby version (#{RUBY_VERSION}) has a VM bug where enabling allocation profiling while using " \
|
182
|
+
"Ractors may cause unexpected issues, including crashes (https://bugs.ruby-lang.org/issues/18464). " \
|
183
|
+
"This does not happen if Ractors are not used."
|
206
184
|
)
|
207
185
|
# ANNOYANCE - Only with Ractors
|
208
186
|
# On all known versions of Ruby 3.x, due to https://bugs.ruby-lang.org/issues/19112, when a ractor gets
|
209
187
|
# garbage collected, Ruby will disable all active tracepoints, which this feature internally relies on.
|
210
|
-
elsif RUBY_VERSION.start_with?(
|
188
|
+
elsif RUBY_VERSION.start_with?("3.")
|
211
189
|
Datadog.logger.warn(
|
212
|
-
|
213
|
-
|
214
|
-
|
190
|
+
"In all known versions of Ruby 3.x, using Ractors may result in allocation profiling unexpectedly " \
|
191
|
+
"stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your " \
|
192
|
+
"application stability or performance. This does not happen if Ractors are not used."
|
215
193
|
)
|
216
194
|
end
|
217
195
|
|
218
|
-
Datadog.logger.debug(
|
196
|
+
Datadog.logger.debug("Enabled allocation profiling")
|
219
197
|
|
220
198
|
true
|
221
199
|
end
|
@@ -225,33 +203,33 @@ module Datadog
|
|
225
203
|
|
226
204
|
return false unless heap_profiling_enabled
|
227
205
|
|
228
|
-
if RUBY_VERSION.start_with?(
|
206
|
+
if RUBY_VERSION.start_with?("2.") && RUBY_VERSION < "2.7"
|
229
207
|
Datadog.logger.warn(
|
230
|
-
|
231
|
-
|
208
|
+
"Heap profiling currently relies on features introduced in Ruby 2.7 and will be forcibly disabled. " \
|
209
|
+
"Please upgrade to Ruby >= 2.7 in order to use this feature."
|
232
210
|
)
|
233
211
|
return false
|
234
212
|
end
|
235
213
|
|
236
|
-
if RUBY_VERSION <
|
214
|
+
if RUBY_VERSION < "3.1"
|
237
215
|
Datadog.logger.debug(
|
238
216
|
"Current Ruby version (#{RUBY_VERSION}) supports forced object recycling which has a bug that the " \
|
239
|
-
|
217
|
+
"heap profiler is forced to work around to remain accurate. This workaround requires force-setting " \
|
240
218
|
"the SEEN_OBJ_ID flag on objects that should have it but don't. Full details can be found in " \
|
241
|
-
|
242
|
-
|
243
|
-
|
219
|
+
"https://github.com/DataDog/dd-trace-rb/pull/3360. This workaround should be safe but can be " \
|
220
|
+
"bypassed by disabling the heap profiler or upgrading to Ruby >= 3.1 where forced object recycling " \
|
221
|
+
"was completely removed (https://bugs.ruby-lang.org/issues/18290)."
|
244
222
|
)
|
245
223
|
end
|
246
224
|
|
247
225
|
unless allocation_profiling_enabled
|
248
226
|
raise ArgumentError,
|
249
|
-
|
227
|
+
"Heap profiling requires allocation profiling to be enabled"
|
250
228
|
end
|
251
229
|
|
252
230
|
Datadog.logger.warn(
|
253
231
|
"Enabled experimental heap profiling: heap_sample_rate=#{heap_sample_rate}. This is experimental, not " \
|
254
|
-
|
232
|
+
"recommended, and will increase overhead!"
|
255
233
|
)
|
256
234
|
|
257
235
|
true
|
@@ -263,7 +241,7 @@ module Datadog
|
|
263
241
|
return false unless heap_profiling_enabled && heap_size_profiling_enabled
|
264
242
|
|
265
243
|
Datadog.logger.warn(
|
266
|
-
|
244
|
+
"Enabled experimental heap size profiling. This is experimental, not recommended, and will increase overhead!"
|
267
245
|
)
|
268
246
|
|
269
247
|
true
|
@@ -271,12 +249,12 @@ module Datadog
|
|
271
249
|
|
272
250
|
private_class_method def self.no_signals_workaround_enabled?(settings) # rubocop:disable Metrics/MethodLength
|
273
251
|
setting_value = settings.profiling.advanced.no_signals_workaround_enabled
|
274
|
-
legacy_ruby_that_should_use_workaround = RUBY_VERSION.start_with?(
|
252
|
+
legacy_ruby_that_should_use_workaround = RUBY_VERSION.start_with?("2.5.")
|
275
253
|
|
276
254
|
unless [true, false, :auto].include?(setting_value)
|
277
255
|
Datadog.logger.error(
|
278
256
|
"Ignoring invalid value for profiling no_signals_workaround_enabled setting: #{setting_value.inspect}. " \
|
279
|
-
|
257
|
+
"Valid options are `true`, `false` or (default) `:auto`."
|
280
258
|
)
|
281
259
|
|
282
260
|
setting_value = :auto
|
@@ -286,10 +264,10 @@ module Datadog
|
|
286
264
|
if legacy_ruby_that_should_use_workaround
|
287
265
|
Datadog.logger.warn(
|
288
266
|
'The profiling "no signals" workaround has been disabled via configuration on a legacy Ruby version ' \
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
267
|
+
"(< 2.6). This is not recommended " \
|
268
|
+
"in production environments, as due to limitations in Ruby APIs, we suspect it may lead to crashes " \
|
269
|
+
"in very rare situations. Please report any issues you run into to Datadog support or " \
|
270
|
+
"via <https://github.com/datadog/dd-trace-rb/issues/new>!"
|
293
271
|
)
|
294
272
|
else
|
295
273
|
Datadog.logger.warn('Profiling "no signals" workaround disabled via configuration')
|
@@ -311,30 +289,30 @@ module Datadog
|
|
311
289
|
# We don't warn users in this situation because "upgrade your Ruby" is not a great warning
|
312
290
|
return true if legacy_ruby_that_should_use_workaround
|
313
291
|
|
314
|
-
if Gem.loaded_specs[
|
292
|
+
if Gem.loaded_specs["mysql2"] && incompatible_libmysqlclient_version?(settings)
|
315
293
|
Datadog.logger.warn(
|
316
294
|
'Enabling the profiling "no signals" workaround because an incompatible version of the mysql2 gem is ' \
|
317
|
-
|
318
|
-
|
295
|
+
"installed. Profiling data will have lower quality. " \
|
296
|
+
"To fix this, upgrade the libmysqlclient in your OS image to version 8.0.0 or above."
|
319
297
|
)
|
320
298
|
return true
|
321
299
|
end
|
322
300
|
|
323
|
-
if Gem.loaded_specs[
|
301
|
+
if Gem.loaded_specs["rugged"]
|
324
302
|
Datadog.logger.warn(
|
325
303
|
'Enabling the profiling "no signals" workaround because the rugged gem is installed. ' \
|
326
|
-
|
327
|
-
|
328
|
-
|
304
|
+
"This is needed because some operations on this gem are currently incompatible with the normal working mode " \
|
305
|
+
"of the profiler, as detailed in <https://github.com/datadog/dd-trace-rb/issues/2721>. " \
|
306
|
+
"Profiling data will have lower quality."
|
329
307
|
)
|
330
308
|
return true
|
331
309
|
end
|
332
310
|
|
333
|
-
if (defined?(::PhusionPassenger) || Gem.loaded_specs[
|
311
|
+
if (defined?(::PhusionPassenger) || Gem.loaded_specs["passenger"]) && incompatible_passenger_version?
|
334
312
|
Datadog.logger.warn(
|
335
313
|
'Enabling the profiling "no signals" workaround because an incompatible version of the passenger gem is ' \
|
336
|
-
|
337
|
-
|
314
|
+
"installed. Profiling data will have lower quality." \
|
315
|
+
"To fix this, upgrade the passenger gem to version 6.0.19 or above."
|
338
316
|
)
|
339
317
|
return true
|
340
318
|
end
|
@@ -355,11 +333,11 @@ module Datadog
|
|
355
333
|
return true if settings.profiling.advanced.skip_mysql2_check
|
356
334
|
|
357
335
|
Datadog.logger.debug(
|
358
|
-
|
336
|
+
"Requiring `mysql2` to check if the `libmysqlclient` version it uses is compatible with profiling"
|
359
337
|
)
|
360
338
|
|
361
339
|
begin
|
362
|
-
require
|
340
|
+
require "mysql2"
|
363
341
|
|
364
342
|
# The mysql2-aurora gem likes to monkey patch itself in replacement of Mysql2::Client, and uses
|
365
343
|
# `method_missing` to delegate to the original BUT unfortunately does not implement `respond_to_missing?` and
|
@@ -380,18 +358,18 @@ module Datadog
|
|
380
358
|
libmysqlclient_version = Gem::Version.new(info[:version])
|
381
359
|
|
382
360
|
compatible =
|
383
|
-
libmysqlclient_version >= Gem::Version.new(
|
361
|
+
libmysqlclient_version >= Gem::Version.new("8.0.0") ||
|
384
362
|
looks_like_mariadb?(info, libmysqlclient_version)
|
385
363
|
|
386
364
|
Datadog.logger.debug(
|
387
|
-
"The `mysql2` gem is using #{compatible ?
|
365
|
+
"The `mysql2` gem is using #{compatible ? "a compatible" : "an incompatible"} version of " \
|
388
366
|
"the `libmysqlclient` library (#{libmysqlclient_version})"
|
389
367
|
)
|
390
368
|
|
391
369
|
!compatible
|
392
370
|
rescue StandardError, LoadError => e
|
393
371
|
Datadog.logger.warn(
|
394
|
-
|
372
|
+
"Failed to probe `mysql2` gem information. " \
|
395
373
|
"Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
|
396
374
|
)
|
397
375
|
|
@@ -401,10 +379,10 @@ module Datadog
|
|
401
379
|
|
402
380
|
# See https://github.com/datadog/dd-trace-rb/issues/2976 for details.
|
403
381
|
private_class_method def self.incompatible_passenger_version?
|
404
|
-
first_compatible_version = Gem::Version.new(
|
382
|
+
first_compatible_version = Gem::Version.new("6.0.19")
|
405
383
|
|
406
|
-
if Gem.loaded_specs[
|
407
|
-
Gem.loaded_specs[
|
384
|
+
if Gem.loaded_specs["passenger"]
|
385
|
+
Gem.loaded_specs["passenger"].version < first_compatible_version
|
408
386
|
elsif defined?(PhusionPassenger::VERSION_STRING)
|
409
387
|
Gem::Version.new(PhusionPassenger::VERSION_STRING) < first_compatible_version
|
410
388
|
else
|
@@ -417,7 +395,7 @@ module Datadog
|
|
417
395
|
overhead_target_percentage
|
418
396
|
else
|
419
397
|
Datadog.logger.error(
|
420
|
-
|
398
|
+
"Ignoring invalid value for profiling overhead_target_percentage setting: " \
|
421
399
|
"#{overhead_target_percentage.inspect}. Falling back to default value."
|
422
400
|
)
|
423
401
|
|
@@ -450,8 +428,8 @@ module Datadog
|
|
450
428
|
header_version = Gem::Version.new(info[:header_version]) if info[:header_version]
|
451
429
|
|
452
430
|
!!(header_version &&
|
453
|
-
libmysqlclient_version < Gem::Version.new(
|
454
|
-
header_version >= Gem::Version.new(
|
431
|
+
libmysqlclient_version < Gem::Version.new("5.0.0") &&
|
432
|
+
header_version >= Gem::Version.new("10.0.0"))
|
455
433
|
end
|
456
434
|
|
457
435
|
private_class_method def self.dir_interruption_workaround_enabled?(settings, no_signals_workaround_enabled)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
3
|
+
require_relative "ext"
|
4
|
+
require_relative "tag_builder"
|
5
5
|
|
6
6
|
module Datadog
|
7
7
|
module Profiling
|
@@ -61,7 +61,7 @@ module Datadog
|
|
61
61
|
@last_flush_finish_at = finish
|
62
62
|
|
63
63
|
if duration_below_threshold?(start, finish)
|
64
|
-
Datadog.logger.debug(
|
64
|
+
Datadog.logger.debug("Skipped exporting profiling events as profile duration is below minimum")
|
65
65
|
return
|
66
66
|
end
|
67
67
|
|