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:, 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
# 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:,
  # **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!"
    )
  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,
    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



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

static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self);

._native_hold_signalsObject



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

static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);

._native_initializeObject



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

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

._native_is_running?Boolean

Returns:

  • (Boolean)


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

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

._native_reset_after_forkObject



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

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

._native_resume_signalsObject



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

static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);

._native_sampling_loopObject



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

static VALUE _native_sampling_loop(VALUE self, VALUE instance);

._native_statsObject



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

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

._native_stats_reset_not_thread_safeObject



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

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

._native_stopObject



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

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

Instance Method Details

#reset_after_forkObject



104
105
106
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 104

def reset_after_fork
  self.class._native_reset_after_fork(self)
end

#start(on_failure_proc: nil) ⇒ Object



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

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
    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



108
109
110
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 108

def stats
  self.class._native_stats(self)
end

#stats_and_reset_not_thread_safeObject



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

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

#stopObject



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 88

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



119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 119

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