unroller 0.0.8 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/Readme +34 -0
  2. data/lib/unroller.rb +77 -19
  3. 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.
@@ -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
- @depth = @initial_depth
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
- @depth += 1
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 @excluding_calls_made_within_unintersting_call and @excluding_calls_made_within_unintersting_call == @depth
246
- if @excluding_calls_made_within_unintersting_call == @depth
247
- puts "Yay, we're back in interesting land!"
248
- @excluding_calls_made_within_unintersting_call = nil
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.trace_off
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 depth #{@depth} again because we're calling uninteresting #{class_name}:#{@id}"
339
- @excluding_calls_made_within_unintersting_call = @depth
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
- column "#{file}:#{line}", :remainder, :chop_left, :magenta
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.8
7
- date: 2007-04-20 00:00:00 -07:00
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