Skip to content

Commit 99fc91f

Browse files
committed
[GR-13219] JT: Forward signals to sub-processes, so they get notified when sending a signal to jt
PullRequest: truffleruby/519
2 parents 8efa3fd + 9af6822 commit 99fc91f

File tree

1 file changed

+98
-73
lines changed

1 file changed

+98
-73
lines changed

tool/jt.rb

Lines changed: 98 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
require 'json'
1818
require 'timeout'
1919
require 'yaml'
20-
require 'open3'
2120
require 'rbconfig'
2221
require 'pathname'
2322

@@ -53,9 +52,6 @@
5352

5453
require "#{TRUFFLERUBY_DIR}/lib/truffle/truffle/openssl-prefix.rb"
5554

56-
# wait for sub-processes to handle the interrupt
57-
trap(:INT) {}
58-
5955
MRI_TEST_MODULES = {
6056
'--no-sulong' => {
6157
help: 'exclude all tests requiring Sulong',
@@ -81,6 +77,23 @@
8177
}
8278
}
8379

80+
SUBPROCESSES = []
81+
82+
# Forward signals to sub-processes, so they get notified when sending a signal to jt
83+
[:SIGINT, :SIGTERM].each do |signal|
84+
trap(signal) do
85+
SUBPROCESSES.each do |pid|
86+
puts "\nSending #{signal} to process #{pid}"
87+
begin
88+
Process.kill(signal, pid)
89+
rescue Errno::ESRCH
90+
# Already killed
91+
end
92+
end
93+
# Keep running jt which will wait for the subprocesses termination
94+
end
95+
end
96+
8497
module Utilities
8598
private
8699

@@ -131,7 +144,7 @@ def find_graal_javacmd_and_options
131144
options = ['--no-bootclasspath']
132145
elsif graal_home
133146
graal_home = File.expand_path(graal_home, TRUFFLERUBY_DIR)
134-
output, _ = mx('-v', '-p', graal_home, 'vm', '-version', :err => :out, capture: true)
147+
output = mx('-v', '-p', graal_home, 'vm', '-version', capture: true, :err => :out)
135148
command_line = output.lines.select { |line| line.include? '-version' }
136149
if command_line.size == 1
137150
command_line = command_line[0]
@@ -288,25 +301,34 @@ def diff(expected, actual)
288301
`diff -u #{expected} #{actual}`
289302
end
290303

291-
def system_timeout(timeout, *args)
292-
begin
293-
pid = Process.spawn(*args)
294-
rescue SystemCallError
295-
return nil
296-
end
304+
def raw_sh_failed_status
305+
`false`
306+
$?
307+
end
297308

298-
begin
299-
Timeout.timeout timeout do
300-
Process.waitpid pid
301-
$?.success?
309+
def raw_sh_with_timeout(timeout, pid)
310+
if !timeout
311+
yield
312+
else
313+
begin
314+
Timeout.timeout(timeout) do
315+
yield
316+
end
317+
rescue Timeout::Error
318+
Process.kill('TERM', pid)
319+
yield # Wait and read the pipe if capture: true
320+
:timeout
302321
end
303-
rescue Timeout::Error
304-
Process.kill('TERM', pid)
305-
Process.waitpid pid
306-
nil
307322
end
308323
end
309324

325+
def raw_sh_track_subprocess(pid)
326+
SUBPROCESSES << pid
327+
yield
328+
ensure
329+
SUBPROCESSES.delete(pid)
330+
end
331+
310332
def raw_sh(*args)
311333
options = args.last.is_a?(Hash) ? args.last : {}
312334
continue_on_failure = options.delete :continue_on_failure
@@ -318,44 +340,51 @@ def raw_sh(*args)
318340
STDERR.puts "$ #{printable_cmd(args)}"
319341
end
320342

