backtracer 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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