zenprofile 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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