Module: Datadog::Profiling::Component

Defined in:
lib/datadog/profiling/component.rb

Overview

Responsible for wiring up the Profiler for execution

Class Method Summary collapse

Class Method Details

.build_profiler_component(settings:, agent_settings:, optional_tracer:) ⇒ Object

Passing in a ‘nil` tracer is supported and will disable the following profiling features:

  • Code Hotspots panel in the trace viewer, as well as scoping a profile down to a span

  • Endpoint aggregation in the profiler UX, including normalization (resource per endpoint call)



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/datadog/profiling/component.rb', line 10

def self.build_profiler_component(settings:, agent_settings:, optional_tracer:) # rubocop:disable Metrics/MethodLength
  return [nil, { profiling_enabled: false }] unless settings.profiling.enabled

  # Workaround for weird dependency direction: the Core::Configuration::Components class currently has a
  # dependency on individual products, in this case the Profiler.
  # (Note "currently": in the future we want to change this so core classes don't depend on specific products)
  #
  # If the current file included a `require 'datadog/profiler'` at its beginning, we would generate circular
  # requires when used from profiling:
  #
  # datadog/profiling
  #     └─requires─> datadog/core
  #                      └─requires─> datadog/core/configuration/components
  #                                       └─requires─> datadog/profiling       # Loop!
  #
  # ...thus in #1998 we removed such a require.
  #
  # On the other hand, if datadog/core is loaded by a different product and no general `require 'ddtrace'` is
  # done, then profiling may not be loaded, and thus to avoid this issue we do a require here (which is a
  # no-op if profiling is already loaded).
  require_relative '../profiling'

  return [nil, { profiling_enabled: false }] unless Profiling.supported?

  # Activate forking extensions
  Profiling::Tasks::Setup.new.run

  # NOTE: Please update the Initialization section of ProfilingDevelopment.md with any changes to this method

  no_signals_workaround_enabled = no_signals_workaround_enabled?(settings)
  timeline_enabled = settings.profiling.advanced.timeline_enabled
  allocation_profiling_enabled = enable_allocation_profiling?(settings)
  heap_sample_every = get_heap_sample_every(settings)
  heap_profiling_enabled = enable_heap_profiling?(settings, allocation_profiling_enabled, heap_sample_every)
  heap_size_profiling_enabled = enable_heap_size_profiling?(settings, heap_profiling_enabled)

  overhead_target_percentage = valid_overhead_target(settings.profiling.advanced.overhead_target_percentage)
  upload_period_seconds = [60, settings.profiling.advanced.upload_period_seconds].max

  recorder = Datadog::Profiling::StackRecorder.new(
    cpu_time_enabled: RUBY_PLATFORM.include?('linux'), # Only supported on Linux currently
    alloc_samples_enabled: allocation_profiling_enabled,
    heap_samples_enabled: heap_profiling_enabled,
    heap_size_enabled: heap_size_profiling_enabled,
    heap_sample_every: heap_sample_every,
    timeline_enabled: timeline_enabled,
  )
  thread_context_collector = build_thread_context_collector(settings, recorder, optional_tracer, timeline_enabled)
  worker = Datadog::Profiling::Collectors::CpuAndWallTimeWorker.new(
    gc_profiling_enabled: enable_gc_profiling?(settings),
    no_signals_workaround_enabled: no_signals_workaround_enabled,
    thread_context_collector: thread_context_collector,
    dynamic_sampling_rate_overhead_target_percentage: overhead_target_percentage,
    allocation_profiling_enabled: allocation_profiling_enabled,
  )

   = {
    no_signals_workaround_enabled: no_signals_workaround_enabled,
    timeline_enabled: timeline_enabled,
    heap_sample_every: heap_sample_every,
  }.freeze

  exporter = build_profiler_exporter(settings, recorder, worker, internal_metadata: )
  transport = build_profiler_transport(settings, agent_settings)
  scheduler = Profiling::Scheduler.new(exporter: exporter, transport: transport, interval: upload_period_seconds)

  [Profiling::Profiler.new(worker: worker, scheduler: scheduler), { profiling_enabled: true }]
end