ddtrace 1.20.0 → 1.21.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 +61 -2
- data/LICENSE-3rdparty.csv +1 -1
- data/bin/ddprofrb +15 -0
- data/bin/ddtracerb +3 -1
- data/ext/{ddtrace_profiling_loader/ddtrace_profiling_loader.c → datadog_profiling_loader/datadog_profiling_loader.c} +2 -2
- data/ext/{ddtrace_profiling_loader → datadog_profiling_loader}/extconf.rb +3 -3
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_cpu_and_wall_time_worker.c +206 -49
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_discrete_dynamic_sampler.c +145 -72
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_discrete_dynamic_sampler.h +17 -5
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.c +92 -2
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/extconf.rb +2 -2
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/http_transport.c +10 -14
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/native_extension_helpers.rb +4 -4
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/private_vm_api_access.c +14 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/private_vm_api_access.h +4 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/profiling.c +1 -1
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/ruby_helpers.c +10 -0
- data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/ruby_helpers.h +2 -0
- data/lib/datadog/core/configuration/components.rb +5 -5
- data/lib/datadog/core/configuration/option.rb +1 -1
- data/lib/datadog/core/configuration/settings.rb +92 -46
- data/lib/datadog/core/diagnostics/environment_logger.rb +4 -3
- data/lib/datadog/core/environment/git.rb +25 -0
- data/lib/datadog/core/environment/identity.rb +18 -48
- data/lib/datadog/core/git/ext.rb +2 -23
- data/lib/datadog/core/remote/negotiation.rb +2 -2
- data/lib/datadog/core/remote/worker.rb +7 -4
- data/lib/datadog/core/transport/ext.rb +2 -0
- data/lib/datadog/core/utils/url.rb +25 -0
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +6 -0
- data/lib/datadog/profiling/collectors/info.rb +101 -0
- data/lib/datadog/profiling/component.rb +12 -14
- data/lib/datadog/profiling/exporter.rb +19 -5
- data/lib/datadog/profiling/ext.rb +2 -0
- data/lib/datadog/profiling/flush.rb +6 -3
- data/lib/datadog/profiling/http_transport.rb +5 -1
- data/lib/datadog/profiling/load_native_extension.rb +5 -5
- data/lib/datadog/profiling/native_extension.rb +1 -1
- data/lib/datadog/profiling/tag_builder.rb +5 -0
- data/lib/datadog/profiling/tasks/exec.rb +3 -3
- data/lib/datadog/profiling/tasks/help.rb +3 -3
- data/lib/datadog/profiling.rb +2 -2
- data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +20 -0
- data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +11 -1
- data/lib/datadog/tracing/contrib/extensions.rb +6 -2
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +11 -4
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +28 -4
- data/lib/datadog/tracing/contrib/rails/patcher.rb +16 -0
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +6 -3
- data/lib/datadog/tracing/metadata/ext.rb +2 -0
- data/lib/datadog/tracing/trace_operation.rb +1 -2
- data/lib/datadog/tracing/transport/http.rb +1 -0
- data/lib/datadog/tracing/transport/trace_formatter.rb +31 -0
- data/lib/ddtrace/version.rb +1 -1
- metadata +56 -53
- data/ext/ddtrace_profiling_native_extension/pid_controller.c +0 -57
- data/ext/ddtrace_profiling_native_extension/pid_controller.h +0 -45
- data/lib/datadog/profiling/diagnostics/environment_logger.rb +0 -39
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/NativeExtensionDesign.md +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id_from_pthread.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id_noop.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_dynamic_sampling_rate.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_dynamic_sampling_rate.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_gc_profiling_helper.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_gc_profiling_helper.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_idle_sampling_helper.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_idle_sampling_helper.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_stack.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_stack.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/heap_recorder.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/heap_recorder.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/helpers.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/libdatadog_helpers.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/libdatadog_helpers.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/setup_signal_handler.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/setup_signal_handler.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/stack_recorder.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/stack_recorder.h +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/time_helpers.c +0 -0
- /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/time_helpers.h +0 -0
|
@@ -25,6 +25,8 @@ module Datadog
|
|
|
25
25
|
HEADER_META_LANG = 'Datadog-Meta-Lang'
|
|
26
26
|
HEADER_META_LANG_VERSION = 'Datadog-Meta-Lang-Version'
|
|
27
27
|
HEADER_META_LANG_INTERPRETER = 'Datadog-Meta-Lang-Interpreter'
|
|
28
|
+
# Use for distinguishing between CRuby, JRuby, and TruffleRuby.
|
|
29
|
+
HEADER_META_LANG_INTERPRETER_VENDOR = 'Datadog-Meta-Lang-Interpreter-Vendor'
|
|
28
30
|
HEADER_META_TRACER_VERSION = 'Datadog-Meta-Tracer-Version'
|
|
29
31
|
|
|
30
32
|
# Header that prevents the Net::HTTP integration from tracing internal trace requests.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module Core
|
|
7
|
+
module Utils
|
|
8
|
+
# Helpers class that provides methods to process URLs
|
|
9
|
+
# such as filtering sensitive information.
|
|
10
|
+
module Url
|
|
11
|
+
def self.filter_basic_auth(url)
|
|
12
|
+
return nil if url.nil?
|
|
13
|
+
|
|
14
|
+
URI(url).tap do |u|
|
|
15
|
+
u.user = nil
|
|
16
|
+
u.password = nil
|
|
17
|
+
end.to_s
|
|
18
|
+
# Git scheme: git@github.com:DataDog/dd-trace-rb.git
|
|
19
|
+
rescue URI::InvalidURIError
|
|
20
|
+
url
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
require 'time'
|
|
5
|
+
|
|
6
|
+
module Datadog
|
|
7
|
+
module Profiling
|
|
8
|
+
module Collectors
|
|
9
|
+
# Collects information of relevance for profiler. This will get sent alongside
|
|
10
|
+
# the profile and show up in the UI or potentially influence processing in some way.
|
|
11
|
+
#
|
|
12
|
+
# Information is currently collected and frozen at construction time. A full collector
|
|
13
|
+
# could be seen as overkill for this case but it allows us to centralize information
|
|
14
|
+
# gathering and easily support more flexible/dynamic info collection in the future.
|
|
15
|
+
class Info
|
|
16
|
+
def initialize(settings)
|
|
17
|
+
@profiler_info = nil
|
|
18
|
+
@info = {
|
|
19
|
+
platform: collect_platform_info,
|
|
20
|
+
runtime: collect_runtime_info,
|
|
21
|
+
application: collect_application_info(settings),
|
|
22
|
+
profiler: collect_profiler_info(settings),
|
|
23
|
+
}.freeze
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_reader :info
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# Instead of trying to figure out real process start time by checking
|
|
31
|
+
# /proc or some other complex/non-portable way, approximate start time
|
|
32
|
+
# by time of requirement of this file.
|
|
33
|
+
START_TIME = Time.now.utc.freeze
|
|
34
|
+
|
|
35
|
+
def collect_platform_info
|
|
36
|
+
@platform_info ||= {
|
|
37
|
+
container_id: Datadog::Core::Environment::Container.container_id,
|
|
38
|
+
hostname: Datadog::Core::Environment::Platform.hostname,
|
|
39
|
+
kernel_name: Datadog::Core::Environment::Platform.kernel_name,
|
|
40
|
+
kernel_release: Datadog::Core::Environment::Platform.kernel_release,
|
|
41
|
+
kernel_version: Datadog::Core::Environment::Platform.kernel_version
|
|
42
|
+
}.freeze
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def collect_runtime_info
|
|
46
|
+
@runtime_info ||= {
|
|
47
|
+
engine: Datadog::Core::Environment::Identity.lang_engine,
|
|
48
|
+
version: Datadog::Core::Environment::Identity.lang_version,
|
|
49
|
+
platform: Datadog::Core::Environment::Identity.lang_platform,
|
|
50
|
+
}.freeze
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def collect_application_info(settings)
|
|
54
|
+
@application_info ||= {
|
|
55
|
+
start_time: START_TIME.iso8601,
|
|
56
|
+
env: settings.env,
|
|
57
|
+
service: settings.service,
|
|
58
|
+
version: settings.version,
|
|
59
|
+
}.freeze
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def collect_profiler_info(settings)
|
|
63
|
+
unless @profiler_info
|
|
64
|
+
lib_datadog_gem = ::Gem.loaded_specs['libdatadog']
|
|
65
|
+
@profiler_info = {
|
|
66
|
+
version: Datadog::Core::Environment::Identity.tracer_version,
|
|
67
|
+
libdatadog: "#{lib_datadog_gem.version}-#{lib_datadog_gem.platform}",
|
|
68
|
+
settings: collect_settings_recursively(settings.profiling),
|
|
69
|
+
}.freeze
|
|
70
|
+
end
|
|
71
|
+
@profiler_info
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# The settings/option model isn't directly serializable because
|
|
75
|
+
# of subsettings and options that link to full blown custom object
|
|
76
|
+
# instances without proper serialization.
|
|
77
|
+
# This method navigates a settings object recursively, converting
|
|
78
|
+
# it into more basic types that are trivially convertible to JSON.
|
|
79
|
+
def collect_settings_recursively(v)
|
|
80
|
+
v = v.options_hash if v.respond_to?(:options_hash)
|
|
81
|
+
|
|
82
|
+
if v.nil? || v.is_a?(Symbol) || v.is_a?(Numeric) || v.is_a?(String) || v.equal?(true) || v.equal?(false)
|
|
83
|
+
Core::Utils::SafeDup.frozen_or_dup(v)
|
|
84
|
+
elsif v.is_a?(Hash)
|
|
85
|
+
collected_hash = v.each_with_object({}) do |(key, value), hash|
|
|
86
|
+
collected_value = collect_settings_recursively(value)
|
|
87
|
+
hash[key] = collected_value
|
|
88
|
+
end
|
|
89
|
+
collected_hash.freeze
|
|
90
|
+
elsif v.is_a?(Enumerable)
|
|
91
|
+
collected_list = v
|
|
92
|
+
.map { |value| collect_settings_recursively(value) }
|
|
93
|
+
collected_list.freeze
|
|
94
|
+
else
|
|
95
|
+
v.inspect
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -8,11 +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
|
-
|
|
12
|
-
|
|
13
|
-
Profiling::Diagnostics::EnvironmentLogger.collect_and_log!
|
|
14
|
-
|
|
15
|
-
return unless settings.profiling.enabled
|
|
11
|
+
return [nil, { profiling_enabled: false }] unless settings.profiling.enabled
|
|
16
12
|
|
|
17
13
|
# Workaround for weird dependency direction: the Core::Configuration::Components class currently has a
|
|
18
14
|
# dependency on individual products, in this case the Profiler.
|
|
@@ -32,7 +28,8 @@ module Datadog
|
|
|
32
28
|
# done, then profiling may not be loaded, and thus to avoid this issue we do a require here (which is a
|
|
33
29
|
# no-op if profiling is already loaded).
|
|
34
30
|
require_relative '../profiling'
|
|
35
|
-
|
|
31
|
+
|
|
32
|
+
return [nil, { profiling_enabled: false }] unless Profiling.supported?
|
|
36
33
|
|
|
37
34
|
# Activate forking extensions
|
|
38
35
|
Profiling::Tasks::Setup.new.run
|
|
@@ -40,7 +37,7 @@ module Datadog
|
|
|
40
37
|
# NOTE: Please update the Initialization section of ProfilingDevelopment.md with any changes to this method
|
|
41
38
|
|
|
42
39
|
no_signals_workaround_enabled = no_signals_workaround_enabled?(settings)
|
|
43
|
-
timeline_enabled = settings.profiling.advanced.
|
|
40
|
+
timeline_enabled = settings.profiling.advanced.timeline_enabled
|
|
44
41
|
allocation_profiling_enabled = enable_allocation_profiling?(settings)
|
|
45
42
|
heap_sample_every = get_heap_sample_every(settings)
|
|
46
43
|
heap_profiling_enabled = enable_heap_profiling?(settings, allocation_profiling_enabled, heap_sample_every)
|
|
@@ -72,11 +69,11 @@ module Datadog
|
|
|
72
69
|
heap_sample_every: heap_sample_every,
|
|
73
70
|
}.freeze
|
|
74
71
|
|
|
75
|
-
exporter = build_profiler_exporter(settings, recorder, internal_metadata: internal_metadata)
|
|
72
|
+
exporter = build_profiler_exporter(settings, recorder, worker, internal_metadata: internal_metadata)
|
|
76
73
|
transport = build_profiler_transport(settings, agent_settings)
|
|
77
74
|
scheduler = Profiling::Scheduler.new(exporter: exporter, transport: transport, interval: upload_period_seconds)
|
|
78
75
|
|
|
79
|
-
Profiling::Profiler.new(worker: worker, scheduler: scheduler)
|
|
76
|
+
[Profiling::Profiler.new(worker: worker, scheduler: scheduler), { profiling_enabled: true }]
|
|
80
77
|
end
|
|
81
78
|
|
|
82
79
|
private_class_method def self.build_thread_context_collector(settings, recorder, optional_tracer, timeline_enabled)
|
|
@@ -89,12 +86,15 @@ module Datadog
|
|
|
89
86
|
)
|
|
90
87
|
end
|
|
91
88
|
|
|
92
|
-
private_class_method def self.build_profiler_exporter(settings, recorder, internal_metadata:)
|
|
89
|
+
private_class_method def self.build_profiler_exporter(settings, recorder, worker, internal_metadata:)
|
|
90
|
+
info_collector = Profiling::Collectors::Info.new(settings)
|
|
93
91
|
code_provenance_collector =
|
|
94
92
|
(Profiling::Collectors::CodeProvenance.new if settings.profiling.advanced.code_provenance_enabled)
|
|
95
93
|
|
|
96
94
|
Profiling::Exporter.new(
|
|
97
95
|
pprof_recorder: recorder,
|
|
96
|
+
worker: worker,
|
|
97
|
+
info_collector: info_collector,
|
|
98
98
|
code_provenance_collector: code_provenance_collector,
|
|
99
99
|
internal_metadata: internal_metadata,
|
|
100
100
|
)
|
|
@@ -135,7 +135,7 @@ module Datadog
|
|
|
135
135
|
end
|
|
136
136
|
|
|
137
137
|
private_class_method def self.enable_allocation_profiling?(settings)
|
|
138
|
-
unless settings.profiling.
|
|
138
|
+
unless settings.profiling.allocation_enabled
|
|
139
139
|
# Allocation profiling disabled, short-circuit out
|
|
140
140
|
return false
|
|
141
141
|
end
|
|
@@ -179,9 +179,7 @@ module Datadog
|
|
|
179
179
|
)
|
|
180
180
|
end
|
|
181
181
|
|
|
182
|
-
Datadog.logger.
|
|
183
|
-
'Enabled experimental allocation profiling. This is experimental, not recommended, and will increase overhead!'
|
|
184
|
-
)
|
|
182
|
+
Datadog.logger.debug('Enabled allocation profiling')
|
|
185
183
|
|
|
186
184
|
true
|
|
187
185
|
end
|
|
@@ -23,31 +23,39 @@ module Datadog
|
|
|
23
23
|
:time_provider,
|
|
24
24
|
:last_flush_finish_at,
|
|
25
25
|
:created_at,
|
|
26
|
-
:internal_metadata
|
|
26
|
+
:internal_metadata,
|
|
27
|
+
:info_json
|
|
27
28
|
|
|
28
29
|
public
|
|
29
30
|
|
|
30
31
|
def initialize(
|
|
31
32
|
pprof_recorder:,
|
|
33
|
+
worker:,
|
|
34
|
+
info_collector:,
|
|
32
35
|
code_provenance_collector:,
|
|
33
36
|
internal_metadata:,
|
|
34
37
|
minimum_duration_seconds: PROFILE_DURATION_THRESHOLD_SECONDS,
|
|
35
38
|
time_provider: Time
|
|
36
39
|
)
|
|
37
40
|
@pprof_recorder = pprof_recorder
|
|
41
|
+
@worker = worker
|
|
38
42
|
@code_provenance_collector = code_provenance_collector
|
|
39
43
|
@minimum_duration_seconds = minimum_duration_seconds
|
|
40
44
|
@time_provider = time_provider
|
|
41
45
|
@last_flush_finish_at = nil
|
|
42
46
|
@created_at = time_provider.now.utc
|
|
43
47
|
@internal_metadata = internal_metadata
|
|
48
|
+
# NOTE: At the time of this comment collected info does not change over time so we'll hardcode
|
|
49
|
+
# it on startup to prevent serializing the same info on every flush.
|
|
50
|
+
@info_json = JSON.fast_generate(info_collector.info).freeze
|
|
44
51
|
end
|
|
45
52
|
|
|
46
53
|
def flush
|
|
47
|
-
|
|
54
|
+
worker_stats = @worker.stats_and_reset_not_thread_safe
|
|
55
|
+
start, finish, compressed_pprof = pprof_recorder.serialize
|
|
48
56
|
@last_flush_finish_at = finish
|
|
49
57
|
|
|
50
|
-
return if
|
|
58
|
+
return if compressed_pprof.nil? # We don't want to report empty profiles
|
|
51
59
|
|
|
52
60
|
if duration_below_threshold?(start, finish)
|
|
53
61
|
Datadog.logger.debug('Skipped exporting profiling events as profile duration is below minimum')
|
|
@@ -60,11 +68,17 @@ module Datadog
|
|
|
60
68
|
start: start,
|
|
61
69
|
finish: finish,
|
|
62
70
|
pprof_file_name: Datadog::Profiling::Ext::Transport::HTTP::PPROF_DEFAULT_FILENAME,
|
|
63
|
-
pprof_data:
|
|
71
|
+
pprof_data: compressed_pprof.to_s,
|
|
64
72
|
code_provenance_file_name: Datadog::Profiling::Ext::Transport::HTTP::CODE_PROVENANCE_FILENAME,
|
|
65
73
|
code_provenance_data: uncompressed_code_provenance,
|
|
66
74
|
tags_as_array: Datadog::Profiling::TagBuilder.call(settings: Datadog.configuration).to_a,
|
|
67
|
-
internal_metadata: internal_metadata
|
|
75
|
+
internal_metadata: internal_metadata.merge(
|
|
76
|
+
{
|
|
77
|
+
worker_stats: worker_stats,
|
|
78
|
+
gc: GC.stat,
|
|
79
|
+
}
|
|
80
|
+
),
|
|
81
|
+
info_json: info_json,
|
|
68
82
|
)
|
|
69
83
|
end
|
|
70
84
|
|
|
@@ -23,6 +23,8 @@ module Datadog
|
|
|
23
23
|
FORM_FIELD_TAG_RUNTIME_VERSION = 'runtime_version'
|
|
24
24
|
FORM_FIELD_TAG_SERVICE = 'service'
|
|
25
25
|
FORM_FIELD_TAG_VERSION = 'version'
|
|
26
|
+
TAG_GIT_REPOSITORY_URL = 'git.repository_url'
|
|
27
|
+
TAG_GIT_COMMIT_SHA = 'git.commit.sha'
|
|
26
28
|
|
|
27
29
|
PPROF_DEFAULT_FILENAME = 'rubyprofile.pprof'
|
|
28
30
|
CODE_PROVENANCE_FILENAME = 'code-provenance.json'
|
|
@@ -14,7 +14,8 @@ module Datadog
|
|
|
14
14
|
:code_provenance_file_name,
|
|
15
15
|
:code_provenance_data, # gzipped json bytes
|
|
16
16
|
:tags_as_array,
|
|
17
|
-
:internal_metadata_json
|
|
17
|
+
:internal_metadata_json,
|
|
18
|
+
:info_json
|
|
18
19
|
|
|
19
20
|
def initialize(
|
|
20
21
|
start:,
|
|
@@ -24,7 +25,8 @@ module Datadog
|
|
|
24
25
|
code_provenance_file_name:,
|
|
25
26
|
code_provenance_data:,
|
|
26
27
|
tags_as_array:,
|
|
27
|
-
internal_metadata
|
|
28
|
+
internal_metadata:,
|
|
29
|
+
info_json:
|
|
28
30
|
)
|
|
29
31
|
@start = start
|
|
30
32
|
@finish = finish
|
|
@@ -33,7 +35,8 @@ module Datadog
|
|
|
33
35
|
@code_provenance_file_name = code_provenance_file_name
|
|
34
36
|
@code_provenance_data = code_provenance_data
|
|
35
37
|
@tags_as_array = tags_as_array
|
|
36
|
-
@internal_metadata_json = JSON.fast_generate(internal_metadata
|
|
38
|
+
@internal_metadata_json = JSON.fast_generate(internal_metadata)
|
|
39
|
+
@info_json = info_json
|
|
37
40
|
end
|
|
38
41
|
end
|
|
39
42
|
end
|
|
@@ -43,6 +43,8 @@ module Datadog
|
|
|
43
43
|
|
|
44
44
|
tags_as_array: flush.tags_as_array,
|
|
45
45
|
internal_metadata_json: flush.internal_metadata_json,
|
|
46
|
+
|
|
47
|
+
info_json: flush.info_json
|
|
46
48
|
)
|
|
47
49
|
|
|
48
50
|
if status == :ok
|
|
@@ -117,7 +119,8 @@ module Datadog
|
|
|
117
119
|
code_provenance_file_name:,
|
|
118
120
|
code_provenance_data:,
|
|
119
121
|
tags_as_array:,
|
|
120
|
-
internal_metadata_json
|
|
122
|
+
internal_metadata_json:,
|
|
123
|
+
info_json:
|
|
121
124
|
)
|
|
122
125
|
self.class._native_do_export(
|
|
123
126
|
exporter_configuration,
|
|
@@ -132,6 +135,7 @@ module Datadog
|
|
|
132
135
|
code_provenance_data,
|
|
133
136
|
tags_as_array,
|
|
134
137
|
internal_metadata_json,
|
|
138
|
+
info_json,
|
|
135
139
|
)
|
|
136
140
|
end
|
|
137
141
|
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
# This file is used to load the profiling native extension. It works in two steps:
|
|
2
2
|
#
|
|
3
|
-
# 1. Load the
|
|
4
|
-
# a special way that avoids exposing native-level code symbols. See `
|
|
3
|
+
# 1. Load the datadog_profiling_loader extension. This extension will be used to load the actual extension, but in
|
|
4
|
+
# a special way that avoids exposing native-level code symbols. See `datadog_profiling_loader.c` for more details.
|
|
5
5
|
#
|
|
6
|
-
# 2. Use the Datadog::Profiling::Loader exposed by the
|
|
6
|
+
# 2. Use the Datadog::Profiling::Loader exposed by the datadog_profiling_loader extension to load the actual
|
|
7
7
|
# profiling native extension.
|
|
8
8
|
#
|
|
9
9
|
# All code on this file is on-purpose at the top-level; this makes it so this file is executed only once,
|
|
10
10
|
# the first time it gets required, to avoid any issues with the native extension being initialized more than once.
|
|
11
11
|
|
|
12
12
|
begin
|
|
13
|
-
require "
|
|
13
|
+
require "datadog_profiling_loader.#{RUBY_VERSION}_#{RUBY_PLATFORM}"
|
|
14
14
|
rescue LoadError => e
|
|
15
15
|
raise LoadError,
|
|
16
16
|
'Failed to load the profiling loader extension. To fix this, please remove and then reinstall ddtrace ' \
|
|
17
17
|
"(Details: #{e.message})"
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
extension_name = "
|
|
20
|
+
extension_name = "datadog_profiling_native_extension.#{RUBY_VERSION}_#{RUBY_PLATFORM}"
|
|
21
21
|
full_file_path = "#{__dir__}/../../#{extension_name}.#{RbConfig::CONFIG['DLEXT']}"
|
|
22
22
|
init_function_name = "Init_#{extension_name.split('.').first}"
|
|
23
23
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Datadog
|
|
4
4
|
module Profiling
|
|
5
5
|
# This module contains classes and methods which are implemented using native code in the
|
|
6
|
-
# ext/
|
|
6
|
+
# ext/datadog_profiling_native_extension folder, as well as some Ruby-level utilities that don't make sense to
|
|
7
7
|
# write using C
|
|
8
8
|
module NativeExtension
|
|
9
9
|
private_class_method def self.working?
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../core/utils'
|
|
4
|
+
require_relative '../core/environment/git'
|
|
4
5
|
|
|
5
6
|
module Datadog
|
|
6
7
|
module Profiling
|
|
@@ -23,6 +24,8 @@ module Datadog
|
|
|
23
24
|
runtime_id: Core::Environment::Identity.id,
|
|
24
25
|
runtime_platform: Core::Environment::Identity.lang_platform,
|
|
25
26
|
runtime_version: Core::Environment::Identity.lang_version,
|
|
27
|
+
git_repository_url: Core::Environment::Git.git_repository_url,
|
|
28
|
+
git_commit_sha: Core::Environment::Git.git_commit_sha,
|
|
26
29
|
# User-provided tags
|
|
27
30
|
user_tags: settings.tags
|
|
28
31
|
)
|
|
@@ -42,6 +45,8 @@ module Datadog
|
|
|
42
45
|
tags[FORM_FIELD_TAG_ENV] = env if env
|
|
43
46
|
tags[FORM_FIELD_TAG_SERVICE] = service if service
|
|
44
47
|
tags[FORM_FIELD_TAG_VERSION] = version if version
|
|
48
|
+
tags[TAG_GIT_REPOSITORY_URL] = git_repository_url if git_repository_url
|
|
49
|
+
tags[TAG_GIT_COMMIT_SHA] = git_commit_sha if git_commit_sha
|
|
45
50
|
|
|
46
51
|
# Make sure everything is an utf-8 string, to avoid encoding issues in native code/libddprof/further downstream
|
|
47
52
|
user_tags.merge(tags).map do |key, value|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Datadog
|
|
2
2
|
module Profiling
|
|
3
3
|
module Tasks
|
|
4
|
-
# Wraps command with Datadog
|
|
4
|
+
# Wraps command with Datadog profiling
|
|
5
5
|
class Exec
|
|
6
6
|
attr_reader :args
|
|
7
7
|
|
|
@@ -36,10 +36,10 @@ module Datadog
|
|
|
36
36
|
def exec_with_error_handling(args)
|
|
37
37
|
Kernel.exec(*args)
|
|
38
38
|
rescue Errno::ENOENT => e
|
|
39
|
-
Kernel.warn "
|
|
39
|
+
Kernel.warn "ddprofrb exec failed: #{e.class.name} #{e.message} (command was '#{args.join(' ')}')"
|
|
40
40
|
Kernel.exit 127
|
|
41
41
|
rescue Errno::EACCES, Errno::ENOEXEC => e
|
|
42
|
-
Kernel.warn "
|
|
42
|
+
Kernel.warn "ddprofrb exec failed: #{e.class.name} #{e.message} (command was '#{args.join(' ')}')"
|
|
43
43
|
Kernel.exit 126
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module Datadog
|
|
2
2
|
module Profiling
|
|
3
3
|
module Tasks
|
|
4
|
-
# Prints help message for usage of `
|
|
4
|
+
# Prints help message for usage of `ddprofrb`
|
|
5
5
|
class Help
|
|
6
6
|
def run
|
|
7
7
|
puts %(
|
|
8
|
-
Usage:
|
|
9
|
-
exec [command]: Executes command with
|
|
8
|
+
Usage: ddprofrb [command] [arguments]
|
|
9
|
+
exec [command]: Executes command with profiling preloaded.
|
|
10
10
|
help: Prints this help message.
|
|
11
11
|
)
|
|
12
12
|
end
|
data/lib/datadog/profiling.rb
CHANGED
|
@@ -77,7 +77,7 @@ module Datadog
|
|
|
77
77
|
|
|
78
78
|
private_class_method def self.try_reading_skipped_reason_file(file_api = File)
|
|
79
79
|
# This file, if it exists, is recorded by extconf.rb during compilation of the native extension
|
|
80
|
-
skipped_reason_file = "#{__dir__}/../../ext/
|
|
80
|
+
skipped_reason_file = "#{__dir__}/../../ext/datadog_profiling_native_extension/skipped_reason.txt"
|
|
81
81
|
|
|
82
82
|
begin
|
|
83
83
|
return unless file_api.exist?(skipped_reason_file)
|
|
@@ -123,13 +123,13 @@ module Datadog
|
|
|
123
123
|
return false unless supported?
|
|
124
124
|
|
|
125
125
|
require_relative 'profiling/ext/forking'
|
|
126
|
+
require_relative 'profiling/collectors/info'
|
|
126
127
|
require_relative 'profiling/collectors/code_provenance'
|
|
127
128
|
require_relative 'profiling/collectors/cpu_and_wall_time_worker'
|
|
128
129
|
require_relative 'profiling/collectors/dynamic_sampling_rate'
|
|
129
130
|
require_relative 'profiling/collectors/idle_sampling_helper'
|
|
130
131
|
require_relative 'profiling/collectors/stack'
|
|
131
132
|
require_relative 'profiling/collectors/thread_context'
|
|
132
|
-
require_relative 'profiling/diagnostics/environment_logger'
|
|
133
133
|
require_relative 'profiling/stack_recorder'
|
|
134
134
|
require_relative 'profiling/exporter'
|
|
135
135
|
require_relative 'profiling/flush'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'context_composite_executor_service'
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module Tracing
|
|
7
|
+
module Contrib
|
|
8
|
+
module ConcurrentRuby
|
|
9
|
+
# This patches the Async - to wrap executor service using ContextCompositeExecutorService
|
|
10
|
+
module AsyncPatch
|
|
11
|
+
def initialize(delegate)
|
|
12
|
+
super(delegate)
|
|
13
|
+
|
|
14
|
+
@executor = ContextCompositeExecutorService.new(@executor)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -6,7 +6,7 @@ module Datadog
|
|
|
6
6
|
module Tracing
|
|
7
7
|
module Contrib
|
|
8
8
|
module ConcurrentRuby
|
|
9
|
-
# Patcher enables patching of 'Future'
|
|
9
|
+
# Patcher enables patching of 'Future' and 'Async' classes.
|
|
10
10
|
module Patcher
|
|
11
11
|
include Contrib::Patcher
|
|
12
12
|
|
|
@@ -21,6 +21,16 @@ module Datadog
|
|
|
21
21
|
patch_future
|
|
22
22
|
require_relative 'promises_future_patch'
|
|
23
23
|
patch_promises_future
|
|
24
|
+
require_relative 'async_patch'
|
|
25
|
+
async_patch
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Propagate tracing context in Concurrent::Async
|
|
29
|
+
def async_patch
|
|
30
|
+
if defined?(::Concurrent::Async)
|
|
31
|
+
# NOTE: AsyncDelegator is a private constant
|
|
32
|
+
::Concurrent::Async.const_get(:AsyncDelegator).prepend(AsyncPatch)
|
|
33
|
+
end
|
|
24
34
|
end
|
|
25
35
|
|
|
26
36
|
# Propagate tracing context in Concurrent::Future
|
|
@@ -168,8 +168,12 @@ module Datadog
|
|
|
168
168
|
integration
|
|
169
169
|
end
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
def use(integration_name, options = {}, &block)
|
|
172
|
+
Core.log_deprecation do
|
|
173
|
+
'Configuration with `use` has been deprecated, use `instrument` instead.'
|
|
174
|
+
end
|
|
175
|
+
instrument(integration_name, options, &block)
|
|
176
|
+
end
|
|
173
177
|
|
|
174
178
|
# For the provided `integration_name`, resolves a matching configuration
|
|
175
179
|
# for the provided integration from an integration-specific `key`.
|
|
@@ -94,6 +94,8 @@ module Datadog
|
|
|
94
94
|
|
|
95
95
|
span.set_error(payload[:exception_object]) if exception_is_error?(payload[:exception_object])
|
|
96
96
|
|
|
97
|
+
integration_route = endpoint.env['grape.routing_args'][:route_info].pattern.origin
|
|
98
|
+
|
|
97
99
|
# override the current span with this notification values
|
|
98
100
|
span.set_tag(Ext::TAG_ROUTE_ENDPOINT, api_view) unless api_view.nil?
|
|
99
101
|
span.set_tag(Ext::TAG_ROUTE_PATH, path)
|
|
@@ -101,6 +103,9 @@ module Datadog
|
|
|
101
103
|
|
|
102
104
|
span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_METHOD, request_method)
|
|
103
105
|
span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_URL, path)
|
|
106
|
+
|
|
107
|
+
trace.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, integration_route)
|
|
108
|
+
trace.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH, endpoint.env['SCRIPT_NAME'])
|
|
104
109
|
ensure
|
|
105
110
|
span.start(start)
|
|
106
111
|
span.finish(finish)
|
|
@@ -125,15 +125,22 @@ module Datadog
|
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
# Read metadata from PG::Result
|
|
128
|
+
#
|
|
129
|
+
# It is important to guard with `nil` check, because it is possible
|
|
130
|
+
# the result is `nil` instead of `PG::Result`.
|
|
131
|
+
#
|
|
132
|
+
# A non-null pointer will generally be returned except in out-of-memory conditions or
|
|
133
|
+
# serious errors such as inability to send the command to the server.
|
|
134
|
+
#
|
|
135
|
+
# see: https://www.postgresql.org/docs/current/libpq-exec.html#LIBPQ-PQEXEC
|
|
128
136
|
if block
|
|
129
137
|
yield(propagated_sql_statement, proc do |result|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
ret
|
|
138
|
+
annotate_span_with_result!(span, result) if result
|
|
139
|
+
block.call(result)
|
|
133
140
|
end)
|
|
134
141
|
else
|
|
135
142
|
result = yield(propagated_sql_statement)
|
|
136
|
-
annotate_span_with_result!(span, result)
|
|
143
|
+
annotate_span_with_result!(span, result) if result
|
|
137
144
|
result
|
|
138
145
|
end
|
|
139
146
|
end
|
|
@@ -66,6 +66,10 @@ module Datadog
|
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
70
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
71
|
+
# rubocop:disable Metrics/MethodLength
|
|
72
|
+
# rubocop:disable Metrics/AbcSize
|
|
69
73
|
def call(env)
|
|
70
74
|
# Find out if this is rack within rack
|
|
71
75
|
previous_request_span = env[Ext::RACK_ENV_REQUEST_SPAN]
|
|
@@ -107,6 +111,30 @@ module Datadog
|
|
|
107
111
|
|
|
108
112
|
# call the rest of the stack
|
|
109
113
|
status, headers, response = @app.call(env)
|
|
114
|
+
|
|
115
|
+
if status != 404 && (last_route = request_trace.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE))
|
|
116
|
+
last_script_name = request_trace.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH)
|
|
117
|
+
|
|
118
|
+
# If the last_script_name is empty but the env['SCRIPT_NAME'] is NOT empty
|
|
119
|
+
# then the current rack request was not routed and must be accounted for
|
|
120
|
+
# which only happens in pure nested rack requests i.e /rack/rack/hello/world
|
|
121
|
+
#
|
|
122
|
+
# To account for the unaccounted nested rack requests of /rack/hello/world,
|
|
123
|
+
# we use 'PATH_INFO knowing that rack cannot have named parameters
|
|
124
|
+
if last_script_name == '' && env['SCRIPT_NAME'] != ''
|
|
125
|
+
last_script_name = last_route
|
|
126
|
+
last_route = env['PATH_INFO']
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Clear the route and route path tags from the request trace to avoid possibility of misplacement
|
|
130
|
+
request_trace.clear_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE)
|
|
131
|
+
request_trace.clear_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH)
|
|
132
|
+
|
|
133
|
+
# Ensure tags are placed in rack.request span as desired
|
|
134
|
+
request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, last_script_name + last_route)
|
|
135
|
+
request_span.clear_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH)
|
|
136
|
+
end
|
|
137
|
+
|
|
110
138
|
[status, headers, response]
|
|
111
139
|
|
|
112
140
|
# rubocop:disable Lint/RescueException
|
|
@@ -142,10 +170,6 @@ module Datadog
|
|
|
142
170
|
end
|
|
143
171
|
# rubocop:enable Lint/RescueException
|
|
144
172
|
|
|
145
|
-
# rubocop:disable Metrics/AbcSize
|
|
146
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
147
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
148
|
-
# rubocop:disable Metrics/MethodLength
|
|
149
173
|
def set_request_tags!(trace, request_span, env, status, headers, response, original_env)
|
|
150
174
|
request_header_collection = Header::RequestHeaderCollection.new(env)
|
|
151
175
|
|