zenprofile 1.0.2

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.
Binary file
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2008-09-17
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
@@ -0,0 +1,10 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/zenprofile
6
+ lib/spy_on.rb
7
+ lib/zenprofile.rb
8
+ lib/zenprofiler.rb
9
+ misc/factorial.rb
10
+ zenprofile-demo.sh
@@ -0,0 +1,86 @@
1
+ = zenprofile
2
+
3
+ * http://rubyforge.org/projects/seattlerb
4
+
5
+ == DESCRIPTION:
6
+
7
+ zenprofiler helps answer WHAT is being called the most. spy_on helps
8
+ answer WHERE those calls are being made. ZenProfiler provides a faster
9
+ version of the standard library ruby profiler. It is otherwise pretty
10
+ much the same as before. spy_on provides a clean way to redefine a
11
+ bottleneck method so you can account for and aggregate all the calls
12
+ to it.
13
+
14
+ % ruby -Ilib bin/zenprofile misc/factorial.rb 50000
15
+ Total time = 3.056884
16
+ Total time = 2.390000
17
+
18
+ total self self total
19
+ % time seconds seconds calls ms/call ms/call name
20
+ 50.70 1.64 1.64 50000 0.03 0.05 Integer#downto
21
+ 19.63 2.27 0.63 200000 0.00 0.00 Fixnum#*
22
+ 14.19 2.73 0.46 50000 0.01 0.05 Factorial#factorial
23
+ 9.93 3.05 0.32 1 320.36 3047.10 Range#each
24
+ 5.54 3.23 0.18 2 89.40 178.79 ZenProfiler#start_hook
25
+
26
+ Once you know that Integer#downto takes 50% of the entire run, you
27
+ can use spy_on to find it. (See misc/factorial.rb for the actual code):
28
+
29
+ % SPY=1 ruby -Ilib misc/factorial.rb 50000
30
+ Spying on Integer#downto
31
+
32
+ Integer.downto
33
+
34
+ 50000: total
35
+ 50000: ./misc/factorial.rb:6:in `factorial' via
36
+ ./misc/factorial.rb:6:in `factorial'
37
+
38
+ == FEATURES/PROBLEMS:
39
+
40
+ * 4x faster than stdlib ruby profiler by using event_hook to bypass set_trace_func
41
+ * 1/14th the amount of code of ruby-prof. Much easier to play with.
42
+
43
+ == SYNOPSIS:
44
+
45
+ % zenprofile [ruby-flags] misc/factorial.rb
46
+
47
+ then:
48
+
49
+ Array.spy_on :select, :each
50
+
51
+ run your code, then you'll see where the calls to Array#select and
52
+ #each are coming from.
53
+
54
+ == REQUIREMENTS:
55
+
56
+ * event_hook
57
+ * RubyInline
58
+
59
+ == INSTALL:
60
+
61
+ * sudo gem install zenprofile
62
+
63
+ == LICENSE:
64
+
65
+ (The MIT License)
66
+
67
+ Copyright (c) 2009 Ryan Davis, seattle.rb
68
+
69
+ Permission is hereby granted, free of charge, to any person obtaining
70
+ a copy of this software and associated documentation files (the
71
+ 'Software'), to deal in the Software without restriction, including
72
+ without limitation the rights to use, copy, modify, merge, publish,
73
+ distribute, sublicense, and/or sell copies of the Software, and to
74
+ permit persons to whom the Software is furnished to do so, subject to
75
+ the following conditions:
76
+
77
+ The above copyright notice and this permission notice shall be
78
+ included in all copies or substantial portions of the Software.
79
+
80
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
81
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
82
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
83
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
84
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
85
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
86
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,16 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/zenprofiler.rb'
6
+
7
+ Hoe.new('zenprofile', ZenProfiler::VERSION) do |p|
8
+ p.rubyforge_name = 'seattlerb'
9
+ p.developer('Ryan Davis', 'ryand-ruby@zenspider.com')
10
+
11
+ p.extra_deps << 'ZenTest'
12
+ p.extra_deps << 'event_hook'
13
+ p.extra_deps << 'RubyInline'
14
+ end
15
+
16
+ # vim: syntax=Ruby
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbconfig'
4
+
5
+ ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['RUBY_INSTALL_NAME'])
6
+ dir = File.join(File.dirname(File.dirname(__FILE__)), 'lib')
7
+
8
+ exec ruby, "-I#{dir}", '-rzenprofile', *ARGV
@@ -0,0 +1,70 @@
1
+ $: << '../../ZenTest/dev/lib'
2
+
3
+ require 'zentest_mapping'
4
+
5
+ ##
6
+ # require 'test/spy_on'
7
+ #
8
+ # class Array
9
+ # spy_on :select, :each
10
+ # end
11
+ #
12
+ # class Hash
13
+ # spy_on :key?
14
+ # # spy_on :[]
15
+ # end
16
+
17
+ class Hash
18
+ alias_method :safe_idx, :[]
19
+ alias_method :safe_asgn, :[]=
20
+ end
21
+
22
+ class Module
23
+ include ZenTestMapping
24
+
25
+ def call_site(depth=1)
26
+ paths = caller(2).map { |path| path =~ /\(eval\)/ ? path : File.expand_path(path).sub(/#{File.expand_path Dir.pwd}/, '.') }
27
+ our = paths.reject { |path| path =~ /vendor.rails|rubygems/ }
28
+ return ["#{our.first} via "] + paths.first(depth)
29
+ end
30
+
31
+ def print_tally(tallies)
32
+ tallies.sort.each do |msg, tally|
33
+ puts
34
+ puts msg
35
+ puts
36
+ total = 0
37
+ tally.values.each do |n| total += n; end
38
+ puts "%5d: %s" % [total, "total"]
39
+
40
+ tally.sort_by { |caller, count| -count }.first(5).each do |caller, count|
41
+ puts "%5d: %s" % [count, caller.join("\n ")]
42
+ end
43
+ end
44
+ end
45
+
46
+ def spy msg, old_msg = nil, depth = 1
47
+ puts "Spying on #{self}##{msg}"
48
+ old_msg ||= "old_#{normal_to_test(msg.to_s).sub(/^test_/, '')}"
49
+ alias_method old_msg, msg
50
+
51
+ class_eval "
52
+ @@tally_done = false
53
+ @@tally ||= Hash.new { |h,k| h.safe_asgn(k, Hash.new(0)) }
54
+ at_exit { at_exit { @@tally_done = true; print_tally @@tally if @@tally; @@tally = nil } }
55
+ def #{msg}(*args, &block)
56
+ unless @@tally_done then
57
+ site = self.class.call_site(#{depth})
58
+ x = @@tally.safe_idx('#{self}.#{msg}')
59
+ x.safe_asgn(site, x.safe_idx(site) + 1)
60
+ end
61
+ self.#{old_msg}(*args, &block)
62
+ end "
63
+ end
64
+
65
+ def spy_on *msgs
66
+ msgs.each do |msg|
67
+ spy msg
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,3 @@
1
+ require 'zenprofiler'
2
+
3
+ ZenProfiler.run
@@ -0,0 +1,215 @@
1
+ require 'rubygems'
2
+ require 'inline'
3
+ require 'singleton'
4
+ $: << '../../event_hook/dev/lib' # TODO: remove
5
+ require 'event_hook'
6
+
7
+ ##
8
+ # ZenProfiler provides a faster version of the standard library ruby
9
+ # profiler. It is otherwise pretty much the same as before.
10
+ #
11
+ # Invoke it via:
12
+ #
13
+ # % zenprofile misc/factorial.rb
14
+ #
15
+ # or:
16
+ #
17
+ # % ruby -rzenprofile misc/factorial.rb
18
+
19
+ class ZenProfiler < EventHook
20
+ VERSION = '1.0.2'
21
+
22
+ @@start = nil
23
+ @@stack = [[0, 0, [nil, :toplevel]], [0, 0, [nil, :dummy]]]
24
+ @@map = Hash.new { |h,k| h[k] = [0, 0.0, 0.0, k] }
25
+ @@map["#toplevel"] = [1, 0.0, 0.0, [nil, "#toplevel"]]
26
+ @@percent_time_threshold = 0.5
27
+
28
+ def self.run(fp = $stdout, opts = {})
29
+ at_exit {
30
+ ZenProfiler::print_profile fp, opts
31
+ }
32
+ ZenProfiler::start_hook
33
+ end
34
+
35
+ def self.start_hook
36
+ @@start ||= Time.now.to_f
37
+ @@start2 ||= Process.times[0]
38
+ super
39
+ end
40
+
41
+ def self.percent_time_threshold
42
+ @@percent_time_threshold
43
+ end
44
+
45
+ def self.percent_time_threshold=(percent_time_threshold)
46
+ @@percent_time_threshold = percent_time_threshold
47
+ end
48
+
49
+ def self.print_profile(f, opts = {})
50
+ stop_hook
51
+
52
+ @@total = Time.now.to_f - @@start
53
+ @@total = 0.01 if @@total == 0
54
+ @@total2 = Process.times[0] - @@start2
55
+ @@map["#toplevel"][1] = @@total
56
+ data = @@map.values.sort_by { |vals| -vals[2] }
57
+ sum = 0
58
+
59
+ f.puts "Total time = %f" % @@total
60
+ f.puts "Total time = %f" % @@total2
61
+ f.puts
62
+ f.puts " total self self total"
63
+ f.puts "% time seconds seconds calls ms/call ms/call name"
64
+
65
+ @@total = data.inject(0) { |acc, (_, _, self_ms, _)| acc + self_ms }
66
+
67
+ data.each do |calls, total_ms, self_ms, name|
68
+ percent_time = self_ms / @@total * 100.0
69
+
70
+ next if percent_time < @@percent_time_threshold
71
+
72
+ sum += self_ms
73
+ klass = name.first
74
+ meth = name.last.to_s
75
+
76
+ signature =
77
+ if klass.nil?
78
+ meth
79
+ elsif klass.kind_of?(Module)
80
+ klassname = klass.name rescue klass.to_s.sub(/#<\S+:(\S+)>/, '\\1')
81
+ "#{klassname}##{meth}"
82
+ else
83
+ "#{klass.class}##{meth}"
84
+ end
85
+
86
+ f.printf "%6.2f ", percent_time
87
+ f.printf "%8.2f ", sum
88
+ f.printf "%8.2f ", self_ms
89
+ f.printf "%8d ", calls
90
+ f.printf "%8.2f ", (self_ms * 1000.0 / calls)
91
+ f.printf "%8.2f ", (total_ms * 1000.0 / calls)
92
+ f.printf "%s", signature
93
+ f.puts
94
+ end
95
+ end
96
+
97
+ if ENV['PURERUBY'] then
98
+ def self.process event, obj, method, klass
99
+ case event
100
+ when CALL, CCALL
101
+ now = Process.times[0]
102
+ @@stack.push [now, 0.0]
103
+ when RETURN, CRETURN
104
+ key = [klass, method]
105
+ if tick = @@stack.pop
106
+ now = Process.times[0]
107
+ data = @@map[key]
108
+ data[0] += 1
109
+ cost = now - tick[0]
110
+ data[1] += cost
111
+ data[2] += cost - tick[1]
112
+ @@stack[-1][1] += cost if @@stack[-1]
113
+ end
114
+ end
115
+ end
116
+ else
117
+ class Inline::C # REFACTOR
118
+ def add_static name, init, type = "VALUE"
119
+ prefix "static #{type} #{name};"
120
+ add_to_init "#{name} = #{init};"
121
+ end
122
+ end
123
+
124
+ inline(:C) do |builder|
125
+ builder.add_type_converter("rb_event_t", '', '')
126
+ builder.add_type_converter("ID", '', '')
127
+ builder.add_type_converter("NODE *", '', '')
128
+
129
+ builder.include '<time.h>'
130
+ builder.include '"node.h"'
131
+
132
+ builder.add_static 'profiler_klass', 'rb_path2class("ZenProfiler")'
133
+ builder.add_static 'eventhook_klass', 'rb_path2class("EventHook")'
134
+ builder.add_static 'stack', 'rb_cv_get(profiler_klass, "@@stack")'
135
+ builder.add_static 'map', 'rb_cv_get(profiler_klass, "@@map")'
136
+
137
+ builder.add_static 'id_allocate', 'rb_intern("allocate")'
138
+
139
+ builder.prefix "
140
+ #define id_call INT2FIX(RUBY_EVENT_CALL)
141
+ #define id_ccall INT2FIX(RUBY_EVENT_C_CALL)
142
+ #define id_return INT2FIX(RUBY_EVENT_RETURN)
143
+ #define id_creturn INT2FIX(RUBY_EVENT_C_RETURN)
144
+ "
145
+
146
+ builder.prefix <<-'EOF'
147
+ VALUE time_now() {
148
+ return rb_float_new(((double) clock() / CLOCKS_PER_SEC));
149
+ }
150
+ EOF
151
+
152
+ builder.c_singleton <<-'EOF'
153
+ static void
154
+ process(VALUE event, VALUE recv, VALUE method, VALUE klass) {
155
+ static int profiling = 0;
156
+
157
+ if (method == id_allocate) return;
158
+ if (profiling) return;
159
+ profiling++;
160
+
161
+ VALUE now = time_now();
162
+
163
+ switch (event) {
164
+ case id_call:
165
+ case id_ccall:
166
+ {
167
+ VALUE time = rb_ary_new2(2);
168
+ rb_ary_store(time, 0, now);
169
+ rb_ary_store(time, 1, rb_float_new(0.0));
170
+ rb_ary_push(stack, time);
171
+ }
172
+ break;
173
+ case id_return:
174
+ case id_creturn:
175
+ {
176
+ if (TYPE(klass) == T_ICLASS) {
177
+ klass = RBASIC(klass)->klass;
178
+ } else if (FL_TEST(klass, FL_SINGLETON)) {
179
+ klass = recv;
180
+ }
181
+
182
+ VALUE key = rb_ary_new2(2);
183
+ rb_ary_store(key, 0, klass);
184
+ rb_ary_store(key, 1, method);
185
+
186
+ VALUE tick = rb_ary_pop(stack);
187
+ if (!RTEST(tick)) break;
188
+
189
+ VALUE data = rb_hash_aref(map, key);
190
+
191
+ rb_ary_store(data, 0, rb_ary_entry(data, 0) + 2); // omg I suck
192
+
193
+ double cost = NUM2DBL(now) - NUM2DBL(rb_ary_entry(tick, 0));
194
+
195
+ rb_ary_store(data, 1, rb_float_new(NUM2DBL(rb_ary_entry(data, 1))
196
+ + cost));
197
+
198
+ rb_ary_store(data, 2, rb_float_new(NUM2DBL(rb_ary_entry(data, 2))
199
+ + cost
200
+ - NUM2DBL(rb_ary_entry(tick, 1))));
201
+
202
+ VALUE toplevel = rb_ary_entry(stack, -1);
203
+ if (RTEST(toplevel)) {
204
+ VALUE tl_stats = rb_ary_entry(toplevel, 1);
205
+ rb_ary_store(toplevel, 1, rb_float_new(NUM2DBL(tl_stats) + cost));
206
+ }
207
+ }
208
+ break;
209
+ } // switch (event)
210
+ profiling--;
211
+ }
212
+ EOF
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,32 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ class Factorial
4
+ def factorial(n)
5
+ f = 1
6
+ n.downto(2) { |x| f *= x }
7
+ return f
8
+ end
9
+ end
10
+
11
+ if ENV['SPY'] then
12
+ require 'spy_on'
13
+
14
+ Integer.spy_on :downto
15
+ end
16
+
17
+ if $0 == __FILE__ then
18
+ f = Factorial.new()
19
+
20
+ max = ARGV.shift || 1000000
21
+ max = max.to_i
22
+
23
+ tstart = Time.now
24
+
25
+ (1..max).each { |m| n = f.factorial(5); }
26
+
27
+ tend = Time.now
28
+
29
+ total = tend - tstart
30
+ avg = total / max
31
+ # printf "Iter = #{max}, T = %.8f sec, %.8f sec / iter\n", total, avg
32
+ end
@@ -0,0 +1,34 @@
1
+ #!/bin/bash
2
+
3
+ N=$1
4
+
5
+ if [ -z $N ]; then N=100_000; fi
6
+ if [ -z $2 ]; then SKIP=no; else SKIP=yes; fi
7
+
8
+ rm -rf ~/.ruby_inline
9
+ sync; sync; sync
10
+
11
+ echo N=$N
12
+
13
+ if [ $SKIP = no ]; then
14
+ echo
15
+ echo ruby vanilla :
16
+ X=1 time ruby misc/factorial.rb $N
17
+
18
+ echo
19
+ echo ruby profiler:
20
+ X=1 time ruby -rprofile misc/factorial.rb $N 2>&1 | egrep -v "^ *0.00"
21
+ fi
22
+
23
+ echo
24
+ echo zen profiler :
25
+ export GEM_SKIP=RubyInline
26
+ X=1 time ./bin/zenprofile misc/factorial.rb $N 2>&1 | egrep -v "^ *0.00"
27
+
28
+ echo
29
+ echo zen profiler pure ruby:
30
+ export GEM_SKIP=RubyInline
31
+ PURERUBY=1 time ./bin/zenprofile misc/factorial.rb $N 2>&1 | egrep -v "^ *0.00"
32
+
33
+ # shugo's version
34
+ # time ruby -I.:lib -runprof misc/factorial.rb $N 2>&1 | head
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zenprofile
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Davis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDPjCCAiagAwIBAgIBADANBgkqhkiG9w0BAQUFADBFMRMwEQYDVQQDDApyeWFu
14
+ ZC1ydWJ5MRkwFwYKCZImiZPyLGQBGRYJemVuc3BpZGVyMRMwEQYKCZImiZPyLGQB
15
+ GRYDY29tMB4XDTA5MDMwNjE4NTMxNVoXDTEwMDMwNjE4NTMxNVowRTETMBEGA1UE
16
+ AwwKcnlhbmQtcnVieTEZMBcGCgmSJomT8ixkARkWCXplbnNwaWRlcjETMBEGCgmS
17
+ JomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALda
18
+ b9DCgK+627gPJkB6XfjZ1itoOQvpqH1EXScSaba9/S2VF22VYQbXU1xQXL/WzCkx
19
+ taCPaLmfYIaFcHHCSY4hYDJijRQkLxPeB3xbOfzfLoBDbjvx5JxgJxUjmGa7xhcT
20
+ oOvjtt5P8+GSK9zLzxQP0gVLS/D0FmoE44XuDr3iQkVS2ujU5zZL84mMNqNB1znh
21
+ GiadM9GHRaDiaxuX0cIUBj19T01mVE2iymf9I6bEsiayK/n6QujtyCbTWsAS9Rqt
22
+ qhtV7HJxNKuPj/JFH0D2cswvzznE/a5FOYO68g+YCuFi5L8wZuuM8zzdwjrWHqSV
23
+ gBEfoTEGr7Zii72cx+sCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAw
24
+ HQYDVR0OBBYEFEfFe9md/r/tj/Wmwpy+MI8d9k/hMA0GCSqGSIb3DQEBBQUAA4IB
25
+ AQAY59gYvDxqSqgC92nAP9P8dnGgfZgLxP237xS6XxFGJSghdz/nI6pusfCWKM8m
26
+ vzjjH2wUMSSf3tNudQ3rCGLf2epkcU13/rguI88wO6MrE0wi4ZqLQX+eZQFskJb/
27
+ w6x9W1ur8eR01s397LSMexySDBrJOh34cm2AlfKr/jokKCTwcM0OvVZnAutaovC0
28
+ l1SVZ0ecg88bsWHA0Yhh7NFxK1utWoIhtB6AFC/+trM0FQEB/jZkIS8SaNzn96Rl
29
+ n0sZEf77FLf5peR8TP/PtmIg7Cyqz23sLM4mCOoTGIy5OcZ8TdyiyINUHtb5ej/T
30
+ FBHgymkyj/AOSqKRIpXPhjC6
31
+ -----END CERTIFICATE-----
32
+
33
+ date: 2009-05-20 00:00:00 -07:00
34
+ default_executable:
35
+ dependencies:
36
+ - !ruby/object:Gem::Dependency
37
+ name: ZenTest
38
+ type: :runtime
39
+ version_requirement:
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ - !ruby/object:Gem::Dependency
47
+ name: event_hook
48
+ type: :runtime
49
+ version_requirement:
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ - !ruby/object:Gem::Dependency
57
+ name: RubyInline
58
+ type: :runtime
59
+ version_requirement:
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ - !ruby/object:Gem::Dependency
67
+ name: hoe
68
+ type: :development
69
+ version_requirement:
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 1.12.2
75
+ version:
76
+ description: |-
77
+ zenprofiler helps answer WHAT is being called the most. spy_on helps
78
+ answer WHERE those calls are being made. ZenProfiler provides a faster
79
+ version of the standard library ruby profiler. It is otherwise pretty
80
+ much the same as before. spy_on provides a clean way to redefine a
81
+ bottleneck method so you can account for and aggregate all the calls
82
+ to it.
83
+
84
+ % ruby -Ilib bin/zenprofile misc/factorial.rb 50000
85
+ Total time = 3.056884
86
+ Total time = 2.390000
87
+
88
+ total self self total
89
+ % time seconds seconds calls ms/call ms/call name
90
+ 50.70 1.64 1.64 50000 0.03 0.05 Integer#downto
91
+ 19.63 2.27 0.63 200000 0.00 0.00 Fixnum#*
92
+ 14.19 2.73 0.46 50000 0.01 0.05 Factorial#factorial
93
+ 9.93 3.05 0.32 1 320.36 3047.10 Range#each
94
+ 5.54 3.23 0.18 2 89.40 178.79 ZenProfiler#start_hook
95
+
96
+ Once you know that Integer#downto takes 50% of the entire run, you
97
+ can use spy_on to find it. (See misc/factorial.rb for the actual code):
98
+
99
+ % SPY=1 ruby -Ilib misc/factorial.rb 50000
100
+ Spying on Integer#downto
101
+
102
+ Integer.downto
103
+
104
+ 50000: total
105
+ 50000: ./misc/factorial.rb:6:in `factorial' via
106
+ ./misc/factorial.rb:6:in `factorial'
107
+ email:
108
+ - ryand-ruby@zenspider.com
109
+ executables:
110
+ - zenprofile
111
+ extensions: []
112
+
113
+ extra_rdoc_files:
114
+ - History.txt
115
+ - Manifest.txt
116
+ - README.txt
117
+ files:
118
+ - History.txt
119
+ - Manifest.txt
120
+ - README.txt
121
+ - Rakefile
122
+ - bin/zenprofile
123
+ - lib/spy_on.rb
124
+ - lib/zenprofile.rb
125
+ - lib/zenprofiler.rb
126
+ - misc/factorial.rb
127
+ - zenprofile-demo.sh
128
+ has_rdoc: true
129
+ homepage: http://rubyforge.org/projects/seattlerb
130
+ licenses: []
131
+
132
+ post_install_message:
133
+ rdoc_options:
134
+ - --main
135
+ - README.txt
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: "0"
143
+ version:
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: "0"
149
+ version:
150
+ requirements: []
151
+
152
+ rubyforge_project: seattlerb
153
+ rubygems_version: 1.3.2
154
+ signing_key:
155
+ specification_version: 3
156
+ summary: zenprofiler helps answer WHAT is being called the most
157
+ test_files: []
158
+
Binary file