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:, logger:) ⇒ Object

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

  • Profiling 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)


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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/datadog/profiling/component.rb', line 12

def self.build_profiler_component(settings:, agent_settings:, optional_tracer:, logger:) # rubocop:disable Metrics/MethodLength
  # 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 'datadog'` 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 settings.profiling.enabled && 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, logger)
  allocation_profiling_enabled = enable_allocation_profiling?(settings, logger)
  heap_sample_every = get_heap_sample_every(settings)
  heap_profiling_enabled = enable_heap_profiling?(settings, allocation_profiling_enabled, heap_sample_every, logger)
  heap_size_profiling_enabled = enable_heap_size_profiling?(settings, heap_profiling_enabled, logger)

  overhead_target_percentage = valid_overhead_target(settings.profiling.advanced.overhead_target_percentage, logger)
  upload_period_seconds = [60, settings.profiling.advanced.upload_period_seconds].max
  cpu_sampling_interval_ms =
    valid_cpu_sampling_interval(settings.profiling.advanced.experimental_cpu_sampling_interval_ms, logger)

  recorder = Datadog::Profiling::StackRecorder.new(
    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,
    heap_clean_after_gc_enabled: settings.profiling.advanced.heap_clean_after_gc_enabled,
  )
  thread_context_collector = build_thread_context_collector(settings, recorder, optional_tracer)
  worker = Datadog::Profiling::Collectors::CpuAndWallTimeWorker.new(
    gc_profiling_enabled: enable_gc_profiling?(settings, logger),
    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,
    allocation_counting_enabled: settings.profiling.advanced.allocation_counting_enabled,
    gvl_profiling_enabled: enable_gvl_profiling?(settings, logger),
    sighandler_sampling_enabled: settings.profiling.advanced.sighandler_sampling_enabled,
    cpu_sampling_interval_ms: cpu_sampling_interval_ms,
    waiting_for_gvl_threshold_ns: settings.profiling.advanced.waiting_for_gvl_threshold_ns,
  )

   = {
    no_signals_workaround_enabled: no_signals_workaround_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)
  profiler = Profiling::Profiler.new(worker: worker, scheduler: scheduler)

  if dir_interruption_workaround_enabled?(settings, no_signals_workaround_enabled)
    Datadog::Profiling::Ext::DirMonkeyPatches.apply!
  end

  if can_apply_exec_monkey_patch?(settings)
    Datadog::Profiling::Ext::ExecMonkeyPatch.apply!
  end

  [profiler, {profiling_enabled: true}]
rescue Exception => e # rubocop:disable Lint/RescueException
  logger.warn do
    "Failed to initialize profiling: #{e.class}: #{e.message} " \
    "Location: #{Array(e.backtrace).first}"
  end
  Datadog::Core::Telemetry::Logger.report(e, description: "Failed to initialize profiling")

  [nil, {profiling_enabled: false}]
end