unroller 0.0.8 → 0.0.10

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.
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