Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PROF-9342] Introduce profiler workaround for Ruby Dir interruption bug #3720

Merged
merged 22 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b03696e
Expose methods for holding/resuming interruptions
ivoanjo Apr 11, 2024
aeb2db6
Bootstrap setting for controlling dir interruption workaround
ivoanjo Apr 24, 2024
88f1164
Add monkey patches for affected dir class APIs
ivoanjo May 10, 2024
3c51234
Add inline suggestion to function
ivoanjo Jun 12, 2024
1721270
Add benchmark for hold/resume interruptions
ivoanjo Jun 12, 2024
42c40b2
Add helper to load monkey patches (similar to our Kernel monkey patches)
ivoanjo Jun 13, 2024
25a96a1
Add type signatures for dir monkey patches
ivoanjo Jun 13, 2024
57e64bf
Load new file when loading profiler
ivoanjo Jun 13, 2024
0e80c77
Wire up dir interruption workaround to setting
ivoanjo Jun 13, 2024
577fdb0
Add test coverage for DirMonkeyPatches
ivoanjo Jun 13, 2024
43a3e5e
Add comments to make Rubocop happy
ivoanjo Jun 14, 2024
9152470
Add nice description to dir interruption workaround setting
ivoanjo Jun 14, 2024
b30ca79
Tweak spec for compatibility with Ruby 2.7
ivoanjo Jun 14, 2024
d6eebe9
Load `DirMonkeyPatches` before referencing it to avoid failing on JRuby
ivoanjo Jun 14, 2024
30601a2
Tweak spec to remove assumption that no signals workaround is disable…
ivoanjo Jun 14, 2024
9ad82fe
Make rubocop happy
ivoanjo Jun 14, 2024
8099c94
Tweak naming of functions to clarify that it's signals being held
ivoanjo Jun 18, 2024
ba0d6b5
Avoid ruby2_keywords and instead have variants for Ruby 2 and 3
ivoanjo Jun 18, 2024
a6366f9
Add test to make sure we don't forget to register new profiler benchm…
ivoanjo Jun 19, 2024
3501237
Add new benchmark to validate_benchmarks_spec
ivoanjo Jun 19, 2024
533eeca
Merge branch 'master' into ivoanjo/prof-9342-dir-interruption-workaround
ivoanjo Jul 1, 2024
259e134
Add clarification for expected behavior of yield
ivoanjo Jul 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions benchmarks/profiler_hold_resume_interruptions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Used to quickly run benchmark under RSpec as part of the usual test suite, to validate it didn't bitrot
VALIDATE_BENCHMARK_MODE = ENV['VALIDATE_BENCHMARK'] == 'true'

return unless __FILE__ == $PROGRAM_NAME || VALIDATE_BENCHMARK_MODE

require 'benchmark/ips'
require 'datadog'
require 'pry'
require_relative 'dogstatsd_reporter'

# This benchmark measures the performance of the hold/resume interruptions used by the DirMonkeyPatches
class ProfilerHoldResumeInterruptions
def create_profiler
Datadog.configure do |c|
c.profiling.enabled = true
end
Datadog::Profiling.wait_until_running
end

def run_benchmark
Benchmark.ips do |x|
benchmark_time = VALIDATE_BENCHMARK_MODE ? { time: 0.01, warmup: 0 } : { time: 10, warmup: 2 }
x.config(
**benchmark_time,
suite: report_to_dogstatsd_if_enabled_via_environment_variable(benchmark_name: 'profiler_hold_resume_interruptions')
)

x.report("hold / resume") do
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end

x.save! 'profiler_hold_resume_interruptions-results.json' unless VALIDATE_BENCHMARK_MODE
x.compare!
end
end
end

puts "Current pid is #{Process.pid}"

