crash-watch 1.1.12 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +1 -1
- data/README.markdown +2 -41
- data/Rakefile +22 -557
- data/bin/crash-watch +23 -86
- data/crash-watch.gemspec +11 -13
- data/lib/crash_watch/app.rb +138 -0
- data/lib/crash_watch/base.rb +25 -0
- data/lib/crash_watch/gdb_controller.rb +263 -307
- data/lib/crash_watch/lldb_controller.rb +178 -0
- data/lib/crash_watch/packaging.rb +29 -10
- data/lib/crash_watch/utils.rb +94 -0
- data/lib/crash_watch/version.rb +22 -1
- data/test/controller_sharedspec.rb +34 -0
- data/test/gdb_controller_spec.rb +86 -113
- data/test/lldb_controller_spec.rb +29 -0
- data/test/spec_helper.rb +9 -0
- metadata +18 -45
- data.tar.gz.asc +0 -12
- metadata.gz.asc +0 -12
@@ -0,0 +1,178 @@
|
|
1
|
+
# encoding: binary
|
2
|
+
#
|
3
|
+
# Copyright (c) 2016 Phusion Holding B.V.
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
# a copy of this software and associated documentation files (the
|
7
|
+
# "Software"), to deal in the Software without restriction, including
|
8
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
# the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be
|
14
|
+
# included in all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
require 'rbconfig'
|
25
|
+
require 'crash_watch/base'
|
26
|
+
require 'crash_watch/utils'
|
27
|
+
|
28
|
+
module CrashWatch
|
29
|
+
class LldbNotFound < Error
|
30
|
+
end
|
31
|
+
|
32
|
+
class LldbController
|
33
|
+
class ExitInfo
|
34
|
+
attr_reader :exit_code, :signal, :backtrace, :snapshot
|
35
|
+
|
36
|
+
def initialize(exit_code, signal, backtrace, snapshot)
|
37
|
+
@exit_code = exit_code
|
38
|
+
@signal = signal
|
39
|
+
@backtrace = backtrace
|
40
|
+
@snapshot = snapshot
|
41
|
+
end
|
42
|
+
|
43
|
+
def signaled?
|
44
|
+
!!@signal
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
END_OF_RESPONSE_MARKER = '--------END_OF_RESPONSE--------'
|
49
|
+
|
50
|
+
attr_accessor :debug
|
51
|
+
|
52
|
+
def initialize
|
53
|
+
@pid, @in, @out = Utils.popen_command(find_lldb, "-x")
|
54
|
+
execute("settings set prompt 'LLDB:'")
|
55
|
+
execute("settings set auto-confirm false")
|
56
|
+
end
|
57
|
+
|
58
|
+
def execute(command_string, timeout = nil)
|
59
|
+
raise "LLDB session is already closed" if !@pid
|
60
|
+
puts "lldb write #{command_string.inspect}" if @debug
|
61
|
+
marker = "\n#{END_OF_RESPONSE_MARKER}\n"
|
62
|
+
@in.puts(command_string)
|
63
|
+
@in.puts("script print #{marker.inspect}")
|
64
|
+
done = false
|
65
|
+
result = []
|
66
|
+
while !done
|
67
|
+
begin
|
68
|
+
if select([@out], nil, nil, timeout)
|
69
|
+
line = @out.readline.chomp
|
70
|
+
line.sub!(/^LLDB:/, '')
|
71
|
+
puts "lldb read #{line.inspect}" if @debug
|
72
|
+
if line == "#{END_OF_RESPONSE_MARKER}"
|
73
|
+
done = true
|
74
|
+
else
|
75
|
+
result << line
|
76
|
+
end
|
77
|
+
else
|
78
|
+
close!
|
79
|
+
done = true
|
80
|
+
result = nil
|
81
|
+
end
|
82
|
+
rescue EOFError
|
83
|
+
done = true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if result
|
88
|
+
# Remove echo of the command string
|
89
|
+
result.slice!(0, 2)
|
90
|
+
# Remove echo of the marker print command
|
91
|
+
result.pop
|
92
|
+
result.pop
|
93
|
+
|
94
|
+
result.join("\n") << "\n"
|
95
|
+
else
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def closed?
|
101
|
+
!@pid
|
102
|
+
end
|
103
|
+
|
104
|
+
def close
|
105
|
+
if !closed?
|
106
|
+
begin
|
107
|
+
execute("process detach", 5) if !closed?
|
108
|
+
execute("quit", 5) if !closed?
|
109
|
+
rescue Errno::EPIPE
|
110
|
+
end
|
111
|
+
if !closed?
|
112
|
+
@in.close
|
113
|
+
@out.close
|
114
|
+
Process.waitpid(@pid)
|
115
|
+
@pid = nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def close!
|
121
|
+
if !closed?
|
122
|
+
@in.close
|
123
|
+
@out.close
|
124
|
+
Process.kill('KILL', @pid)
|
125
|
+
Process.waitpid(@pid)
|
126
|
+
@pid = nil
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def attach(pid)
|
131
|
+
pid = pid.to_s.strip
|
132
|
+
raise ArgumentError if pid.empty?
|
133
|
+
result = execute("attach -p #{pid}")
|
134
|
+
result !~ /(unable to attach|cannot attach)/
|
135
|
+
end
|
136
|
+
|
137
|
+
def program_counter
|
138
|
+
execute("p/x $pc").gsub(/.* = /, '')
|
139
|
+
end
|
140
|
+
|
141
|
+
def current_thread
|
142
|
+
execute("thread info") =~ /^thread #(.+?): /
|
143
|
+
$1
|
144
|
+
end
|
145
|
+
|
146
|
+
def current_thread_backtrace
|
147
|
+
execute("bt").strip
|
148
|
+
end
|
149
|
+
|
150
|
+
def all_threads_backtraces
|
151
|
+
execute("bt all").strip
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
def find_lldb
|
156
|
+
result = nil
|
157
|
+
if ENV['LLDB'] && File.executable?(ENV['LLDB'])
|
158
|
+
result = ENV['LLDB']
|
159
|
+
else
|
160
|
+
ENV['PATH'].to_s.split(/:+/).each do |path|
|
161
|
+
filename = "#{path}/lldb"
|
162
|
+
if File.file?(filename) && File.executable?(filename)
|
163
|
+
result = filename
|
164
|
+
break
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
puts "Found lldb at: #{result}" if result
|
170
|
+
|
171
|
+
if result.nil?
|
172
|
+
raise LldbNotFound
|
173
|
+
else
|
174
|
+
result
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -1,15 +1,34 @@
|
|
1
|
+
# Copyright (c) 2010-2016 Phusion Holding B.V.
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
1
22
|
CRASH_WATCH_FILES = [
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
23
|
+
"README.markdown",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"Rakefile",
|
26
|
+
"crash-watch.gemspec",
|
27
|
+
"bin/**/*",
|
28
|
+
"lib/**/*",
|
29
|
+
"test/**/*"
|
9
30
|
]
|
10
31
|
|
11
32
|
CRASH_WATCH_EXCLUDE_FILES = [
|
12
|
-
|
13
|
-
"debian.template/**/*",
|
14
|
-
"rpm/**/*"
|
33
|
+
"Rakefile"
|
15
34
|
]
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# encoding: binary
|
2
|
+
#
|
3
|
+
# Copyright (c) 2016 Phusion Holding B.V.
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
# a copy of this software and associated documentation files (the
|
7
|
+
# "Software"), to deal in the Software without restriction, including
|
8
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
# the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be
|
14
|
+
# included in all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
module CrashWatch
|
25
|
+
module Utils
|
26
|
+
extend Utils
|
27
|
+
|
28
|
+
def self.included(klass)
|
29
|
+
# When included into another class, make sure that Utils
|
30
|
+
# methods are made private.
|
31
|
+
public_instance_methods(false).each do |method_name|
|
32
|
+
klass.send(:private, method_name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def gdb_installed?
|
37
|
+
command_installed?('gdb')
|
38
|
+
end
|
39
|
+
|
40
|
+
def lldb_installed?
|
41
|
+
command_installed?('lldb')
|
42
|
+
end
|
43
|
+
|
44
|
+
def command_installed?(command)
|
45
|
+
path = ENV['PATH'].to_s
|
46
|
+
path.split(File::PATH_SEPARATOR).each do |dir|
|
47
|
+
next if dir.empty?
|
48
|
+
filename = "#{dir}/#{command}"
|
49
|
+
if File.file?(filename) && File.executable?(filename)
|
50
|
+
return true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
def popen_command(*command)
|
57
|
+
a, b = IO.pipe
|
58
|
+
c, d = IO.pipe
|
59
|
+
if Process.respond_to?(:spawn)
|
60
|
+
args = command.dup
|
61
|
+
args << {
|
62
|
+
STDIN => a,
|
63
|
+
STDOUT => d,
|
64
|
+
STDERR => d,
|
65
|
+
:close_others => true
|
66
|
+
}
|
67
|
+
pid = Process.spawn(*args)
|
68
|
+
else
|
69
|
+
pid = fork do
|
70
|
+
STDIN.reopen(a)
|
71
|
+
STDOUT.reopen(d)
|
72
|
+
STDERR.reopen(d)
|
73
|
+
b.close
|
74
|
+
c.close
|
75
|
+
exec(*command)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
a.close
|
79
|
+
d.close
|
80
|
+
b.binmode
|
81
|
+
c.binmode
|
82
|
+
[pid, b, c]
|
83
|
+
end
|
84
|
+
|
85
|
+
def find_signal_name(signo)
|
86
|
+
Signal.list.each_pair do |name, number|
|
87
|
+
if number == signo
|
88
|
+
return name
|
89
|
+
end
|
90
|
+
end
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/crash_watch/version.rb
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
# Copyright (c) 2010-2016 Phusion Holding B.V.
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
1
22
|
module CrashWatch
|
2
|
-
|
23
|
+
VERSION_STRING = '1.2.0'
|
3
24
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
shared_examples_for 'a CrashWatch controller' do
|
4
|
+
def run_script_and_wait(code, snapshot_callback = nil, &block)
|
5
|
+
@process = IO.popen(%Q{exec ruby -e #{Shellwords.escape code}}, 'w')
|
6
|
+
@gdb.attach(@process.pid)
|
7
|
+
thread = Thread.new do
|
8
|
+
sleep 0.1
|
9
|
+
if block
|
10
|
+
block.call
|
11
|
+
end
|
12
|
+
@process.write("\n")
|
13
|
+
end
|
14
|
+
exit_info = @gdb.wait_until_exit(&snapshot_callback)
|
15
|
+
thread.join
|
16
|
+
exit_info
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#attach" do
|
20
|
+
before :each do
|
21
|
+
@process = IO.popen("exec ruby -e 'sleep 9999'", "w")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns true if attaching worked" do
|
25
|
+
expect(@gdb.attach(@process.pid)).to be_truthy
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns false if the PID doesn't exist" do
|
29
|
+
Process.kill('KILL', @process.pid)
|
30
|
+
sleep 0.25
|
31
|
+
expect(@gdb.attach(@process.pid)).to be_falsey
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/test/gdb_controller_spec.rb
CHANGED
@@ -1,116 +1,89 @@
|
|
1
1
|
source_root = File.expand_path(File.dirname(__FILE__) + "/..")
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
require "#{source_root}/test/spec_helper"
|
3
|
+
require "#{source_root}/test/controller_sharedspec"
|
5
4
|
require 'crash_watch/gdb_controller'
|
5
|
+
require 'crash_watch/utils'
|
6
|
+
|
7
|
+
if CrashWatch::Utils.gdb_installed?
|
8
|
+
describe CrashWatch::GdbController do
|
9
|
+
before :each do
|
10
|
+
@gdb = CrashWatch::GdbController.new
|
11
|
+
end
|
12
|
+
|
13
|
+
after :each do
|
14
|
+
@gdb.close
|
15
|
+
if @process
|
16
|
+
Process.kill('KILL', @process.pid)
|
17
|
+
@process.close
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
include_examples 'a CrashWatch controller'
|
22
|
+
|
23
|
+
describe "#execute" do
|
24
|
+
it "executes the desired command and returns its output" do
|
25
|
+
expect(@gdb.execute("echo hello world")).to eq("hello world\n")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#wait_until_exit" do
|
30
|
+
it "returns the expected information if the process exited normally" do
|
31
|
+
exit_info = run_script_and_wait('STDIN.readline')
|
32
|
+
expect(exit_info.exit_code).to eq(0)
|
33
|
+
expect(exit_info).not_to be_signaled
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns the expected information if the process exited with a non-zero exit code" do
|
37
|
+
exit_info = run_script_and_wait('STDIN.readline; exit 3')
|
38
|
+
expect(exit_info.exit_code).to eq(3)
|
39
|
+
expect(exit_info).not_to be_signaled
|
40
|
+
expect(exit_info.backtrace).not_to be_nil
|
41
|
+
expect(exit_info.backtrace).not_to be_empty
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns the expected information if the process exited because of a signal" do
|
45
|
+
exit_info = run_script_and_wait(
|
46
|
+
'STDIN.readline;' +
|
47
|
+
'require "rubygems";' +
|
48
|
+
'require "ffi";' +
|
49
|
+
'module MyLib;' +
|
50
|
+
'extend FFI::Library;' +
|
51
|
+
'ffi_lib "c";' +
|
52
|
+
'attach_function :abort, [], :void;' +
|
53
|
+
'end;' +
|
54
|
+
'MyLib.abort')
|
55
|
+
expect(exit_info).to be_signaled
|
56
|
+
expect(exit_info.backtrace).to match(/abort/)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "ignores non-fatal signals" do
|
60
|
+
exit_info = run_script_and_wait('trap("INT") { }; STDIN.readline; exit 2') do
|
61
|
+
Process.kill('INT', @process.pid)
|
62
|
+
end
|
63
|
+
expect(exit_info.exit_code).to eq(2)
|
64
|
+
expect(exit_info).not_to be_signaled
|
65
|
+
expect(exit_info.backtrace).not_to be_nil
|
66
|
+
expect(exit_info.backtrace).not_to be_empty
|
67
|
+
end
|
6
68
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@process.write("\n")
|
29
|
-
end
|
30
|
-
exit_info = @gdb.wait_until_exit(&snapshot_callback)
|
31
|
-
thread.join
|
32
|
-
return exit_info
|
33
|
-
end
|
34
|
-
|
35
|
-
describe "#execute" do
|
36
|
-
it "executes the desired command and returns its output" do
|
37
|
-
@gdb.execute("echo hello world").should == "hello world\n"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
describe "#attach" do
|
42
|
-
before :each do
|
43
|
-
@process = IO.popen("sleep 9999", "w")
|
44
|
-
end
|
45
|
-
|
46
|
-
it "returns true if attaching worked" do
|
47
|
-
@gdb.attach(@process.pid).should be_true
|
48
|
-
end
|
49
|
-
|
50
|
-
it "returns false if the PID doesn't exist" do
|
51
|
-
Process.kill('KILL', @process.pid)
|
52
|
-
sleep 0.25
|
53
|
-
@gdb.attach(@process.pid).should be_false
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
describe "#wait_until_exit" do
|
58
|
-
it "returns the expected information if the process exited normally" do
|
59
|
-
exit_info = run_script_and_wait('STDIN.readline')
|
60
|
-
exit_info.exit_code.should == 0
|
61
|
-
exit_info.should_not be_signaled
|
62
|
-
end
|
63
|
-
|
64
|
-
it "returns the expected information if the process exited with a non-zero exit code" do
|
65
|
-
exit_info = run_script_and_wait('STDIN.readline; exit 3')
|
66
|
-
exit_info.exit_code.should == 3
|
67
|
-
exit_info.should_not be_signaled
|
68
|
-
exit_info.backtrace.should_not be_nil
|
69
|
-
exit_info.backtrace.should_not be_empty
|
70
|
-
end
|
71
|
-
|
72
|
-
it "returns the expected information if the process exited because of a signal" do
|
73
|
-
exit_info = run_script_and_wait(
|
74
|
-
'STDIN.readline;' +
|
75
|
-
'require "rubygems";' +
|
76
|
-
'require "ffi";' +
|
77
|
-
'module MyLib;' +
|
78
|
-
'extend FFI::Library;' +
|
79
|
-
'ffi_lib "c";' +
|
80
|
-
'attach_function :abort, [], :void;' +
|
81
|
-
'end;' +
|
82
|
-
'MyLib.abort')
|
83
|
-
exit_info.should be_signaled
|
84
|
-
exit_info.backtrace.should =~ /abort/
|
85
|
-
end
|
86
|
-
|
87
|
-
it "ignores non-fatal signals" do
|
88
|
-
exit_info = run_script_and_wait('trap("INT") { }; STDIN.readline; exit 2') do
|
89
|
-
Process.kill('INT', @process.pid)
|
90
|
-
end
|
91
|
-
exit_info.exit_code.should == 2
|
92
|
-
exit_info.should_not be_signaled
|
93
|
-
exit_info.backtrace.should_not be_nil
|
94
|
-
exit_info.backtrace.should_not be_empty
|
95
|
-
end
|
96
|
-
|
97
|
-
it "returns information of the signal that aborted the process, not information of ignored signals" do
|
98
|
-
exit_info = run_script_and_wait(
|
99
|
-
'trap("INT") { };' +
|
100
|
-
'STDIN.readline;' +
|
101
|
-
'require "rubygems";' +
|
102
|
-
'require "ffi";' +
|
103
|
-
'module MyLib;' +
|
104
|
-
'extend FFI::Library;' +
|
105
|
-
'ffi_lib "c";' +
|
106
|
-
'attach_function :abort, [], :void;' +
|
107
|
-
'end;' +
|
108
|
-
'MyLib.abort'
|
109
|
-
) do
|
110
|
-
Process.kill('INT', @process.pid)
|
111
|
-
end
|
112
|
-
exit_info.should be_signaled
|
113
|
-
exit_info.backtrace.should =~ /abort/
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
69
|
+
it "returns information of the signal that aborted the process, not information of ignored signals" do
|
70
|
+
exit_info = run_script_and_wait(
|
71
|
+
'trap("INT") { };' +
|
72
|
+
'STDIN.readline;' +
|
73
|
+
'require "rubygems";' +
|
74
|
+
'require "ffi";' +
|
75
|
+
'module MyLib;' +
|
76
|
+
'extend FFI::Library;' +
|
77
|
+
'ffi_lib "c";' +
|
78
|
+
'attach_function :abort, [], :void;' +
|
79
|
+
'end;' +
|
80
|
+
'MyLib.abort'
|
81
|
+
) do
|
82
|
+
Process.kill('INT', @process.pid)
|
83
|
+
end
|
84
|
+
expect(exit_info).to be_signaled
|
85
|
+
expect(exit_info.backtrace).to match(/abort/)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|