321-
if use_exec
322-
result = exec(*args)
323-
elsif timeout
324-
result = system_timeout(timeout, *args)
325-
elsif capture
326-
if options.delete(:out) == :err
327-
err, status = Open3.capture2e(*args)
328-
out = ""
329-
else
330-
out, err, status = Open3.capture3(*args)
331-
end
332-
result = status.success?
333-
else
334-
result = system(*args)
343+
exec(*args) if use_exec
344+
345+
if capture
346+
raise ":capture can only be combined with :err => :out" if options.include?(:out)
347+
pipe_r, pipe_w = IO.pipe
348+
options[:out] = pipe_w
349+
options[:err] = pipe_w if options[:err] == :out
335350
end
336351

337-
if result
338-
if capture
339-
[out, err]
340-
else
341-
true
342-
end
343-
elsif continue_on_failure
344-
false
352+
status = nil
353+
out = nil
354+
begin
355+
pid = Process.spawn(*args)
356+
rescue Errno::ENOENT # No such executable
357+
status = raw_sh_failed_status
345358
else
346-
status = $? unless capture
347-
$stderr.puts "FAILED (#{status}): #{printable_cmd(args)}"
359+
raw_sh_track_subprocess(pid) do
360+
pipe_w.close if capture
348361

349-
if capture
350-
$stderr.puts out
351-
$stderr.puts err
362+
result = raw_sh_with_timeout(timeout, pid) do
363+
out = pipe_r.read if capture
364+
_, status = Process.waitpid2(pid)
365+
end
366+
if result == :timeout
367+
status = raw_sh_failed_status
368+
end
352369
end
370+
end
353371

354-
if status && status.exitstatus
355-
exit status.exitstatus
372+
result = status.success?
373+
374+
if capture
375+
pipe_r.close
376+
end
377+
378+
if status.success? || continue_on_failure
379+
if capture
380+
out
356381
else
357-
exit 1
382+
status.success?
358383
end
384+
else
385+
$stderr.puts "FAILED (#{status}): #{printable_cmd(args)}"
386+
$stderr.puts out if capture
387+
exit status.to_i
359388
end
360389
end
361390

@@ -794,7 +823,7 @@ def pr_clean(*args)
794823
puts "Open PRs: #{open_prs}"
795824