ProfilerHoldResumeInterruptions.new.instance_exec do
create_profiler
run_benchmark
end
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
static VALUE rescued_sample_allocation(VALUE tracepoint_data);
static void delayed_error(struct cpu_and_wall_time_worker_state *state, const char *error);
static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
static VALUE _native_hold_interruptions(DDTRACE_UNUSED VALUE self);
static VALUE _native_resume_interruptions(DDTRACE_UNUSED VALUE self);

// Note on sampler global state safety:
//
Expand Down Expand Up @@ -285,7 +287,9 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_allocation_count", _native_allocation_count, 0);
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_is_running?", _native_is_running, 1);
rb_define_singleton_method(testing_module, "_native_current_sigprof_signal_handler", _native_current_sigprof_signal_handler, 0);
// TODO: Remove `_native_is_running` from `testing_module` once `prof-correctness` has been updated to not need it
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_hold_interruptions", _native_hold_interruptions, 0);
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_resume_interruptions", _native_resume_interruptions, 0);
// TODO: Remove `_native_is_running` from `testing_module` (should be in class) once `prof-correctness` has been updated to not need it
rb_define_singleton_method(testing_module, "_native_is_running?", _native_is_running, 1);
rb_define_singleton_method(testing_module, "_native_install_testing_signal_handler", _native_install_testing_signal_handler, 0);
rb_define_singleton_method(testing_module, "_native_remove_testing_signal_handler", _native_remove_testing_signal_handler, 0);
Expand Down Expand Up @@ -1159,3 +1163,17 @@ static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VA

return Qnil;
}

// Masks SIGPROF interruptions for the current thread. Please don't use this -- you may end up with incomplete
// profiling data.
static VALUE _native_hold_interruptions(DDTRACE_UNUSED VALUE self) {
block_sigprof_signal_handler_from_running_in_current_thread();
return Qtrue;
}

// Unmasks SIGPROF interruptions for the current thread. If there's a pending sample, it'll be triggered inside this
// method.
static VALUE _native_resume_interruptions(DDTRACE_UNUSED VALUE self) {
unblock_sigprof_signal_handler_from_running_in_current_thread();
return Qtrue;
}
lloeki marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ void remove_sigprof_signal_handler(void) {
if (sigaction(SIGPROF, &signal_handler_config, NULL) != 0) rb_sys_fail("Failure while removing the signal handler");
}

