Class: Datadog::Profiling::Collectors::CpuAndWallTimeWorker

Inherits:
Object
  • Object
show all
Defined in:
lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb,
ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c

Overview

Used to trigger the periodic execution of Collectors::ThreadState, which implements all of the sampling logic itself; this class only implements the “when to do it” part. Almost all of this class is implemented as native code.

Methods prefixed with native are implemented in ‘collectors_cpu_and_wall_time_worker.c`

Defined Under Namespace

Modules: Testing

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(gc_profiling_enabled:, no_signals_workaround_enabled:, thread_context_collector:, dynamic_sampling_rate_overhead_target_percentage:, allocation_profiling_enabled:, allocation_counting_enabled:, gvl_profiling_enabled:, sighandler_sampling_enabled:, dynamic_sampling_rate_enabled: true, skip_idle_samples_for_testing: false, idle_sampling_helper: IdleSamplingHelper.new) ⇒ CpuAndWallTimeWorker

Returns a new instance of CpuAndWallTimeWorker.



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
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 18

def initialize(
  gc_profiling_enabled:,
  no_signals_workaround_enabled:,
  thread_context_collector:,
  dynamic_sampling_rate_overhead_target_percentage:,
  allocation_profiling_enabled:,
  allocation_counting_enabled:,
  gvl_profiling_enabled:,
  sighandler_sampling_enabled:,
  # **NOTE**: This should only be used for testing; disabling the dynamic sampling rate will increase the
  # profiler overhead!
  dynamic_sampling_rate_enabled: true,
  skip_idle_samples_for_testing: false,
  idle_sampling_helper: IdleSamplingHelper.new
)
  unless dynamic_sampling_rate_enabled
    Datadog.logger.warn(
      "Profiling dynamic sampling rate disabled. This should only be used for testing, and will increase overhead!"
    )
    Datadog::Core::Telemetry::Logger.error(
      "Profiling dynamic sampling rate disabled. This should only be used for testing, and will increase overhead!"
    )
  end

  self.class._native_initialize(
    self_instance: self,
    thread_context_collector: thread_context_collector,
    gc_profiling_enabled: gc_profiling_enabled,
    idle_sampling_helper: idle_sampling_helper,
    no_signals_workaround_enabled: no_signals_workaround_enabled,
    dynamic_sampling_rate_enabled: dynamic_sampling_rate_enabled,
    dynamic_sampling_rate_overhead_target_percentage: dynamic_sampling_rate_overhead_target_percentage,
    allocation_profiling_enabled: allocation_profiling_enabled,
    allocation_counting_enabled: allocation_counting_enabled,
    gvl_profiling_enabled: gvl_profiling_enabled,
    sighandler_sampling_enabled: sighandler_sampling_enabled,
    skip_idle_samples_for_testing: skip_idle_samples_for_testing,
  )
  @worker_thread = nil
  @failure_exception = nil
  @start_stop_mutex = Mutex.new
  @idle_sampling_helper = idle_sampling_helper
  @wait_until_running_mutex = Mutex.new
  @wait_until_running_condition = ConditionVariable.new
end

Class Method Details

._native_allocation_countObject



223
# File 'ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c', line 223

static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self);

._native_hold_signalsObject



230
# File 'ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c', line 230

static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);

._native_initializeObject



190
# File 'ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c', line 190

static VALUE _native_initialize(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self);

._native_is_running?Boolean

Returns:

  • (Boolean)


204
# File 'ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c', line 204

static VALUE _native_is_running(DDTRACE_UNUSED VALUE self, VALUE instance);

._native_reset_after_forkObject



215
# File 'ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c', line 215

static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE instance);

._native_resume_signalsObject



231
# File 'ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c', line 231

static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);

._native_sampling_loopObject



192
# File 'ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c', line 192

static VALUE _native_sampling_loop(VALUE self, VALUE instance);

._native_statsObject



217
# File 'ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c', line 217

static VALUE _native_stats(DDTRACE_UNUSED VALUE self, VALUE instance);

._native_stats_reset_not_thread_safeObject



218
# File 'ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c', line 218

static VALUE _native_stats_reset_not_thread_safe(DDTRACE_UNUSED VALUE self, VALUE instance);

._native_stopObject



193
# File 'ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c', line 193

static VALUE _native_stop(DDTRACE_UNUSED VALUE _self, VALUE self_instance, VALUE worker_thread);

Instance Method Details

#reset_after_forkObject



110
111
112
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 110

def reset_after_fork
  self.class._native_reset_after_fork(self)
end

#start(on_failure_proc: nil) ⇒ Object



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
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 64

def start(on_failure_proc: nil)
  @start_stop_mutex.synchronize do
    return if @worker_thread&.alive?

    Datadog.logger.debug { "Starting thread for: #{self}" }

    @idle_sampling_helper.start

    @worker_thread = Thread.new do
      Thread.current.name = self.class.name

      self.class._native_sampling_loop(self)

      Datadog.logger.debug("CpuAndWallTimeWorker thread stopping cleanly")
    rescue Exception => e # rubocop:disable Lint/RescueException
      @failure_exception = e
      Datadog.logger.warn(
        "CpuAndWallTimeWorker thread error. " \
        "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
      )
      on_failure_proc&.call
      Datadog::Core::Telemetry::Logger.report(e, description: "CpuAndWallTimeWorker thread error", pii_safe: true)
    end
    @worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
    @worker_thread.thread_variable_set(:fork_safe, true)
  end

  true
end

#statsObject



114
115
116
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 114

def stats
  self.class._native_stats(self)
end

#stats_and_reset_not_thread_safeObject



118
119
120
121
122
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 118

def stats_and_reset_not_thread_safe
  stats = self.stats
  self.class._native_stats_reset_not_thread_safe(self)
  stats
end

#stopObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 94

def stop
  @start_stop_mutex.synchronize do
    Datadog.logger.debug("Requesting CpuAndWallTimeWorker thread shut down")

    @idle_sampling_helper.stop

    return unless @worker_thread

    self.class._native_stop(self, @worker_thread)

    @worker_thread.join
    @worker_thread = nil
    @failure_exception = nil
  end
end

#wait_until_running(timeout_seconds: 5) ⇒ Object

Useful for testing, to e.g. make sure the profiler is running before we start running some code we want to observe



125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 125

def wait_until_running(timeout_seconds: 5)
  @wait_until_running_mutex.synchronize do
    return true if self.class._native_is_running?(self)

    @wait_until_running_condition.wait(@wait_until_running_mutex, timeout_seconds)

    if self.class._native_is_running?(self)
      true
    else
      raise "Timeout waiting for #{self.class.name} to start (waited for #{timeout_seconds} seconds)"
    end
  end
end