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



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



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

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

._native_reset_after_forkObject



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

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

._native_serializeObject



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

static VALUE _native_serialize(VALUE self, VALUE recorder_instance);

._native_statsObject



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

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_serialize_mutex.synchronize { self.class._native_serialize(self) }

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

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

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

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

    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_serialize_mutex.synchronize { self.class._native_serialize(self) }

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

    encoded_profile
  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