796825
sh 'git', 'fetch', Remotes.bitbucket, '--prune' # ensure we have locally only existing remote branches
797-
branches, _ = sh 'git', 'branch', '--remote', '--list', capture: true
826+
branches = sh 'git', 'branch', '--remote', '--list', capture: true
798827
branches_to_delete = branches.
799828
scan(/^ *#{Remotes.bitbucket}\/(github\/pr\/(\d+))$/).
800829
reject { |_, number| open_prs.include? Integer(number) }
@@ -814,7 +843,7 @@ def pr_clean(*args)
814843
def pr_push(*args)
815844
# Fetch PRs on GitHub
816845
fetch = "+refs/pull/*/head:refs/remotes/#{Remotes.github}/pr/*"
817-
out, _err = sh 'git', 'config', '--get-all', "remote.#{Remotes.github}.fetch", capture: true
846+
out = sh 'git', 'config', '--get-all', "remote.#{Remotes.github}.fetch", capture: true
818847
sh 'git', 'config', '--add', "remote.#{Remotes.github}.fetch", fetch unless out.include? fetch
819848
sh 'git', 'fetch', Remotes.github
820849

@@ -823,7 +852,7 @@ def pr_push(*args)
823852
github_pr_branch = "#{Remotes.github}/pr/#{pr_number}"
824853
else
825854
github_pr_branch = begin
826-
out, _err = sh 'git', 'branch', '-r', '--contains', 'HEAD', capture: true
855+
out = sh 'git', 'branch', '-r', '--contains', 'HEAD', capture: true
827856
candidate = out.lines.find { |l| l.strip.start_with? "#{Remotes.github}/pr/" }
828857
candidate && candidate.strip.chomp
829858
end
@@ -868,9 +897,9 @@ def github(dir = TRUFFLERUBY_DIR)
868897
def remote_urls(dir = TRUFFLERUBY_DIR)
869898
@remote_urls ||= Hash.new
870899
@remote_urls[dir] ||= begin
871-
out, _err = raw_sh 'git', '-C', dir, 'remote', capture: true, no_print_cmd: true
900+
out = raw_sh 'git', '-C', dir, 'remote', capture: true, no_print_cmd: true
872901
out.split.map do |remote|
873-
url, _err = raw_sh 'git', '-C', dir, 'config', '--get', "remote.#{remote}.url", capture: true, no_print_cmd: true
902+
url = raw_sh 'git', '-C', dir, 'config', '--get', "remote.#{remote}.url", capture: true, no_print_cmd: true
874903
[remote, url.chomp]
875904
end
876905
end
@@ -1112,7 +1141,7 @@ def test_cexts(*args)
11121141

11131142
sh 'clang', '-c', '-emit-llvm', *openssl_cflags, 'test/truffle/cexts/xopenssl/main.c', '-o', 'test/truffle/cexts/xopenssl/main.bc'
11141143
mx 'build', '--dependencies', 'SULONG_LAUNCHER' # For mx lli
1115-
out, _ = mx('lli', "-Dpolyglot.llvm.libraries=#{openssl_lib}", 'test/truffle/cexts/xopenssl/main.bc', capture: true)
1144+
out = mx('lli', "-Dpolyglot.llvm.libraries=#{openssl_lib}", 'test/truffle/cexts/xopenssl/main.bc', capture: true)
11161145
raise out.inspect unless out == "5d41402abc4b2a76b9719d911017c592\n"
11171146

11181147
when 'minimum', 'method', 'module', 'globals', 'backtraces', 'xopenssl'
@@ -1458,7 +1487,6 @@ def build_stats_native_runtime_compilable_methods(*args)
14581487
end
14591488

14601489
def metrics(command, *args)
1461-
trap(:INT) { puts; exit }
14621490
args = args.dup
14631491
case command
14641492
when 'alloc'
@@ -1481,8 +1509,8 @@ def metrics_alloc(*args)
14811509
samples = []
14821510
METRICS_REPS.times do
14831511
log '.', "sampling\n"
1484-
out, err = run_ruby '-J-Dtruffleruby.metrics.memory_used_on_exit=true', '-J-verbose:gc', *args, capture: true, no_print_cmd: true
1485-
samples.push memory_allocated(out+err)
1512+
out = run_ruby '-J-Dtruffleruby.metrics.memory_used_on_exit=true', '-J-verbose:gc', *args, capture: true, :err => :out, no_print_cmd: true
1513+
samples.push memory_allocated(out)
14861514
end
14871515
log "\n", nil
14881516
range = samples.max - samples.min
@@ -1568,12 +1596,12 @@ def metrics_maxrss(*args)
15681596
log '.', "sampling\n"
15691597

15701598
max_rss_in_mb = if LINUX
1571-
out, err = raw_sh('/usr/bin/time', '-v', '--', find_launcher(true), *args, capture: true, no_print_cmd: true)
1572-
err =~ /Maximum resident set size \(kbytes\): (?<max_rss_in_kb>\d+)/m
1599+
out = raw_sh('/usr/bin/time', '-v', '--', find_launcher(true), *args, capture: true, :err => :out, no_print_cmd: true)
1600+
out =~ /Maximum resident set size \(kbytes\): (?<max_rss_in_kb>\d+)/m
15731601
Integer($~[:max_rss_in_kb]) / 1024.0
15741602
elsif MAC
1575-
out, err = raw_sh('/usr/bin/time', '-l', '--', find_launcher(true), *args, capture: true, no_print_cmd: true)
1576-
err =~ /(?<max_rss_in_bytes>\d+)\s+maximum resident set size/m
1603+
out = raw_sh('/usr/bin/time', '-l', '--', find_launcher(true), *args, capture: true, :err => :out, no_print_cmd: true)
1604+
out =~ /(?<max_rss_in_bytes>\d+)\s+maximum resident set size/m
15771605
Integer($~[:max_rss_in_bytes]) / 1024.0 / 1024.0
15781606
else
15791607
raise "Can't measure RSS on this platform."
@@ -1610,9 +1638,9 @@ def metrics_native_instructions(*args)
16101638

16111639
use_json = args.delete '--json'
16121640

1613-
out, err = raw_sh('perf', 'stat', '-e', 'instructions', '--', find_launcher(true), *args, capture: true, no_print_cmd: true)
1641+
out = raw_sh('perf', 'stat', '-e', 'instructions', '--', find_launcher(true), *args, capture: true, :err => :out, no_print_cmd: true)
16141642

1615-
err =~ /(?<instruction_count>[\d,]+)\s+instructions/m
1643+
out =~ /(?<instruction_count>[\d,]+)\s+instructions/m
16161644
instruction_count = $~[:instruction_count].gsub(',', '')
16171645

16181646
log "\n", nil
@@ -1639,9 +1667,9 @@ def metrics_time(*args)
16391667
samples = METRICS_REPS.times.map do
16401668
log '.', "sampling\n"
16411669
start = Time.now
1642-
_, err = run_ruby(*args, capture: true, no_print_cmd: true, :out => :err)
1670+
out = run_ruby(*args, capture: true, no_print_cmd: true, :err => :out)
16431671
finish = Time.now
1644-
get_times(err, (finish - start) * 1000.0)
1672+
get_times(out, (finish - start) * 1000.0)
16451673
end
16461674
log "\n", nil
16471675

@@ -1780,22 +1808,19 @@ def profile(*args)
17801808
run_args = *DEFAULT_PROFILE_OPTIONS + args
17811809

17821810
begin
1783-
profile_data, err = run_ruby(env, *run_args, capture: true)
1784-
$stderr.puts(err) unless err.empty?
1811+
profile_data = run_ruby(env, *run_args, capture: true)
17851812

17861813
profile_data_file = Tempfile.new %w[truffleruby-profile .json]
17871814
profile_data_file.write(profile_data)
17881815
profile_data_file.close
17891816

1790-
flamegraph_data, err = raw_sh "#{repo}/stackcollapse-graalvm.rb", profile_data_file.path, capture: true
1791-
$stderr.puts(err) unless err.empty?
1817+
flamegraph_data = raw_sh "#{repo}/stackcollapse-graalvm.rb", profile_data_file.path, capture: true
17921818

17931819
flamegraph_data_file = Tempfile.new 'truffleruby-flamegraph-data'
17941820
flamegraph_data_file.write(flamegraph_data)
17951821
flamegraph_data_file.close
17961822

1797-
svg_data, err = raw_sh "#{repo}/flamegraph.pl", flamegraph_data_file.path, capture: true
1798-
$stderr.puts(err) unless err.empty?
1823+
svg_data = raw_sh "#{repo}/flamegraph.pl", flamegraph_data_file.path, capture: true
17991824

18001825
Dir.mkdir(PROFILES_DIR) unless Dir.exist?(PROFILES_DIR)
18011826
svg_filename = "#{PROFILES_DIR}/flamegraph_#{Time.now.strftime("%Y%m%d-%H%M%S")}.svg"
@@ -1954,7 +1979,7 @@ def check_filename_length
19541979

19551980
def check_parser
19561981
build('parser')
1957-
diff, _err = sh 'git', 'diff', 'src/main/java/org/truffleruby/parser/parser/RubyParser.java', :err => :out, capture: true
1982+
diff = sh 'git', 'diff', 'src/main/java/org/truffleruby/parser/parser/RubyParser.java', capture: true
19581983
unless diff.empty?
19591984
STDERR.puts "DIFF:"
19601985
STDERR.puts diff

0 commit comments

Comments
 (0)