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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -2
  3. data/LICENSE-3rdparty.csv +1 -1
  4. data/bin/ddprofrb +15 -0
  5. data/bin/ddtracerb +3 -1
  6. data/ext/{ddtrace_profiling_loader/ddtrace_profiling_loader.c → datadog_profiling_loader/datadog_profiling_loader.c} +2 -2
  7. data/ext/{ddtrace_profiling_loader → datadog_profiling_loader}/extconf.rb +3 -3
  8. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_cpu_and_wall_time_worker.c +206 -49
  9. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_discrete_dynamic_sampler.c +145 -72
  10. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_discrete_dynamic_sampler.h +17 -5
  11. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.c +92 -2
  12. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/extconf.rb +2 -2
  13. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/http_transport.c +10 -14
  14. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/native_extension_helpers.rb +4 -4
  15. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/private_vm_api_access.c +14 -0
  16. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/private_vm_api_access.h +4 -0
  17. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/profiling.c +1 -1
  18. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/ruby_helpers.c +10 -0
  19. data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/ruby_helpers.h +2 -0
  20. data/lib/datadog/core/configuration/components.rb +5 -5
  21. data/lib/datadog/core/configuration/option.rb +1 -1
  22. data/lib/datadog/core/configuration/settings.rb +92 -46
  23. data/lib/datadog/core/diagnostics/environment_logger.rb +4 -3
  24. data/lib/datadog/core/environment/git.rb +25 -0
  25. data/lib/datadog/core/environment/identity.rb +18 -48
  26. data/lib/datadog/core/git/ext.rb +2 -23
  27. data/lib/datadog/core/remote/negotiation.rb +2 -2
  28. data/lib/datadog/core/remote/worker.rb +7 -4
  29. data/lib/datadog/core/transport/ext.rb +2 -0
  30. data/lib/datadog/core/utils/url.rb +25 -0
  31. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +6 -0
  32. data/lib/datadog/profiling/collectors/info.rb +101 -0
  33. data/lib/datadog/profiling/component.rb +12 -14
  34. data/lib/datadog/profiling/exporter.rb +19 -5
  35. data/lib/datadog/profiling/ext.rb +2 -0
  36. data/lib/datadog/profiling/flush.rb +6 -3
  37. data/lib/datadog/profiling/http_transport.rb +5 -1
  38. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  39. data/lib/datadog/profiling/native_extension.rb +1 -1
  40. data/lib/datadog/profiling/tag_builder.rb +5 -0
  41. data/lib/datadog/profiling/tasks/exec.rb +3 -3
  42. data/lib/datadog/profiling/tasks/help.rb +3 -3
  43. data/lib/datadog/profiling.rb +2 -2
  44. data/lib/datadog/tracing/contrib/concurrent_ruby/async_patch.rb +20 -0
  45. data/lib/datadog/tracing/contrib/concurrent_ruby/patcher.rb +11 -1
  46. data/lib/datadog/tracing/contrib/extensions.rb +6 -2
  47. data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -0
  48. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +11 -4
  49. data/lib/datadog/tracing/contrib/rack/middlewares.rb +28 -4
  50. data/lib/datadog/tracing/contrib/rails/patcher.rb +16 -0
  51. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +6 -3
  52. data/lib/datadog/tracing/metadata/ext.rb +2 -0
  53. data/lib/datadog/tracing/trace_operation.rb +1 -2
  54. data/lib/datadog/tracing/transport/http.rb +1 -0
  55. data/lib/datadog/tracing/transport/trace_formatter.rb +31 -0
  56. data/lib/ddtrace/version.rb +1 -1
  57. metadata +56 -53
  58. data/ext/ddtrace_profiling_native_extension/pid_controller.c +0 -57
  59. data/ext/ddtrace_profiling_native_extension/pid_controller.h +0 -45
  60. data/lib/datadog/profiling/diagnostics/environment_logger.rb +0 -39
  61. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/NativeExtensionDesign.md +0 -0
  62. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id.h +0 -0
  63. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id_from_pthread.c +0 -0
  64. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/clock_id_noop.c +0 -0
  65. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_dynamic_sampling_rate.c +0 -0
  66. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_dynamic_sampling_rate.h +0 -0
  67. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_gc_profiling_helper.c +0 -0
  68. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_gc_profiling_helper.h +0 -0
  69. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_idle_sampling_helper.c +0 -0
  70. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_idle_sampling_helper.h +0 -0
  71. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_stack.c +0 -0
  72. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_stack.h +0 -0
  73. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/collectors_thread_context.h +0 -0
  74. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/heap_recorder.c +0 -0
  75. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/heap_recorder.h +0 -0
  76. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/helpers.h +0 -0
  77. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/libdatadog_helpers.c +0 -0
  78. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/libdatadog_helpers.h +0 -0
  79. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/setup_signal_handler.c +0 -0
  80. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/setup_signal_handler.h +0 -0
  81. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/stack_recorder.c +0 -0
  82. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/stack_recorder.h +0 -0
  83. /data/ext/{ddtrace_profiling_native_extension → datadog_profiling_native_extension}/time_helpers.c +0 -0
  84. /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
@@ -100,6 +100,12 @@ module Datadog
100
100
  def stats
101
101
  self.class._native_stats(self)
102
102
  end
103
+
104
+ def stats_and_reset_not_thread_safe
105
+ stats = self.stats
106
+ self.class._native_stats_reset_not_thread_safe(self)
107
+ stats
108
+ end
103
109
  end
104
110
  end
105
111
  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
- require_relative '../profiling/diagnostics/environment_logger'
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
- return unless Profiling.supported?
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.experimental_timeline_enabled
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.advanced.experimental_allocation_enabled
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.warn(
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
- start, finish, uncompressed_pprof = pprof_recorder.serialize
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 uncompressed_pprof.nil? # We don't want to report empty profiles
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: uncompressed_pprof.to_s,
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.map { |k, v| [k, v.to_s] }.to_h)
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 ddtrace_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 `ddtrace_profiling_loader.c` for more details.
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 ddtrace_profiling_loader extension to load the actual
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 "ddtrace_profiling_loader.#{RUBY_VERSION}_#{RUBY_PLATFORM}"
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 = "ddtrace_profiling_native_extension.#{RUBY_VERSION}_#{RUBY_PLATFORM}"
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/ddtrace_profiling_native_extension folder, as well as some Ruby-level utilities that don't make sense to
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 tracing
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 "ddtracerb exec failed: #{e.class.name} #{e.message} (command was '#{args.join(' ')}')"
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 "ddtracerb exec failed: #{e.class.name} #{e.message} (command was '#{args.join(' ')}')"
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 `ddtrace`
4
+ # Prints help message for usage of `ddprofrb`
5
5
  class Help
6
6
  def run
7
7
  puts %(
8
- Usage: ddtracerb [command] [arguments]
9
- exec [command]: Executes command with tracing & profiling preloaded.
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
@@ -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/ddtrace_profiling_native_extension/skipped_reason.txt"
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' class.
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
- # TODO: Deprecate in the next major version, as `instrument` better describes this method's purpose
172
- alias_method :use, :instrument
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
- ret = block.call(result)
131
- annotate_span_with_result!(span, result)
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