Skip to content

Commit 4196e11

Browse files
committed
Stop profiling in forked child process
Otherwise the child will hang forever on exit when trying to dump the profiling data. Which is problematic when trying to profile some programs. There might be more elegant ways to handle it, this is mostly a proof of concept. Also it's not quite fully working, the test passes alone, but but if ran after some other specific tests, it end up in a SIGTRAP, which I'm not sure why: ``` bundle exec rake SEED=26641 TESTOPTS="-v" ... 1) Failure: TestVernier#test_that_forked_children_do_not_hang [test/test_vernier.rb:15]: Expected #<Process::Status: pid 63146 SIGTRAP (signal 5)> to be success?. ```
1 parent fe10f55 commit 4196e11

File tree

5 files changed

+46
-0
lines changed

5 files changed

+46
-0
lines changed

lib/vernier.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
require_relative "vernier/stack_table"
66
require_relative "vernier/result"
77
require_relative "vernier/hooks"
8+
require_relative "vernier/fork"
89
require_relative "vernier/vernier"
910
require_relative "vernier/output/firefox"
1011
require_relative "vernier/output/top"
@@ -56,6 +57,13 @@ def self.stop_profile
5657
result
5758
end
5859

60+
def self.cancel_profile
61+
if @collector
62+
@collector.stop
63+
@collector = nil
64+
end
65+
end
66+
5967
def self.trace_retained(**profile_options, &block)
6068
profile(**profile_options.merge(mode: :retained), &block)
6169
end

lib/vernier/collector.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ def record_interval(category, name = category)
7171
)
7272
end
7373

74+
def cancel
75+
finish
76+
@thread_names.cancel
77+
@hooks.each do |hook|
78+
hook.disable
79+
end
80+
nil
81+
end
82+
7483
def stop
7584
result = finish
7685

lib/vernier/fork.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# frozen_string_literal: true
2+
3+
module Vernier
4+
if ::Process.respond_to?(:_fork)
5+
module ForkHooks
6+
def _fork
7+
pid = super
8+
if pid == 0 # We're in the child
9+
Vernier.cancel_profile
10+
end
11+
pid
12+
end
13+
end
14+
15+
::Process.singleton_class.prepend(ForkHooks)
16+
end
17+
end

lib/vernier/thread_names.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ def [](object_id)
1313
@names[object_id] || "thread obj_id:#{object_id}"
1414
end
1515

16+
def cancel
17+
@tp.disable
18+
end
19+
1620
def finish
1721
collect_running
1822
@tp.disable

test/test_vernier.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,12 @@ class TestVernier < Minitest::Test
66
def test_that_it_has_a_version_number
77
refute_nil ::Vernier::VERSION
88
end
9+
10+
def test_that_forked_children_do_not_hang
11+
pid = Process.fork do
12+
# noop
13+
end
14+
_, status = Process.waitpid2(pid)
15+
assert_predicate status, :success?
16+
end
917
end

0 commit comments

Comments
 (0)