Class: Datadog::Profiling::StackRecorder

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

Overview

Stores stack samples in a native libdatadog data structure and expose Ruby-level serialization APIs Note that ‘record_sample` is only accessible from native code. Methods prefixed with native are implemented in `stack_recorder.c`

Defined Under Namespace

Modules: Testing

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cpu_time_enabled:, alloc_samples_enabled:, heap_samples_enabled:, heap_size_enabled:, heap_sample_every:, timeline_enabled:, heap_clean_after_gc_enabled:) ⇒ StackRecorder

Returns a new instance of StackRecorder.



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
# File 'lib/datadog/profiling/stack_recorder.rb', line 11

def initialize(
  cpu_time_enabled:,
  alloc_samples_enabled:,
  heap_samples_enabled:,
  heap_size_enabled:,
  heap_sample_every:,
  timeline_enabled:,
  heap_clean_after_gc_enabled:
)
  # This mutex works in addition to the fancy C-level mutexes we have in the native side (see the docs there).
  # It prevents multiple Ruby threads calling serialize at the same time -- something like
  # `10.times { Thread.new { stack_recorder.serialize } }`.
  # This isn't something we expect to happen normally, but because it would break the assumptions of the
  # C-level mutexes (that there is a single serializer thread), we add it here as an extra safeguard against it
  # accidentally happening.
  @no_concurrent_synchronize_mutex = Mutex.new

  self.class._native_initialize(
    self_instance: self,
    cpu_time_enabled: cpu_time_enabled,
    alloc_samples_enabled: alloc_samples_enabled,
    heap_samples_enabled: heap_samples_enabled,
    heap_size_enabled: heap_size_enabled,
    heap_sample_every: heap_sample_every,
    timeline_enabled: timeline_enabled,
    heap_clean_after_gc_enabled: heap_clean_after_gc_enabled,
  )
end

Class Method Details

._native_initializeObject



240
# File 'ext/datadog_profiling_native_extension/stack_recorder.c', line 240

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

._native_reset_after_forkObject



252
# File 'ext/datadog_profiling_native_extension/stack_recorder.c', line 252

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

._native_serializeObject



241
# File 'ext/datadog_profiling_native_extension/stack_recorder.c', line 241

static VALUE _native_serialize(VALUE self, VALUE recorder_instance);

._native_statsObject



263
# File 'ext/datadog_profiling_native_extension/stack_recorder.c', line 263

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

.for_testing(cpu_time_enabled: true, alloc_samples_enabled: false, heap_samples_enabled: false, heap_size_enabled: false, heap_sample_every: 1, timeline_enabled: false, heap_clean_after_gc_enabled: true, **options) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/datadog/profiling/stack_recorder.rb', line 40

def self.for_testing(
  cpu_time_enabled: true,
  alloc_samples_enabled: false,
  heap_samples_enabled: false,
  heap_size_enabled: false,
  heap_sample_every: 1,
  timeline_enabled: false,
  heap_clean_after_gc_enabled: true,
  **options
)
  new(
    cpu_time_enabled: cpu_time_enabled,
    alloc_samples_enabled: alloc_samples_enabled,
    heap_samples_enabled: heap_samples_enabled,
    heap_size_enabled: heap_size_enabled,
    heap_sample_every: heap_sample_every,
    timeline_enabled: timeline_enabled,
    heap_clean_after_gc_enabled: heap_clean_after_gc_enabled,
    **options,
  )
end

Instance Method Details

#reset_after_forkObject



95
96
97
# File 'lib/datadog/profiling/stack_recorder.rb', line 95

def reset_after_fork
  self.class._native_reset_after_fork(self)
end

#serializeObject



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/datadog/profiling/stack_recorder.rb', line 62

def serialize
  status, result = @no_concurrent_synchronize_mutex.synchronize { self.class._native_serialize(self) }

  if status == :ok
    start, finish, encoded_pprof, profile_stats = result

    Datadog.logger.debug { "Encoded profile covering #{start.iso8601} to #{finish.iso8601}" }

    [start, finish, encoded_pprof, profile_stats]
  else
    error_message = result

    Datadog.logger.error("Failed to serialize profiling data: #{error_message}")
    Datadog::Core::Telemetry::Logger.error("Failed to serialize profiling data")

    nil
  end
end

#serialize!Object



81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/datadog/profiling/stack_recorder.rb', line 81

def serialize!
  status, result = @no_concurrent_synchronize_mutex.synchronize { self.class._native_serialize(self) }

  if status == :ok
    _start, _finish, encoded_pprof = result

    encoded_pprof
  else
    error_message = result

    raise("Failed to serialize profiling data: #{error_message}")
  end
end

#statsObject



99
100
101
# File 'lib/datadog/profiling/stack_recorder.rb', line 99

def stats
  self.class._native_stats(self)
end