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:, dynamic_sampling_rate_enabled: true, skip_idle_samples_for_testing: false, idle_sampling_helper: IdleSamplingHelper.new) ⇒ CpuAndWallTimeWorker

Returns a new instance of CpuAndWallTimeWorker.



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

def initialize(
  gc_profiling_enabled:,
  no_signals_workaround_enabled:,
  thread_context_collector:,
  dynamic_sampling_rate_overhead_target_percentage:,
  allocation_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,
    thread_context_collector,
    gc_profiling_enabled,
    idle_sampling_helper,
    no_signals_workaround_enabled,
    dynamic_sampling_rate_enabled,
    dynamic_sampling_rate_overhead_target_percentage,
    allocation_profiling_enabled,
    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



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

static VALUE _native_allocation_count(DDTRACE_UNUSED VALUE self);

._native_initializeObject



174
175
176
177
178
179
180
181
182
183
184
185
# File 'ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c', line 174

static VALUE _native_initialize(
  DDTRACE_UNUSED VALUE _self,
  VALUE self_instance,
  VALUE thread_context_collector_instance,
  VALUE gc_profiling_enabled,
  VALUE idle_sampling_helper_instance,
  VALUE no_signals_workaround_enabled,
  VALUE dynamic_sampling_rate_enabled,
  VALUE dynamic_sampling_rate_overhead_target_percentage,
  VALUE allocation_profiling_enabled,
  VALUE skip_idle_samples_for_testing
);

._native_is_running?Boolean

Returns:

  • (Boolean)


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

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

._native_reset_after_forkObject



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

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

._native_sampling_loopObject



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

static VALUE _native_sampling_loop(VALUE self, VALUE instance);

._native_statsObject



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

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

._native_stats_reset_not_thread_safeObject



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

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

._native_stopObject



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

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

Instance Method Details

#reset_after_forkObject



100
101
102
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 100

def reset_after_fork
  self.class._native_reset_after_fork(self)
end

#start(on_failure_proc: nil) ⇒ Object



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

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

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

    @idle_sampling_helper.start

    @worker_thread = Thread.new do
      begin
        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
    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



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

def stats
  self.class._native_stats(self)
end

#stats_and_reset_not_thread_safeObject



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

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

#stopObject



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 84

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



115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb', line 115

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