unroller 0.0.8 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/Readme +34 -0
- data/lib/unroller.rb +77 -19
- metadata +2 -2
data/Readme
CHANGED
@@ -63,6 +63,32 @@ If you'd like to see the values of all local variables as they exist right befor
|
|
63
63
|
|
64
64
|
Note: You might like to know that the state of those variables is _after_ executing that line, too, but currently that's not possible.
|
65
65
|
|
66
|
+
Also, I haven't yet figured out a way to show the return value it's going to return with when we hit a 'return' event. That would be nice to have, too, if one can figure out how to implement it...
|
67
|
+
|
68
|
+
===Only tracing if a condition is met
|
69
|
+
|
70
|
+
For example, a lot of the time you will be in a loop or iterator and you will only be interested in what's going on <i>during certain iterations</i> of that loop.
|
71
|
+
|
72
|
+
There are two ways you can accomplish that selectivity:
|
73
|
+
|
74
|
+
The good old simple way:
|
75
|
+
|
76
|
+
Unroller::trace if name =~ /interesting/
|
77
|
+
code_that_may_or_may_not_be_traced
|
78
|
+
Unroller::trace_off
|
79
|
+
|
80
|
+
And the somewhat trickier but arguably more elegant way that still uses a block (which always gets executed):
|
81
|
+
|
82
|
+
Unroller::trace :if => { name =~ /interesting/ } do
|
83
|
+
code_that_may_or_may_not_be_traced
|
84
|
+
end
|
85
|
+
|
86
|
+
The other reason that the latter way is preferred is that the return value of the code-being-traced is preserved. With the first method, you could end up breaking things if the trace_off happens to be the last value in your method (because then the value of trace_off will be used as the return valuel).
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
Note: The actual application code (the code in the block passed to Unroller::trace, if using the block version) will _always_ get executed, with either of these methods. It is only the tracing that we are toggling, not the execution of the code within the trace(d) block.
|
91
|
+
|
66
92
|
===Reducing verbosity
|
67
93
|
|
68
94
|
This can generate some really *verbose* output... Not only can be impractical to try to *read* through the reams of pages it can produce, but it can also take an hour just to output it in the first place!
|
@@ -126,6 +152,9 @@ If you enable tracing from within that method, it will only show the calls that
|
|
126
152
|
|
127
153
|
So... you could always <tt>p caller(0)</tt> within your mystery function and then put a trace around one of the calls leading up to this call, a little further up the stack...
|
128
154
|
|
155
|
+
I know, wouldn't it be nice if you could just tell it, "Show me the execution traces for the 3 calls leading up to this call"? But alas, Unroller can only affect what happens _after_ it gets called, not before.
|
156
|
+
If I ever get around to writing an interactive tree-based UI for this, then I guess we could give the _impression_ of being able to show calls leading up to a certain call. But only by recording *all* calls and then hiding everything except the calls you're interested in...
|
157
|
+
|
129
158
|
|
130
159
|
===Usage in Rails
|
131
160
|
|
@@ -186,9 +215,14 @@ It's also sort of like a call stack (caller(0)). But unlike the callstack you us
|
|
186
215
|
|
187
216
|
==Possibly related projects
|
188
217
|
|
218
|
+
I didn't see any projects out there that did what I was wanting, so I wrote my own (and was surprised by how easy it was!). But if you know of any similar projects out there, let me know and I'll check it out.
|
219
|
+
|
220
|
+
Here are the closest projects I've run across so far...
|
189
221
|
* http://rubyforge.org/projects/dev-utils/ : Collects utilities that aid Ruby development, e.g. testing and debugging. Version 1.0 contains simple debug logging, tracing, and escaping to IRB.
|
190
222
|
* http://rubyforge.org/projects/ruby-uml/ : Generates uml diagrams by tracing the run of an application for analysation of an existing application and to provide support for refactorisations.
|
191
223
|
|
224
|
+
I've also heard rumors that Ruby comes standard with some kind of debugger (?) ... Maybe I wouldn't have written this if I'd known how to use that (is it any good?? is it easy to use??)... but... I still haven't even looked at that.
|
225
|
+
|
192
226
|
==To do
|
193
227
|
|
194
228
|
You're welcome to submit comments and/or patches.
|
data/lib/unroller.rb
CHANGED
@@ -65,6 +65,8 @@ class Unroller
|
|
65
65
|
@exclude_classes = []
|
66
66
|
@show_args = true
|
67
67
|
@show_locals = false
|
68
|
+
@show_filename_and_line_numbers = true
|
69
|
+
@include_c_calls = false # Might be useful if you're debugging your own C extension. Otherwise, we really don't care about C-calls because we can't see the source for them anyway...
|
68
70
|
@strip_comments = true # :todo:
|
69
71
|
@use_full_path = false # :todo:
|
70
72
|
@screen_width = 150
|
@@ -122,7 +124,8 @@ class Unroller
|
|
122
124
|
# 1. Sometimes (and I don't know why), the code that gets shown for a 'return' event doesn't even look like it has anything to do with a return...
|
123
125
|
# 2. If we've been stuck in this method for a long time and we're really deep, the user has probably forgotten by now which method we are returning from
|
124
126
|
# (the filename may give some clue, but not enough), so this is likely to be a welcome reminder a lot of the time.
|
125
|
-
@
|
127
|
+
@internal_depth = 0 # This is the "true" depth. It is incremented/decremented for *every* call/return, even those that we're not displaying. It is necessary for the implementation of "excluding_calls_made_within_unintersting_call", to detect when we get back to interesting land.
|
128
|
+
@depth = @initial_depth # This is the user-friendly depth. It only counts calls/returns that we *display*; it does not change when we enter into a call that we're not displaying (a "hidden" call).
|
126
129
|
@output_line = ''
|
127
130
|
@column_counter = 0
|
128
131
|
@tracing = false
|
@@ -168,7 +171,7 @@ class Unroller
|
|
168
171
|
#printf "- (event=%8s) (klass=%10s) (id=%10s) (%s:%-2d)\n", event, klass, id, file, line #if klass.to_s == 'false'
|
169
172
|
#puts 'false!!!!!!!'+klass.inspect if klass.to_s == 'false'
|
170
173
|
|
171
|
-
return if ['c-call', 'c-return'].include? event
|
174
|
+
return if ['c-call', 'c-return'].include? event unless include_c_calls
|
172
175
|
#(puts 'exclude') if @excluding_calls_made_within_unintersting_call unless event == 'return' # Until we hit a return and can break out of this uninteresting call, we don't want to do *anything*.
|
173
176
|
#return if uninteresting_class?(klass.to_s) unless (klass == false)
|
174
177
|
|
@@ -192,15 +195,17 @@ class Unroller
|
|
192
195
|
file_column file, line
|
193
196
|
newline
|
194
197
|
|
195
|
-
# The locals at this point will be simply be the arguments that were passed in to this method.
|
196
|
-
do_show_locals if show_args
|
197
|
-
|
198
198
|
@lines_output += 1
|
199
199
|
|
200
200
|
@call_stack.push fully_qualified_method
|
201
|
-
end
|
202
201
|
|
203
|
-
|
202
|
+
@depth += 1
|
203
|
+
#puts "++ Increased depth to #{depth}"
|
204
|
+
|
205
|
+
# The locals at this point will be simply be the arguments that were passed in to this method.
|
206
|
+
do_show_locals if show_args
|
207
|
+
end
|
208
|
+
@internal_depth += 1
|
204
209
|
|
205
210
|
|
206
211
|
when 'class'
|
@@ -220,11 +225,14 @@ class Unroller
|
|
220
225
|
|
221
226
|
|
222
227
|
when 'return'
|
223
|
-
puts "Warning: @depth < 0. You may wish to call trace with a :depth => depth value greater than #{@initial_depth}" if @depth < 0
|
224
|
-
@depth -= 1 unless @depth == 0
|
225
|
-
returning_from = @call_stack.pop
|
226
228
|
|
229
|
+
@internal_depth -= 1
|
227
230
|
unless skip_line?
|
231
|
+
puts "Warning: @depth < 0. You may wish to call trace with a :depth => depth value greater than #{@initial_depth}" if @depth-1 < 0
|
232
|
+
@depth -= 1 unless @depth == 0
|
233
|
+
#puts "-- Decreased depth to #{depth}"
|
234
|
+
returning_from = @call_stack.pop
|
235
|
+
|
228
236
|
code = code_for(file, line, '\\'.magenta, :green, suffix = " (returning from #{returning_from})".green)
|
229
237
|
code = code_for(file, line, '\\'.magenta + " (returning from #{returning_from})".green, :green) unless code =~ /return|end/
|
230
238
|
# I've seen some really weird statements listed as "return" statements.
|
@@ -242,11 +250,10 @@ class Unroller
|
|
242
250
|
end
|
243
251
|
|
244
252
|
# Did we just get out of an uninteresting call?? Are we back in interesting land again??
|
245
|
-
if
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
end
|
253
|
+
if @excluding_calls_made_within_unintersting_call and
|
254
|
+
@excluding_calls_made_within_unintersting_call == @internal_depth
|
255
|
+
puts "Yay, we're back in interesting land!"
|
256
|
+
@excluding_calls_made_within_unintersting_call = nil
|
250
257
|
end
|
251
258
|
|
252
259
|
|
@@ -290,7 +297,9 @@ class Unroller
|
|
290
297
|
end
|
291
298
|
|
292
299
|
def self.trace_off
|
293
|
-
@@instance.
|
300
|
+
if @@instance and @@instance.tracing
|
301
|
+
@@instance.trace_off
|
302
|
+
end
|
294
303
|
end
|
295
304
|
def trace_off
|
296
305
|
@tracing = false
|
@@ -306,6 +315,7 @@ protected
|
|
306
315
|
end
|
307
316
|
def do_show_locals
|
308
317
|
variables = Variables.new(:local, @binding)
|
318
|
+
# puts "In do_show_locals at depth #{depth}; event = #{@event}"
|
309
319
|
if variables.any?
|
310
320
|
column variables.to_s
|
311
321
|
newline
|
@@ -335,8 +345,8 @@ protected
|
|
335
345
|
|
336
346
|
returning(class_name =~ regexp) do |uninteresting|
|
337
347
|
if uninteresting && recursive && @excluding_calls_made_within_unintersting_call.nil?
|
338
|
-
puts "Turning tracing off until we get back to
|
339
|
-
@excluding_calls_made_within_unintersting_call = @
|
348
|
+
puts "Turning tracing off until we get back to internal_depth #{@internal_depth} again because we're calling uninteresting #{class_name}:#{@id}"
|
349
|
+
@excluding_calls_made_within_unintersting_call = @internal_depth
|
340
350
|
end
|
341
351
|
end
|
342
352
|
end
|
@@ -396,7 +406,9 @@ protected
|
|
396
406
|
end
|
397
407
|
|
398
408
|
def file_column(file, line)
|
399
|
-
|
409
|
+
if show_filename_and_line_numbers
|
410
|
+
column "#{file}:#{line}", :remainder, :chop_left, :magenta
|
411
|
+
end
|
400
412
|
end
|
401
413
|
|
402
414
|
#----------------------------------------------------------
|
@@ -453,6 +465,14 @@ end
|
|
453
465
|
|
454
466
|
|
455
467
|
if $0 == __FILE__
|
468
|
+
puts '-----------------------------------------------------------'
|
469
|
+
puts 'Can call trace_off even if not tracing'
|
470
|
+
Unroller::trace_off
|
471
|
+
|
472
|
+
puts '-----------------------------------------------------------'
|
473
|
+
puts 'Testing return value (should print 3 in this case)'
|
474
|
+
puts Unroller::trace { 1 + 2 }
|
475
|
+
|
456
476
|
puts '-----------------------------------------------------------'
|
457
477
|
puts 'Simple test'
|
458
478
|
def jump!(how_high = 3)
|
@@ -635,6 +655,44 @@ if $0 == __FILE__
|
|
635
655
|
create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces
|
636
656
|
end
|
637
657
|
|
658
|
+
=begin HistoricalNote
|
659
|
+
# This test used to generate output more like this (note the extreme indenting):
|
660
|
+
|
661
|
+
| create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces | unroller.rb:645
|
662
|
+
| + calling Object::create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces
|
663
|
+
| / def create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces | unroller.rb:641
|
664
|
+
| | Uninteresting::ClassThatCluttersUpOnesTraces.new.a | unroller.rb:642
|
665
|
+
| | | | | | | | | | + calling Interesting::method
|
666
|
+
| | | | | | | | | | / def self.method | unroller.rb:620
|
667
|
+
| | | | | | | | | | | '...' | unroller.rb:621
|
668
|
+
| | | | | | | | | | \ end (returning from Interesting::method) | unroller.rb:622
|
669
|
+
| | | | | | | | | | + calling Interesting::method
|
670
|
+
| | | | | | | | | | / def method | unroller.rb:623
|
671
|
+
| | | | | | | | | | | '...' | unroller.rb:624
|
672
|
+
| | | | | | | | | | \ end (returning from Interesting::method) | unroller.rb:625
|
673
|
+
| \ end (returning from )
|
674
|
+
|
675
|
+
# ... which is probably a technically more accurate picture of the current call stack. However, it
|
676
|
+
|
677
|
+
# This changed when the idea of an @internal_depth separate from the @depth (that the user sees) was introduced.
|
678
|
+
|
679
|
+
| create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces | unroller.rb:655
|
680
|
+
| + calling Object::create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces
|
681
|
+
| / def create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces | unroller.rb:651
|
682
|
+
| | Uninteresting::ClassThatCluttersUpOnesTraces.new.a | unroller.rb:652
|
683
|
+
| | + calling Interesting::method
|
684
|
+
| | / def self.method | unroller.rb:630
|
685
|
+
| | | '...' | unroller.rb:631
|
686
|
+
| | \ end (returning from Interesting::method) | unroller.rb:632
|
687
|
+
| | + calling Interesting::method
|
688
|
+
| | / def method | unroller.rb:633
|
689
|
+
| | | '...' | unroller.rb:634
|
690
|
+
| | \ end (returning from Interesting::method) | unroller.rb:635
|
691
|
+
| \ end (returning from Object::create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces) | unroller.rb:653
|
692
|
+
|
693
|
+
Much cleaner looking...
|
694
|
+
=end
|
695
|
+
|
638
696
|
puts '-----------------------------------------------------------'
|
639
697
|
puts 'Now let\'s be recursive! We should *not* see any calls to Interesting::* this time. But after we return from it, we should see the call to jump!'
|
640
698
|
Unroller::trace(:exclude_classes => [[/Uninteresting/, :recursive]]) do
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: unroller
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2007-04-
|
6
|
+
version: 0.0.10
|
7
|
+
date: 2007-04-23 00:00:00 -07:00
|
8
8
|
summary: A tool for generating human-readable "execution traces"
|
9
9
|
require_paths:
|
10
10
|
- lib
|