backtracer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,39 @@
1
+ ruby_backtracer: a library to output higher quality backtraces if an unhandled exception is raised
2
+
3
+ ex:
4
+ running given script examples/crash.rb used to output:
5
+ examples>ruby crash.rb
6
+ crash.rb:2:in `go2': unhandled exception
7
+ from crash.rb:6:in `go'
8
+ from crash.rb:9
9
+
10
+ now outputs:
11
+ examples>ruby -r../backtrace_with_code_and_locals crash.rb
12
+
13
+ unhandled exception: crash.rb:2: raise
14
+ locals: {"a"=>"3", "b"=>55}
15
+ from:
16
+ crash.rb:1 go2(a=>3, b=>55)
17
+ locals: {"a"=>"3", "b"=>55}
18
+ crash.rb:5 go(a=>3)
19
+ locals: {"a"=>"3"}
20
+
21
+ Now wasn't that prettier?
22
+
23
+ There are several other tracing options provided, if you don't want as much output, or want more speed. Specify which by script name.
24
+
25
+ ex: backtrace_nothing_swallowed.rb outputs the same as the default exception output, except it doesn't have the
26
+ ...skip 24 lines...
27
+ line in the middle (also no speed slowdown, and no local variables displayed).
28
+
29
+ Try them out by running test_all.rb in the examples folder, or eyeball the example output files in examples/example_output*
30
+
31
+ http://github.com/rogerdpack/ruby_backtracer/tree/master
32
+
33
+ Note: some options depends on ruby-debug [MRI] gem, some don't.
34
+
35
+ To install clone from github, above, then ruby -rscriptname your_script.
36
+
37
+ related projects: unroller, http://eigenclass.org/hiki/method+arguments+via+introspection, liveconsole, ruby-debug
38
+
39
+ send comments to rogerdpack on github.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "backtracer"
5
+ gemspec.summary = gemspec.description = "Quality backtraces for ruby"
6
+ gemspec.email = "rogerdpack@gmail.com"
7
+ # gemspec.homepage = "http://github.com/technicalpickles/the-perfect-gem"
8
+ # gemspec.description = "TODO"
9
+ # gemspec.authors = ["Josh Nichols"]
10
+ # gemspec.dependency
11
+ end
12
+ rescue LoadError
13
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
14
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/examples/crash.rb ADDED
@@ -0,0 +1,10 @@
1
+ def go2(a, b)
2
+ raise
3
+ end
4
+
5
+ def go(a);
6
+ go2(a, 55);
7
+ end
8
+
9
+ go '3'
10
+
@@ -0,0 +1,22 @@
1
+ # more "complicated" file to show off backtraces
2
+ def go2(a, b)
3
+ 3
4
+ b = 3
5
+ within_go2 = 4
6
+ raise
7
+ end
8
+ def go(a);
9
+ within_go = 2
10
+ go2(a, 55);
11
+ 'abc'
12
+ end
13
+
14
+ def does_nothing a
15
+
16
+ end
17
+
18
+ does_nothing 3
19
+ does_nothing 4
20
+ does_nothing 5
21
+
22
+ go '3'
@@ -0,0 +1,39 @@
1
+
2
+
3
+
4
+ running ruby -r../backtrace_nothing_swallowed.rb crash.rb
5
+ crash.rb:2:in `go2': unhandled exception
6
+ from crash.rb:6:in `go'
7
+ from crash.rb:9
8
+ ====
9
+ crash.rb:2:in `go2'
10
+ crash.rb:6:in `go'
11
+ crash.rb:9
12
+ ====
13
+
14
+
15
+
16
+ running ruby -r../backtrace_with_code.rb crash.rb
17
+ crash.rb:2:in `go2': unhandled exception
18
+ from crash.rb:6:in `go'
19
+ from crash.rb:9
20
+ ====
21
+ crash.rb:2:in `go2'
22
+ raise
23
+ crash.rb:6:in `go'
24
+ go2(a, 55);
25
+ crash.rb:9
26
+ go '3'
27
+ ====
28
+
29
+
30
+
31
+ running ruby -r../backtrace_with_code_and_locals.rb crash.rb
32
+
33
+ unhandled exception: crash.rb:2: raise
34
+ locals: {"a"=>"3", "b"=>55}
35
+ from:
36
+ crash.rb:1 go2(a=>3, b=>55)
37
+ locals: {"a"=>"3", "b"=>55}
38
+ crash.rb:5 go(a=>3)
39
+ locals: {"a"=>"3"}
@@ -0,0 +1,42 @@
1
+ ruby 1.8.6 (2009-3-4 mbari 8B/0x8770 on patchlevel 287) [i686-linux]
2
+ #0:crash.rb:1::-: def go2(a, b)
3
+ |#0:crash.rb:1:Module:>: def go2(a, b)
4
+ args were []
5
+ #0:crash.rb:1:Module:<: def go2(a, b)
6
+ #0:crash.rb:5::-: def go(a);
7
+ |#0:crash.rb:5:Module:>: def go(a);
8
+ args were []
9
+ #0:crash.rb:5:Module:<: def go(a);
10
+ #0:crash.rb:9::-: go '3'
11
+ |#0:crash.rb:5:Object:>: def go(a);
12
+ args were [["a", "3"]]
13
+ |#0:crash.rb:6:Object:-: go2(a, 55);
14
+ | |#0:crash.rb:1:Object:>: def go2(a, b)
15
+ args were [["a", "3"], ["b", 55]]
16
+ | |#0:crash.rb:2:Object:-: raise
17
+ | | |#0:crash.rb:2:Kernel:>: raise
18
+ args were []
19
+ | | | |#0:crash.rb:2:Class:>: raise
20
+ args were []
21
+ | | | | |#0:crash.rb:2:Exception:>: raise
22
+ args were []
23
+ | | | |#0:crash.rb:2:Exception:<: raise
24
+ | | |#0:crash.rb:2:Class:<: raise
25
+ | | | |#0:crash.rb:2:Exception:>: raise
26
+ args were []
27
+ | | |#0:crash.rb:2:Exception:<: raise
28
+ | | | |#0:crash.rb:2:Exception:>: raise
29
+ args were []
30
+ | | |#0:crash.rb:2:Exception:<: raise
31
+ | | |#0:crash.rb:2:Object:R: raise
32
+ | |#0:crash.rb:2:Kernel:<: raise
33
+ |#0:crash.rb:3:Object:<: end
34
+ #0:crash.rb:7:Object:<: end
35
+
36
+ unhandled exception: crash.rb:2: raise
37
+ locals: {"a"=>"3", "b"=>55}
38
+ from:
39
+ crash.rb:1 go2(a=>3, b=>55)
40
+ locals: {"a"=>"3", "b"=>55}
41
+ crash.rb:5 go(a=>3)
42
+ locals: {"a"=>"3"}
@@ -0,0 +1,12 @@
1
+ for file in Dir.glob("../lib/backtrace_*") do
2
+ commands = []
3
+ for crash_file in ['crash.rb'] do
4
+ commands << "ruby -r#{file} #{crash_file}"
5
+ end
6
+ #commands << "ruby -v -r../backtrace_with_code_and_locals.rb ../crash.rb"
7
+ for command in commands
8
+ puts "\n\n\nrunning #{command}\n"
9
+ system(command)
10
+ end
11
+
12
+ end
@@ -0,0 +1 @@
1
+ system "ruby -v -r../lib/backtrace_with_code_and_locals.rb crash.rb"
@@ -0,0 +1,6 @@
1
+ # this one is easy
2
+ at_exit {
3
+ puts "==== "
4
+ puts $!.backtrace.join("\n")
5
+ puts "===="
6
+ }
@@ -0,0 +1,2 @@
1
+ # this is the default
2
+ require File.dirname(__FILE__) + '/core_backtracer.rb'
data/lib/backtracer.rb ADDED
@@ -0,0 +1,16 @@
1
+ # this one display full BT with code, at the end [no performance loss]
2
+ SCRIPT_LINES__ = {}
3
+ at_exit {
4
+ puts "==== "
5
+
6
+ backtrace_with_code = $!.backtrace.map{|bt|
7
+ file, line, junk = bt.split(":")
8
+ line = line.to_i - 1
9
+ actual_file = SCRIPT_LINES__[file]
10
+ actual_line = actual_file[line] if actual_file
11
+ "#{bt}\n\t#{actual_line.strip if actual_line}"
12
+ }
13
+
14
+ puts backtrace_with_code
15
+ puts "===="
16
+ }
@@ -0,0 +1,277 @@
1
+ #
2
+ # backtracer.rb - display backtrace of most recent error on exit [quality back trace, mind you]
3
+ # based off tracer.rb, unroller, python
4
+ # original tracer.rb by Keiju ISHITSUKA(Nippon Rational Inc.)
5
+ # Tons of the code is useless and could be removed, but at least it works
6
+
7
+ require 'rubygems'
8
+ require 'ruby-debug'
9
+ Debugger.start # we use this to track args.
10
+
11
+ #Debugger.keep_frame_binding = true # whatever this did :P
12
+ #
13
+ # tracer main class
14
+ #
15
+ class Tracer
16
+ @RCS_ID='-$Id: tracer.rb,v 1.8 1998/05/19 03:42:49 keiju Exp keiju $-'
17
+
18
+ @stdout = STDOUT
19
+ @verbose = false
20
+ class << self
21
+ attr :verbose, true
22
+ alias verbose? verbose
23
+ attr :stdout, true
24
+ end
25
+
26
+ EVENT_SYMBOL = {
27
+ "line" => "-",
28
+ "call" => ">",
29
+ "return" => "<",
30
+ "class" => "C",
31
+ "end" => "E",
32
+ "c-call" => ">",
33
+ "c-return" => "<",
34
+ "raise" => "R"
35
+ }
36
+
37
+ def initialize
38
+ @threads = Hash.new
39
+ if defined? Thread.main
40
+ @threads[Thread.main.object_id] = 0
41
+ else
42
+ @threads[Thread.current.object_id] = 0
43
+ end
44
+
45
+ @get_line_procs = {}
46
+
47
+ @filters = []
48
+ end
49
+
50
+ def stdout
51
+ Tracer.stdout
52
+ end
53
+
54
+ def on
55
+ if block_given?
56
+ on
57
+ begin
58
+ yield
59
+ ensure
60
+ off
61
+ end
62
+ else
63
+ set_trace_func method(:trace_func).to_proc
64
+ stdout.print "Trace on\n" if Tracer.verbose?
65
+ end
66
+ end
67
+
68
+ def off
69
+ set_trace_func nil
70
+ stdout.print "Trace off\n" if Tracer.verbose?
71
+ end
72
+
73
+ def add_filter(p = proc)
74
+ @filters.push p
75
+ end
76
+
77
+ def set_get_line_procs(file, p = proc)
78
+ @get_line_procs[file] = p
79
+ end
80
+
81
+ def get_line(file, line)
82
+ self.class.get_line(file, line)
83
+ end
84
+
85
+ def self.get_line(file, line)
86
+ @get_line_procs ||= {}
87
+ if p = @get_line_procs[file]
88
+ return p.call(line)
89
+ end
90
+
91
+ unless list = SCRIPT_LINES__[file]
92
+ begin
93
+ f = open(file)
94
+ begin
95
+ SCRIPT_LINES__[file] = list = f.readlines
96
+ ensure
97
+ f.close
98
+ end
99
+ rescue
100
+ SCRIPT_LINES__[file] = list = []
101
+ end
102
+ end
103
+
104
+ if l = list[line - 1]
105
+ l
106
+ else
107
+ "-\n"
108
+ end
109
+ end
110
+
111
+ def get_thread_no
112
+ if no = @threads[Thread.current.object_id]
113
+ no
114
+ else
115
+ @threads[Thread.current.object_id] = @threads.size
116
+ end
117
+ end
118
+
119
+ require 'pp'
120
+ @@depths = {} # I think I added this [rdp]
121
+ @@last_line = nil
122
+ @@last_file = nil
123
+ @@last_symbol = nil
124
+ def trace_func(event, file, line, id, binding, klass, *nothing)
125
+ begin
126
+ return if file == __FILE__
127
+
128
+ type = EVENT_SYMBOL[event] # "<" or ">"
129
+ thread_no = get_thread_no
130
+ Thread.current['backtrace'] ||= []
131
+ @@depths[thread_no] ||= 0
132
+ Thread.current['backtrace'] << nil if Thread.current['backtrace'].length < (@@depths[thread_no] ) # pad it :)
133
+ if type == ">"
134
+ @@depths[thread_no] += 1
135
+ elsif type == "<"
136
+ @@depths[thread_no] -= 1
137
+ end
138
+
139
+ for p in @filters
140
+ return unless p.call event, file, line, id, binding, klass
141
+ end
142
+ return if file.include? 'ruby-debug' # debugger output
143
+
144
+ saved_crit = Thread.critical
145
+ Thread.critical = true
146
+ # TODO only do the backtrace if last command was 'raise'
147
+ if type == 'R'
148
+ Thread.current['backtrace'][@@depths[thread_no] - 1] = [[file, line], [], binding]
149
+ Thread.current['backtrace'] = Thread.current['backtrace'][0..@@depths[thread_no]] # clear old stuffs
150
+ end
151
+
152
+ if [file, line] != @@last_line # for output sake [not output too many lines]
153
+ if @@last_symbol == '>' # then we need to add to the backtrace--we've advanced down a call in the callstack and can now glean its variables' values
154
+ previous_frame_binding = Debugger.current_context.frame_binding(2)
155
+ collected = []
156
+ args = Debugger.current_context.frame_args 1 rescue nil # maybe it had no arguments [ltodo check]
157
+
158
+ if args
159
+ for arg in args
160
+ value = eval(arg, previous_frame_binding)
161
+ collected << [arg, value]
162
+ end
163
+ else
164
+ print "WEIRD--please report err spot 1, how to reproduce"
165
+ end
166
+ print 'args were ', collected.inspect, "\n" if $VERBOSE
167
+
168
+ Thread.current['backtrace'][@@depths[thread_no] - 1] = [[@@last_file, @@last_line], collected, previous_frame_binding]
169
+ end
170
+ end
171
+
172
+ out = " |" * @@depths[thread_no] + sprintf("#%d:%s:%d:%s:%s: %s",
173
+ get_thread_no,
174
+ file,
175
+ line,
176
+ klass || '',
177
+ type,
178
+ get_line(file, line))
179
+
180
+ print out if $VERBOSE
181
+ @@last_line = line
182
+ @@last_file = file
183
+ @@last_symbol = type
184
+
185
+ Thread.critical = saved_crit
186
+ rescue Exception => e
187
+ # TODO investigate print "BAD" + e.to_s + e.backtrace.inspect
188
+ end
189
+ end
190
+
191
+ Single = new
192
+ def Tracer.on
193
+ if block_given?
194
+ Single.on{yield}
195
+ else
196
+ Single.on
197
+ end
198
+ end
199
+
200
+ def Tracer.off
201
+ Single.off
202
+ end
203
+
204
+ def Tracer.set_get_line_procs(file_name, p = proc)
205
+ Single.set_get_line_procs(file_name, p)
206
+ end
207
+
208
+
209
+ def Tracer.add_filter(p = proc)
210
+ Single.add_filter(p)
211
+ end
212
+
213
+ def Tracer.output_locals(previous_binding, prefix="\t\t")
214
+ locals_name = Kernel.eval("local_variables", previous_binding)
215
+ locals = {}
216
+ for name in locals_name do
217
+ locals[name] = Kernel.eval(name, previous_binding)
218
+ end
219
+
220
+ puts "#{prefix}locals: " + locals.inspect
221
+ end
222
+
223
+
224
+ at_exit do # at_exit seems to be run by the last running Thread. Or an exiting thread? Not sure for always.
225
+ off
226
+ raise_location = Thread.current['backtrace'].pop
227
+ loc = raise_location[0]
228
+ puts
229
+ puts "unhandled exception: #{loc[0]}:#{loc[1]}: #{get_line loc[0], loc[1]}"
230
+ if(defined?($NO_LOCALS))
231
+ no_locals = true
232
+ else
233
+ no_locals = false
234
+ end
235
+ output_locals(raise_location[2], "\t") unless no_locals
236
+ puts "\t from:\n"
237
+
238
+ # last most one is redundant with the raise one...
239
+ Thread.current['backtrace'].pop
240
+
241
+ for loc, params, binding in Thread.current['backtrace'].reverse do
242
+ original_line = get_line loc[0], loc[1]
243
+ # TODO handle non parentheses
244
+ line_no_params = original_line.split('(')[0]
245
+ line_no_params.gsub!('def ', '')
246
+ line_params = line_no_params + "("
247
+ comma = ""
248
+ for param in params do
249
+ line_params += param[0].to_s + "=>" + param[1].to_s + ", "
250
+ comma = ", "
251
+ end
252
+ line_params = line_params[0..-3] # strip off ending comma
253
+ line_params += ")" if line_params =~ /\(/
254
+ puts "\t#{loc[0]}:#{loc[1]} #{line_params}"
255
+ output_locals binding unless no_locals
256
+ end
257
+
258
+
259
+ exit!
260
+ end
261
+ end
262
+
263
+ SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
264
+ Tracer::on
265
+ if $0 == __FILE__
266
+ # direct call
267
+
268
+ $0 = ARGV[0]
269
+ ARGV.shift
270
+ Tracer.on
271
+ require $0
272
+ elsif caller(0).size == 1
273
+ Tracer.on
274
+ end
275
+
276
+ # TODO: do are arg snapshots capture it right [soon enough]?
277
+ # TODO: don't output error if none thrown :)
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: backtracer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors: []
7
+
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-29 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Quality backtraces for ruby
17
+ email: rogerdpack@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - README
26
+ - Rakefile
27
+ - VERSION
28
+ - examples/crash.rb
29
+ - examples/crash_longer.rb
30
+ - examples/example_test_all_output
31
+ - examples/example_test_large_output
32
+ - examples/run_all_styles_of_backtracer.rb
33
+ - examples/run_large_style_output.rb
34
+ - lib/backtrace_nothing_swallowed.rb
35
+ - lib/backtrace_with_code_and_locals.rb
36
+ - lib/backtracer.rb
37
+ - lib/core_backtracer.rb
38
+ has_rdoc: true
39
+ homepage:
40
+ licenses: []
41
+
42
+ post_install_message:
43
+ rdoc_options:
44
+ - --charset=UTF-8
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.3.5
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: Quality backtraces for ruby
66
+ test_files:
67
+ - examples/crash.rb
68
+ - examples/crash_longer.rb
69
+ - examples/run_all_styles_of_backtracer.rb
70
+ - examples/run_large_style_output.rb