static void toggle_sigprof_signal_handler_for_current_thread(int action) {
static inline void toggle_sigprof_signal_handler_for_current_thread(int action) {
sigset_t signals_to_toggle;
sigemptyset(&signals_to_toggle);
sigaddset(&signals_to_toggle, SIGPROF);
Expand Down
16 changes: 16 additions & 0 deletions lib/datadog/core/configuration/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,22 @@ def initialize(*_)
end
end

# The profiler gathers data by sending `SIGPROF` unix signals to Ruby application threads.
#
# We've discovered that this can trigger a bug in a number of Ruby APIs in the `Dir` class, as
# described in https://github.com/DataDog/dd-trace-rb/issues/3450 . This workaround prevents the issue
# from happening by monkey patching the affected APIs.
#
# (In the future, once a fix lands upstream, we'll disable this workaround for Rubies that don't need it)
#
# @default `DD_PROFILING_DIR_INTERRUPTION_WORKAROUND_ENABLED` environment variable as a boolean,
# otherwise `true`
option :dir_interruption_workaround_enabled do |o|
o.env 'DD_PROFILING_DIR_INTERRUPTION_WORKAROUND_ENABLED'
o.type :bool
o.default true
end

# Configures how much wall-time overhead the profiler targets. The profiler will dynamically adjust the
# interval between samples it takes so as to try and maintain the property that it spends no longer than
# this amount of wall-clock time profiling. For example, with the default value of 2%, the profiler will
Expand Down
1 change: 1 addition & 0 deletions lib/datadog/profiling.rb
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ def self.allocation_count # rubocop:disable Lint/NestedMethodDefinition (On purp
return false unless supported?

require_relative 'profiling/ext/forking'
require_relative 'profiling/ext/dir_monkey_patches'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be more functionally named? ("what" instead of "how")

WDYT of dir_signal_masking?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with this name because I know it will show up in the profiling flamegraph for customers.

My thinking is that it's clearer if you see "oh, why is there a datadog thing here? ah, it's monkey patching my uses of dir". Signal masking makes me think a bit more along hte lines of "what is this signal masking thing? what is masking anyway?".

Copy link
Contributor

@lloeki lloeki Jul 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, interesting! I was thinking that if I saw that in a stack trace or something I would wonder "why is Datadog monkey patching dir stuff at all?". In a way "monkey patching" is how but not what/why. In a way it's also redundant with ext, the sort of conventional way to say "this folder contains things that extend stuff". dir_signal_fix maybe?

But at this stage that's becoming bikeshedding and certainly not a blocker. Feel free to move forward.

Adding links to upstream issues in the comment at the top of the file would help too I think (I reread it and with only the Datadog issue link it makes it like this is a Datadog issue when it's really an upstream issue)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the joke about naming things being hard applies here >_>. I suspect/fear just relying on ext/ may be too subtle (e.g. when you're looking a flamegraph), but am open to changing if folks get confused about the current name.

+1 I've added a link to the upstream ticket in 259e134.

require_relative 'profiling/collectors/info'
require_relative 'profiling/collectors/code_provenance'
require_relative 'profiling/collectors/cpu_and_wall_time_worker'
Expand Down
13 changes: 13 additions & 0 deletions lib/datadog/profiling/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ def self.build_profiler_component(settings:, agent_settings:, optional_tracer:)
crashtracker = build_crashtracker(settings, transport)
profiler = Profiling::Profiler.new(worker: worker, scheduler: scheduler, optional_crashtracker: crashtracker)

if dir_interruption_workaround_enabled?(settings, no_signals_workaround_enabled)
Datadog::Profiling::Ext::DirMonkeyPatches.apply!
end

[profiler, { profiling_enabled: true }]
end

Expand Down Expand Up @@ -445,6 +449,15 @@ def self.build_profiler_component(settings:, agent_settings:, optional_tracer:)
libmysqlclient_version < Gem::Version.new('5.0.0') &&
header_version >= Gem::Version.new('10.0.0'))
end

private_class_method def self.dir_interruption_workaround_enabled?(settings, no_signals_workaround_enabled)
return false if no_signals_workaround_enabled

# NOTE: In the future this method will evolve to check for Ruby versions affected and not apply the workaround
# when it's not needed but currently all known Ruby versions are affected.

settings.profiling.advanced.dir_interruption_workaround_enabled
end
end
end
end
223 changes: 223 additions & 0 deletions lib/datadog/profiling/ext/dir_monkey_patches.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
# frozen_string_literal: true

module Datadog
module Profiling
module Ext
# All Ruby versions as of this writing have bugs in the dir class implementation, causing issues such as
# https://github.com/DataDog/dd-trace-rb/issues/3450 .
#
# This monkey patch for the Ruby `Dir` class works around these bugs for affected Ruby versions by temporarily
# blocking the profiler from interrupting system calls.
#
# A lot of these APIs do very similar things -- they're provided by Ruby as helpers so users don't need to keep
# reimplementing them but share the same underlying buggy code. And so our monkey patches are a bit repetitive
# as well.
# We don't DRY out this file to have minimal overhead.
#
# These monkey patches are applied by the profiler when the "dir_interruption_workaround_enabled" setting is
# enabled. See the profiling settings for more detail.
module DirMonkeyPatches
def self.apply!
::Dir.singleton_class.prepend(Datadog::Profiling::Ext::DirClassMonkeyPatches)
::Dir.prepend(Datadog::Profiling::Ext::DirInstanceMonkeyPatches)

true
end
end

# Monkey patches for Dir.singleton_class. See DirMonkeyPatches above for more details.
module DirClassMonkeyPatches
def [](*args, &block)
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super
ensure
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
ruby2_keywords :[] if respond_to?(:ruby2_keywords, true)
lloeki marked this conversation as resolved.
Show resolved Hide resolved

def children(*args, &block)
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super
ensure
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
ruby2_keywords :children if respond_to?(:ruby2_keywords, true)

def each_child(*args, &block)
if block
begin
# <-- Begin critical region
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super do |entry_name|
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
# <-- We're safe now while running customer code
yield entry_name
# <-- We'll go back to the Dir internals, critical region again
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
lloeki marked this conversation as resolved.
Show resolved Hide resolved
end
ensure
# <-- End critical region
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
else
# This returns an enumerator. We don't want/need to intercede here, the enumerator will eventually call the
# other branch once it gets going.
super
end
end
ruby2_keywords :each_child if respond_to?(:ruby2_keywords, true)

def empty?(*args, &block)
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super
ensure
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
ruby2_keywords :empty? if respond_to?(:ruby2_keywords, true)

def entries(*args, &block)
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super
ensure
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
ruby2_keywords :entries if respond_to?(:ruby2_keywords, true)

def foreach(*args, &block)
if block
begin
# <-- Begin critical region
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super do |entry_name|
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
# <-- We're safe now while running customer code
yield entry_name
# <-- We'll go back to the Dir internals, critical region again
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
lloeki marked this conversation as resolved.
Show resolved Hide resolved
end
ensure
# <-- End critical region
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
else
# This returns an enumerator. We don't want/need to intercede here, the enumerator will eventually call the
# other branch once it gets going.
super
end
end
ruby2_keywords :foreach if respond_to?(:ruby2_keywords, true)

def glob(*args, &block)
if block
begin
# <-- Begin critical region
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super do |entry_name|
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
# <-- We're safe now while running customer code
yield entry_name
# <-- We'll go back to the Dir internals, critical region again
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
lloeki marked this conversation as resolved.
Show resolved Hide resolved
end
ensure
# <-- End critical region
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
else
begin
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super
ensure
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
end
end
ruby2_keywords :glob if respond_to?(:ruby2_keywords, true)

def home(*args, &block)
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super
ensure
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
ruby2_keywords :home if respond_to?(:ruby2_keywords, true)
end

# Monkey patches for Dir. See DirMonkeyPatches above for more details.
module DirInstanceMonkeyPatches
def each(*args, &block)
if block
begin
# <-- Begin critical region
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super do |entry_name|
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
# <-- We're safe now while running customer code
yield entry_name
# <-- We'll go back to the Dir internals, critical region again
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be in an ensure as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I don't think so...? It's ok to deliver signals during exception handling, so if an exception gets raised in the client code, it should be fine for the profiler to continue operating from them on, since no more directory calls will be made by Ruby.

Or do you mean making sure that _native_hold_interruptions never raises or something similar?

Copy link
Contributor

@lloeki lloeki Jul 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't know, hence why I ask ;)

I'm basing myself on the following outline:

begin
  # [A+] enter our code: hold
  super do
    # [B-] exit our code back to not-our-code: resume
    yield
    # [B+] enter our code: hold
  end
ensure
  # [A-] exit our code: resume
end

On the super happy path the execution order is [A+] [B-] [B+] [A-].

But if yield raises it becomes [A+] [B-] [A-], which in my mind triggers some "consistency" alarms and makes me wonder about internal details:

  • what happens when _native_resume_signals is called twice in a row? is it a NOOP? is it doing work twice (e.g invoking a syscall) but idempotent?
  • there seems to be work to be protected after yield (because of that [B+]) and before method exit ([A-]) so I presume that work is in super, but if yield raises there may be work to protect too in a hypothetical ensure block inside that super too? Or is [B+] entirely superfluous even in the happy yield case?

Conversely, imagine an abstraction like so:

module SignalHolder
  def hold
	# hold
    yield
  ensure
    # resume
  end

  def ignore
    # resume
    yield
  ensure
    # hold
  end
end

And the code becoming:

SignalHolder.hold do
  super { SignalHolder.ignore { yield } }
end

The above is not even a suggestion it's merely to materialise and compare the kind of structural "consistency".

It could simply instead just need a comment to prevent the mind tripwire or help someone who would refactor this code in the future for some reason.

But really, I'd say it's fine to merge if you're confident and my concerns are unwarranted.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks for explaining!

So, in parts:

  • Yes, it's OK if the yield raises. Specifically:
    • We can call _native_resume_signals many times in a row.

      As you suspected, it leads to pthread_sigmask which I believe on Linux will lead to the rt_sigprocmask system call being called.
      I've included a benchmark in the PR of what the cost of this is, and here's a run I did:

      ruby 3.1.4p223 (2023-03-30 revision 957bb7cb81) [x86_64-linux]
      Warming up --------------------------------------
             hold / resume   391.856k i/100ms
      Calculating -------------------------------------
             hold / resume      3.829M (± 1.9%) i/s -     38.402M in  10.033530s
      

      ...so I think we're fine if we end up calling it a few more times than strictly needed.

    • The root of the issue in the Ruby VM happens because in the affected APIs Ruby silently interprets an interruption as "end of folder/empty folder". When an exception is being raised, the iteration will stop anyway, so there's no longer a concern of the interruption causing a semantic mismatch (at least that I can see).

I've pushed 259e134 to add this note to the code as in hindsight I agree it was not very clear before (and hopefully this clarifies it)

end
ensure
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions # <-- End critical region
end
else
# This returns an enumerator. We don't want/need to intercede here, the enumerator will eventually call the
# other branch once it gets going.
super
end
end
ruby2_keywords :each if respond_to?(:ruby2_keywords, true)

unless RUBY_VERSION.start_with?('2.5.') # This is Ruby 2.6+
p-datadog marked this conversation as resolved.
Show resolved Hide resolved
def each_child(*args, &block)
if block
begin
# <-- Begin critical region
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super do |entry_name|
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
# <-- We're safe now while running customer code
yield entry_name
# <-- We'll go back to the Dir internals, critical region again
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
lloeki marked this conversation as resolved.
Show resolved Hide resolved
end
ensure
# <-- End critical region
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
else
# This returns an enumerator. We don't want/need to intercede here, the enumerator will eventually call the
# other branch once it gets going.
super
end
end
ruby2_keywords :each_child if respond_to?(:ruby2_keywords, true)

def children(*args, &block)
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super
ensure
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
ruby2_keywords :children if respond_to?(:ruby2_keywords, true)
end

def tell(*args, &block)
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super
ensure
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
ruby2_keywords :tell if respond_to?(:ruby2_keywords, true)

def pos(*args, &block)
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_hold_interruptions
super
ensure
Datadog::Profiling::Collectors::CpuAndWallTimeWorker._native_resume_interruptions
end
ruby2_keywords :pos if respond_to?(:ruby2_keywords, true)
end
end
end
end
3 changes: 3 additions & 0 deletions sig/datadog/profiling.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ module Datadog
def self.allocation_count: () -> ::Integer?
def self.enabled?: () -> bool
def self.wait_until_running: () -> true

private

def self.replace_noop_allocation_count: () -> void
def self.native_library_compilation_skipped?: () -> ::String?
def self.try_reading_skipped_reason_file: (?untyped file_api) -> ::String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ module Datadog
def self._native_is_running?: (CpuAndWallTimeWorker self_instance) -> bool
def self._native_allocation_count: () -> ::Integer?
def self._native_sampling_loop: (CpuAndWallTimeWorker self_instance) -> void
def self._native_hold_interruptions: () -> void
def self._native_resume_interruptions: () -> void

def wait_until_running: (timeout_seconds: ::Integer?) -> true
end
Expand Down
Loading
Loading