easy-prof 1.0.0
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.
- data/.gitignore +3 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +248 -0
- data/Rakefile +47 -0
- data/VERSION.yml +5 -0
- data/init.rb +1 -0
- data/lib/easy_prof.rb +52 -0
- data/lib/easy_profiler/action_controller_extensions.rb +35 -0
- data/lib/easy_profiler/configuration.rb +120 -0
- data/lib/easy_profiler/firebug_logger.rb +27 -0
- data/lib/easy_profiler/no_profile_instance.rb +5 -0
- data/lib/easy_profiler/profile.rb +59 -0
- data/lib/easy_profiler/profile_instance.rb +111 -0
- data/lib/easy_profiler/profile_instance_base.rb +93 -0
- data/spec/easy_profiler_spec.rb +8 -0
- data/spec/no_profile_instance_spec.rb +37 -0
- data/spec/profile_instance_spec.rb +113 -0
- data/spec/profile_spec.rb +113 -0
- data/spec/spec_helper.rb +9 -0
- metadata +84 -0
data/.gitignore
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Dmytro Shteflyuk
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
= easy-prof
|
2
|
+
|
3
|
+
Simple and easy to use Ruby code profiler, which could be used
|
4
|
+
as a Rails plugin.
|
5
|
+
|
6
|
+
== Installation
|
7
|
+
|
8
|
+
There are two options when approaching easy-prof installation:
|
9
|
+
|
10
|
+
* using the gem (recommended)
|
11
|
+
* install as a Rails plugin
|
12
|
+
|
13
|
+
To install as a gem, add this to your environment.rb:
|
14
|
+
|
15
|
+
config.gem 'easy-prof', :lib => 'easy_prof'
|
16
|
+
|
17
|
+
And then run the command:
|
18
|
+
|
19
|
+
sudo rake gems:install
|
20
|
+
|
21
|
+
To install meta-tags as a Rails plugin use this:
|
22
|
+
|
23
|
+
script/plugin install git://github.com/kpumuk/easy-prof.git
|
24
|
+
|
25
|
+
== Description
|
26
|
+
|
27
|
+
The main idea behind the easy-prof is creating check points and your
|
28
|
+
code and measuring time needed to execute code blocks. Here is the
|
29
|
+
example of easy-prof output:
|
30
|
+
|
31
|
+
[home#index] Benchmark results:
|
32
|
+
[home#index] debug: Logged in user home page
|
33
|
+
[home#index] progress: 0.7002 s [find top videos]
|
34
|
+
[home#index] progress: 0.0452 s [build categories list]
|
35
|
+
[home#index] progress: 0.0019 s [build tag cloud]
|
36
|
+
[home#index] progress: 0.0032 s [find featured videos]
|
37
|
+
[home#index] progress: 0.0324 s [find latest videos]
|
38
|
+
[home#index] debug: VIEW STARTED
|
39
|
+
[home#index] progress: 0.0649 s [top videos render]
|
40
|
+
[home#index] progress: 0.0014 s [categories render]
|
41
|
+
[home#index] progress: 2.5887 s [tag cloud render]
|
42
|
+
[home#index] progress: 0.0488 s [latest videos render]
|
43
|
+
[home#index] progress: 0.1053 s [featured video render]
|
44
|
+
[home#index] results: 3.592 s
|
45
|
+
|
46
|
+
From this output you can see what checkpoints takes longer to reach,
|
47
|
+
and what code fragments are pretty fast.
|
48
|
+
|
49
|
+
== Usage
|
50
|
+
|
51
|
+
The library extends <tt>Kernel</tt> with a method <tt>easy_profiler</tt>.
|
52
|
+
By default profiling is disabled globally, so you should pass :enabled
|
53
|
+
parameter to enable profiling of particular code. Also there is a time
|
54
|
+
:limit option which could be used to skip logging of blocks which are
|
55
|
+
fast enough.
|
56
|
+
|
57
|
+
For more details see the options description below.
|
58
|
+
|
59
|
+
easy_profiler('sleep', :enabled => true) do |p|
|
60
|
+
sleep 1
|
61
|
+
p.progress('sleep 1')
|
62
|
+
p.debug('checkpoint reached')
|
63
|
+
sleep 2
|
64
|
+
p.progress('sleep 2')
|
65
|
+
end
|
66
|
+
|
67
|
+
Method accepts two parameters: profiling session name and a hash of
|
68
|
+
options:
|
69
|
+
|
70
|
+
* <tt>:enabled</tt> -- value indicating whether profiling is enabled.
|
71
|
+
* <tt>:limit</tt> -- minimum time period which should be reached to
|
72
|
+
print profiling log.
|
73
|
+
* <tt>:count_ar_instances</tt> -- indicating whether profiler should
|
74
|
+
log an approximate number of instantiated ActiveRecord objects.
|
75
|
+
* <tt>:count_memory_usage</tt> -- indicating whether profiler should
|
76
|
+
log an approximate amount of memory used.
|
77
|
+
* <tt>:logger</tt> -- a +Logger+ instance.
|
78
|
+
|
79
|
+
== Configuration
|
80
|
+
|
81
|
+
There are some global configuration options exists:
|
82
|
+
|
83
|
+
EasyProfiler.configure do |config|
|
84
|
+
config.enable_profiling = false
|
85
|
+
config.print_limit = 0.01
|
86
|
+
config.count_ar_instances = false
|
87
|
+
config.count_memory_usage = false
|
88
|
+
config.logger = nil # or Rails.logger or whatever
|
89
|
+
config.colorize_logging = true
|
90
|
+
config.live_logging = false
|
91
|
+
end
|
92
|
+
|
93
|
+
* <tt>enable_profiling</tt> -- used to enable or disable profiling
|
94
|
+
globalle (<tt>false</tt> by default).
|
95
|
+
* <tt>print_limit</tt> -- used to set a minimum time period in seconds
|
96
|
+
which should be reached to dump profile to the log (<tt>0.01</tt>
|
97
|
+
by default).
|
98
|
+
* <tt>count_ar_instances</tt> -- indicating whether profiler should
|
99
|
+
log an approximate number of instantiated ActiveRecord objects.
|
100
|
+
* <tt>count_memory_usage</tt> -- indicating whether profiler should
|
101
|
+
log an approximate amount of memory used.
|
102
|
+
* <tt>logger</tt> -- a <tt>Logger</tt> instance to dump logs to.
|
103
|
+
* <tt>colorize_logging</tt> -- when <tt>true</tt>, output will be
|
104
|
+
colorized (useful when dumping profiling information into the
|
105
|
+
Rails log).
|
106
|
+
* <tt>live_logging</tt> -- when <tt>true</tt>, every profiling info
|
107
|
+
will be pushed to the log immediately (by default everything will
|
108
|
+
be dumped in the end of profiling session).
|
109
|
+
|
110
|
+
== Active Record instances number profiling
|
111
|
+
|
112
|
+
easy-prof can log a number of instantiated ActiveRecord instances.
|
113
|
+
To enable this kind of profiling, use a <tt>:count_ar_instances</tt>
|
114
|
+
option or global setting with the same name.
|
115
|
+
|
116
|
+
Please note, that easy-prof completely disables garbage collector
|
117
|
+
during this kind of profiling. It could hurt your overall application
|
118
|
+
performance, so do not use it on production boxes. Also I can't
|
119
|
+
guaranty 100% precision, but it is about this value in almost all
|
120
|
+
cases.
|
121
|
+
|
122
|
+
Further reading:
|
123
|
+
* That’s Not a Memory Leak, It’s Bloat http://www.engineyard.com/blog/2009/thats-not-a-memory-leak-its-bloat/
|
124
|
+
|
125
|
+
== Memory usage profiling
|
126
|
+
|
127
|
+
The plugin is able to log an amount of memory used by current Ruby
|
128
|
+
process. To enable this kind of profiling, use a <tt>:count_memory_usage</tt>
|
129
|
+
option or global setting with the same name.
|
130
|
+
|
131
|
+
Please note, that easy-prof completely disables garbage collector
|
132
|
+
during this kind of profiling. It could hurt your overall application
|
133
|
+
performance, so do not use it on production boxes. Also I can't
|
134
|
+
guaranty 100% precision, but it is about this value in almost all
|
135
|
+
cases.
|
136
|
+
|
137
|
+
== Dumping results to the Firebug console
|
138
|
+
|
139
|
+
If you are profiling a Ruby on Rails application, it could be useful
|
140
|
+
to get profiling results from production server sometimes. To achieve
|
141
|
+
this you can use a <tt>FirebugLogger</tt>, bundled with this plugin.
|
142
|
+
In any controller you have a helper called <tt>firebug_logger</tt>,
|
143
|
+
so you can pass it to EasyProfiler using <tt>:logger</tt> option:
|
144
|
+
|
145
|
+
easy_profiler('home#index', :logger => firebug_logger, :limit => 2) do |p|
|
146
|
+
end
|
147
|
+
|
148
|
+
The idea behind this logger is pretty simple (as everything in this
|
149
|
+
plugin): there is an <tt>after_filter</tt> named <tt>dump_firebug_profile</tt>,
|
150
|
+
which dumps profiling information after your action finished its work.
|
151
|
+
Please note: it will not output any line when profiling session is
|
152
|
+
disabled or time limit is not reached.
|
153
|
+
|
154
|
+
Do not forget to protect firebug output: it is a bad idea to allow
|
155
|
+
anyone to see your profiling session dump. You can allow admin
|
156
|
+
users only to use firebug, or restrict this feature by IP address.
|
157
|
+
|
158
|
+
BTW, you can use Firebug Console Lite (http://getfirebug.com/lite.html)
|
159
|
+
to get this feature working in any browser! By default it works
|
160
|
+
perfectly in Firefox with Firebug installed, and in Safari 4.
|
161
|
+
|
162
|
+
== Ruby on Rails application profiling
|
163
|
+
|
164
|
+
Here is the complete example of a Rails action profiling:
|
165
|
+
|
166
|
+
class HomeController < ApplicationController
|
167
|
+
def index
|
168
|
+
easy_profiler('home#index', :enabled => profile_request?, :limit => 2) do |p|
|
169
|
+
p.progress 'logged in user home page'
|
170
|
+
|
171
|
+
@top_videos = Video.top(:limit => 10)
|
172
|
+
p.progress 'find top videos'
|
173
|
+
|
174
|
+
@categories = Category.all(:order => 'name DESC')
|
175
|
+
p.progress 'build categories list'
|
176
|
+
|
177
|
+
@tag_cloud = Tag.tag_cloud(:limit => 200)
|
178
|
+
p.progress 'build tag cloud'
|
179
|
+
|
180
|
+
@featured_videos = Video.featured(limit => 5)
|
181
|
+
p.progress 'find featured videos'
|
182
|
+
|
183
|
+
@latest_videos = Video.latest(:limit => 5)
|
184
|
+
p.progress 'find latest videos'
|
185
|
+
|
186
|
+
@profiler = p
|
187
|
+
p.debug 'VIEW STARTED'
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
private
|
192
|
+
|
193
|
+
# Method returns +true+ if current request should ouput profiling information
|
194
|
+
def profile_request?
|
195
|
+
params['_with_profiling'] == 'yes'
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
And view:
|
200
|
+
|
201
|
+
<div id="top_videos">
|
202
|
+
<%= render :partial => 'top_videos' %>
|
203
|
+
<% @profiler.progress 'top videos render' %>
|
204
|
+
</div>
|
205
|
+
|
206
|
+
<div class="tabs">
|
207
|
+
<ul id="taxonomy">
|
208
|
+
<li><a href="#" id="categories" class="current">Categories</a></li>
|
209
|
+
<li><a href="#" id="tags">Tags</a></li>
|
210
|
+
</ul>
|
211
|
+
<div class="categories_panel">
|
212
|
+
<%= render :partial => 'categories' %>
|
213
|
+
<% @profiler.progress 'categories render' %>
|
214
|
+
</div>
|
215
|
+
<div class="categories_panel hidden">
|
216
|
+
<%= render :partial => 'tag_cloud' %>
|
217
|
+
<% @profiler.progress 'tag cloud render' %>
|
218
|
+
</div>
|
219
|
+
</div>
|
220
|
+
|
221
|
+
<div class="box">
|
222
|
+
<div id="latest">
|
223
|
+
<%= render :partial => 'videos', :videos => @latest_videos %>
|
224
|
+
<% @profiler.progress 'latest videos render' %>
|
225
|
+
</div>
|
226
|
+
<div id="featured">
|
227
|
+
<%= render :partial => 'videos', :videos => @featured_videos %>
|
228
|
+
<% @profiler.progress 'featured video render' %>
|
229
|
+
</div>
|
230
|
+
</div>
|
231
|
+
|
232
|
+
As you can see from this example, profiler will be enabled only when
|
233
|
+
you pass a _with_profiling parameter with value yes:
|
234
|
+
|
235
|
+
http://example.com/home?_with_profiling=yes
|
236
|
+
|
237
|
+
== Who are the authors?
|
238
|
+
|
239
|
+
This plugin has been created in Scribd.com for our internal use
|
240
|
+
and then the sources were opened for other people to use. All the
|
241
|
+
code in this package has been developed by Dmytro Shteflyuk for
|
242
|
+
Scribd.com and is released under the MIT license. For more details,
|
243
|
+
see the MIT-LICENSE file.
|
244
|
+
|
245
|
+
== Credits
|
246
|
+
|
247
|
+
* Dmytro Shteflyuk (author) <kpumuk@kpumuk.info> http://kpumuk.info
|
248
|
+
* Alexey Kovyrin (contributor) <alexey@kovyrin.net> http://kovyrin.net
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |gemspec|
|
6
|
+
gemspec.name = 'easy-prof'
|
7
|
+
gemspec.summary = 'Simple and easy to use Ruby code profiler'
|
8
|
+
gemspec.description = 'Simple Ruby code profiler to use both in Rails applications and generic Ruby scripts.'
|
9
|
+
gemspec.email = 'kpumuk@kpumuk.info'
|
10
|
+
gemspec.homepage = 'http://github.com/kpumuk/easy-prof'
|
11
|
+
gemspec.authors = ['Dmytro Shteflyuk']
|
12
|
+
end
|
13
|
+
Jeweler::GemcutterTasks.new
|
14
|
+
rescue LoadError
|
15
|
+
puts 'Jeweler not available. Install it with: sudo gem install jeweler'
|
16
|
+
end
|
17
|
+
|
18
|
+
begin
|
19
|
+
require 'spec/rake/spectask'
|
20
|
+
|
21
|
+
desc 'Default: run unit tests.'
|
22
|
+
task :default => :spec
|
23
|
+
|
24
|
+
desc 'Test the easy-prof plugin.'
|
25
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
26
|
+
t.libs << 'lib'
|
27
|
+
t.pattern = 'spec/**/*_spec.rb'
|
28
|
+
t.verbose = true
|
29
|
+
t.spec_opts = ['-cfs']
|
30
|
+
end
|
31
|
+
rescue LoadError
|
32
|
+
puts 'RSpec not available. Install it with: sudo gem install rspec'
|
33
|
+
end
|
34
|
+
|
35
|
+
begin
|
36
|
+
require 'yard'
|
37
|
+
YARD::Rake::YardocTask.new(:yard) do |t|
|
38
|
+
t.options = ['--title', 'EasyProf Documentation']
|
39
|
+
if ENV['PRIVATE']
|
40
|
+
t.options.concat ['--protected', '--private']
|
41
|
+
else
|
42
|
+
t.options.concat ['--protected', '--no-private']
|
43
|
+
end
|
44
|
+
end
|
45
|
+
rescue LoadError
|
46
|
+
puts 'Yard not available. Install it with: sudo gem install yard'
|
47
|
+
end
|
data/VERSION.yml
ADDED
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'easy_prof'
|
data/lib/easy_prof.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module EasyProfiler
|
4
|
+
autoload :Configuration, 'easy_profiler/configuration'
|
5
|
+
autoload :Profile, 'easy_profiler/profile'
|
6
|
+
autoload :ProfileInstanceBase, 'easy_profiler/profile_instance_base'
|
7
|
+
autoload :ProfileInstance, 'easy_profiler/profile_instance'
|
8
|
+
autoload :NoProfileInstance, 'easy_profiler/no_profile_instance'
|
9
|
+
autoload :FirebugLogger, 'easy_profiler/firebug_logger'
|
10
|
+
autoload :ActionControllerExtensions, 'easy_profiler/action_controller_extensions'
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def configure(force = false)
|
14
|
+
yield configuration(force)
|
15
|
+
end
|
16
|
+
|
17
|
+
def configuration(force = false)
|
18
|
+
if !@configuration || force
|
19
|
+
@configuration = Configuration.new
|
20
|
+
end
|
21
|
+
@configuration
|
22
|
+
end
|
23
|
+
alias :config :configuration
|
24
|
+
end
|
25
|
+
extend ClassMethods
|
26
|
+
end
|
27
|
+
|
28
|
+
if Object.const_defined?(:ActionController)
|
29
|
+
ActionController::Base.send(:include, EasyProfiler::ActionControllerExtensions)
|
30
|
+
end
|
31
|
+
|
32
|
+
module Kernel
|
33
|
+
# Wraps code block into the profiling session.
|
34
|
+
#
|
35
|
+
# See the <tt>EasyProfiler::Profile.start</tt> method for
|
36
|
+
# parameters description.
|
37
|
+
#
|
38
|
+
# Example:
|
39
|
+
# easy_profiler('sleep', :enabled => true) do |p|
|
40
|
+
# sleep 1
|
41
|
+
# p.progress('sleep 1')
|
42
|
+
# p.debug('checkpoint reached')
|
43
|
+
# sleep 2
|
44
|
+
# p.progress('sleep 2')
|
45
|
+
# end
|
46
|
+
def easy_profiler(name, options = {})
|
47
|
+
profiler = EasyProfiler::Profile.start(name, options)
|
48
|
+
yield profiler
|
49
|
+
ensure
|
50
|
+
EasyProfiler::Profile.stop(name) if profiler
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module EasyProfiler
|
2
|
+
module ActionControllerExtensions
|
3
|
+
def self.included(base) #:nodoc:
|
4
|
+
base.send :include, InstanceMethods
|
5
|
+
|
6
|
+
base.class_eval do
|
7
|
+
after_filter :dump_firebug_profile
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
# Exposes firebug variable where logs can be submitted.
|
13
|
+
#
|
14
|
+
# class UserController < ApplicationController
|
15
|
+
# def index
|
16
|
+
# firebug.debug 'Why I can be easily debugging with this thing!'
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
def firebug_logger
|
20
|
+
@_firebug_logger ||= FirebugLogger.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def dump_firebug_profile
|
24
|
+
return if firebug_logger.logs.empty?
|
25
|
+
|
26
|
+
logs = firebug_logger.logs.collect do |message|
|
27
|
+
# We have to add any escape characters
|
28
|
+
"console.info('#{self.class.helpers.escape_javascript(message)}');"
|
29
|
+
end.join("\n")
|
30
|
+
|
31
|
+
response.body << self.class.helpers.javascript_tag(logs)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module EasyProfiler
|
2
|
+
class Configuration
|
3
|
+
# Value indicating whether profiling is globally enabled.
|
4
|
+
attr_reader :enable_profiling
|
5
|
+
|
6
|
+
# Minimum time period which should be reached to dump
|
7
|
+
# profile to the log.
|
8
|
+
attr_reader :print_limit
|
9
|
+
|
10
|
+
# Value indicating whether profiler should log an
|
11
|
+
# approximate number of instantiated ActiveRecord objects.
|
12
|
+
attr_reader :count_ar_instances
|
13
|
+
|
14
|
+
# Value indicating whether profiler should log an
|
15
|
+
# approximate amount of memory used.
|
16
|
+
attr_reader :count_memory_usage
|
17
|
+
|
18
|
+
# Accepts a logger conforming to the interface of Log4r or the
|
19
|
+
# default Ruby 1.8+ Logger class, which is then passed
|
20
|
+
# on to any profiler instance made.
|
21
|
+
attr_writer :logger
|
22
|
+
|
23
|
+
attr_reader :colorize_logging
|
24
|
+
|
25
|
+
# Value indicating whether profiler should flush
|
26
|
+
# logs on every checkpoint.
|
27
|
+
attr_reader :live_logging
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@enable_profiling = false
|
31
|
+
@print_limit = 0.01
|
32
|
+
@count_ar_instances = false
|
33
|
+
@count_memory_usage = false
|
34
|
+
@logger = nil
|
35
|
+
@colorize_logging = true
|
36
|
+
@live_logging = false
|
37
|
+
end
|
38
|
+
|
39
|
+
# Sets a value indicating whether profiling is globally enabled.
|
40
|
+
def enable_profiling=(value)
|
41
|
+
@enable_profiling = !!value
|
42
|
+
end
|
43
|
+
|
44
|
+
# Sets a minimum time period which should be reached to dump
|
45
|
+
# profile to the log.
|
46
|
+
def print_limit=(value)
|
47
|
+
@print_limit = FalseClass === value ? false : value.to_f
|
48
|
+
end
|
49
|
+
|
50
|
+
# Sets a value indicating whether profiler should log an
|
51
|
+
# approximate number of instantiated ActiveRecord objects.
|
52
|
+
def count_ar_instances=(value)
|
53
|
+
@count_ar_instances = !!value
|
54
|
+
end
|
55
|
+
|
56
|
+
# Sets a value indicating whether profiler should log an
|
57
|
+
# approximate amount of memory used.
|
58
|
+
#
|
59
|
+
# @param Boolean value
|
60
|
+
# identifies whether memory profiling should be enabled.
|
61
|
+
#
|
62
|
+
def count_memory_usage=(value)
|
63
|
+
@count_memory_usage = !!value
|
64
|
+
end
|
65
|
+
|
66
|
+
def colorize_logging=(value)
|
67
|
+
@colorize_logging = !!value
|
68
|
+
end
|
69
|
+
|
70
|
+
# Sets a value indicating whether profiler should flush
|
71
|
+
# logs on every checkpoint.
|
72
|
+
#
|
73
|
+
def live_logging=(value)
|
74
|
+
@live_logging = !!value
|
75
|
+
end
|
76
|
+
|
77
|
+
# Gets a logger instance.
|
78
|
+
#
|
79
|
+
# When profiler is started inside Rails application,
|
80
|
+
# creates a "log/profile.log" files where all profile
|
81
|
+
# logs will be places. In regular scripts dumps
|
82
|
+
# information directly to STDOUT. You can use
|
83
|
+
# <tt>EasyProfiler.configuration.logger</tt> to set another
|
84
|
+
# logger.
|
85
|
+
#
|
86
|
+
def logger
|
87
|
+
unless @logger
|
88
|
+
@logger = if Object.const_defined?(:Rails)
|
89
|
+
Logger.new(File.join(Rails.root, 'log', 'profile.log'))
|
90
|
+
else
|
91
|
+
Logger.new($stdout)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
@logger
|
95
|
+
end
|
96
|
+
|
97
|
+
def merge(options = {})
|
98
|
+
config = self.dup
|
99
|
+
config.enable_profiling = options[:enabled] if options.has_key?(:enabled)
|
100
|
+
config.enable_profiling = options[:enable_profiling] if options.has_key?(:enable_profiling)
|
101
|
+
config.print_limit = options[:limit] if options.has_key?(:limit)
|
102
|
+
config.print_limit = options[:print_limit] if options.has_key?(:print_limit)
|
103
|
+
config.count_ar_instances = options[:count_ar_instances] if options.has_key?(:count_ar_instances)
|
104
|
+
config.count_memory_usage = options[:count_memory_usage] if options.has_key?(:count_memory_usage)
|
105
|
+
config.logger = options[:logger] if options.has_key?(:logger)
|
106
|
+
config.colorize_logging = options[:colorize_logging] if options.has_key?(:colorize_logging)
|
107
|
+
config.live_logging = options[:live_logging] if options.has_key?(:live_logging)
|
108
|
+
|
109
|
+
config
|
110
|
+
end
|
111
|
+
|
112
|
+
def disable_gc?
|
113
|
+
count_ar_instances or count_memory_usage
|
114
|
+
end
|
115
|
+
|
116
|
+
def enabled?
|
117
|
+
enable_profiling
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module EasyProfiler
|
2
|
+
# A logger used to output logs to the Firebug console.
|
3
|
+
#
|
4
|
+
# Based on http://rails.aizatto.com/category/plugins/firebug-logger/
|
5
|
+
class FirebugLogger
|
6
|
+
attr_accessor :logs
|
7
|
+
|
8
|
+
def initialize #:nodoc:
|
9
|
+
@logs = []
|
10
|
+
end
|
11
|
+
|
12
|
+
# Clear the logs.
|
13
|
+
def clear
|
14
|
+
@logs = []
|
15
|
+
end
|
16
|
+
|
17
|
+
# Adds a line to the log.
|
18
|
+
#
|
19
|
+
# == arguments
|
20
|
+
# * +message+ -- log message to be logged
|
21
|
+
# * +block+ -- optional. Return value of the block will be the message that will be logged.
|
22
|
+
def info(message = nil)
|
23
|
+
message = yield if message.nil? && block_given?
|
24
|
+
@logs << message
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module EasyProfiler
|
2
|
+
# Contains global profiling parameters and methods to start and
|
3
|
+
# stop profiling.
|
4
|
+
class Profile
|
5
|
+
@@profile_results = {}
|
6
|
+
|
7
|
+
# Starts a profiling session.
|
8
|
+
#
|
9
|
+
# Parameters:
|
10
|
+
# * +name+ -- session name.
|
11
|
+
# * +options+ -- a +Hash+ of options.
|
12
|
+
#
|
13
|
+
# Possible options:
|
14
|
+
# * <tt>:enabled</tt> -- value indicating whether profiling is enabled.
|
15
|
+
# * <tt>:limit</tt> -- minimum time period which should be reached to print profiling log.
|
16
|
+
# * <tt>:count_ar_instances</tt> —- indicating whether profiler should log an approximate number of instantiated ActiveRecord objects.
|
17
|
+
# * <tt>:count_memory_usage</tt> —- indicating whether profiler should log an approximate amount of memory used.
|
18
|
+
# * <tt>:logger</tt> -- a +Logger+ instance.
|
19
|
+
# * <tt>:colorize_logging</tt> -- indicating whether profiling log lines should be colorized.
|
20
|
+
# * <tt>:live_logging</tt> -- indicating whether profiler should flush logs on every checkpoint.
|
21
|
+
#
|
22
|
+
# Returns:
|
23
|
+
# * an instance of profiler (descendant of the <tt>EasyProfiler::ProfileInstanceBase</tt> class).
|
24
|
+
def self.start(name, options = {})
|
25
|
+
if @@profile_results[name]
|
26
|
+
raise ArgumentError.new("EasyProfiler::Profile.start() collision! '#{name}' is already started.")
|
27
|
+
end
|
28
|
+
|
29
|
+
config = EasyProfiler.configuration.merge(options)
|
30
|
+
|
31
|
+
# Disable garbage collector to get more precise results
|
32
|
+
GC.disable if config.disable_gc?
|
33
|
+
|
34
|
+
klass = config.enabled? ? ProfileInstance : NoProfileInstance
|
35
|
+
instance = klass.new(name, config)
|
36
|
+
|
37
|
+
@@profile_results[name] = instance
|
38
|
+
end
|
39
|
+
|
40
|
+
# Finishes a profiling session and dumps results to the log.
|
41
|
+
#
|
42
|
+
# Parameters:
|
43
|
+
# * +name+ -- session name, used in +start+ method.
|
44
|
+
def self.stop(name)
|
45
|
+
unless instance = @@profile_results.delete(name)
|
46
|
+
raise ArgumentError.new("EasyProfiler::Profile.stop() error! '#{name}' is not started yet.")
|
47
|
+
end
|
48
|
+
|
49
|
+
instance.dump_results
|
50
|
+
|
51
|
+
# Enable garbage collector which has been disabled before
|
52
|
+
GC.enable if instance.config.disable_gc?
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.reset!
|
56
|
+
@@profile_results = {}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module EasyProfiler
|
2
|
+
# Class used when profiling is disabled.
|
3
|
+
class ProfileInstance < ProfileInstanceBase
|
4
|
+
# Sets a profiling checkpoint (block execution time will be printed).
|
5
|
+
#
|
6
|
+
# Parameters:
|
7
|
+
# * message -- a message to associate with a check point.
|
8
|
+
def progress(message)
|
9
|
+
progress = (now = Time.now.to_f) - @progress
|
10
|
+
@progress = now
|
11
|
+
|
12
|
+
ar_instances_count = if config.count_ar_instances
|
13
|
+
ar_instances_delta = (ar_instances = active_record_instances_count) - @current_ar_instances
|
14
|
+
@current_ar_instances = ar_instances
|
15
|
+
", #{ar_instances_delta} AR objects"
|
16
|
+
end
|
17
|
+
|
18
|
+
memory_usage_value = if config.count_memory_usage
|
19
|
+
memory_usage_delta = (memory_usage = process_memory_usage) - @current_memory_usage
|
20
|
+
@current_memory_usage = memory_usage
|
21
|
+
", #{format_memory_size(total_memory_usage)}"
|
22
|
+
end
|
23
|
+
|
24
|
+
buffer_checkpoint("progress: %0.4f s#{ar_instances_count}#{memory_usage_value} [#{message}]" % progress)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Sets a profiling checkpoint without execution time printing.
|
28
|
+
#
|
29
|
+
# Parameters:
|
30
|
+
# * message -- a message to associate with a check point.
|
31
|
+
def debug(message)
|
32
|
+
@progress = Time.now.to_f
|
33
|
+
buffer_checkpoint("debug: #{message}")
|
34
|
+
end
|
35
|
+
|
36
|
+
# Dumps results to the log.
|
37
|
+
def dump_results
|
38
|
+
progress('END')
|
39
|
+
t = total
|
40
|
+
|
41
|
+
if config.live_logging || false === config.print_limit || t > config.print_limit.to_f
|
42
|
+
log_header(true)
|
43
|
+
@buffer.each { |message| log_line(message) }
|
44
|
+
log_footer(t)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Gets a total profiling time.
|
51
|
+
def total
|
52
|
+
Time.now.to_f - @start
|
53
|
+
end
|
54
|
+
|
55
|
+
# Gets a number of total AR objects instantiated number.
|
56
|
+
def total_ar_instances
|
57
|
+
active_record_instances_count - @start_ar_instances
|
58
|
+
end
|
59
|
+
|
60
|
+
# Gets a total amount of memory used.
|
61
|
+
def total_memory_usage
|
62
|
+
process_memory_usage - @start_memory_usage
|
63
|
+
end
|
64
|
+
|
65
|
+
# Buffers a profiling checkpoint.
|
66
|
+
def buffer_checkpoint(message)
|
67
|
+
log_header
|
68
|
+
if config.live_logging
|
69
|
+
log_line(message)
|
70
|
+
else
|
71
|
+
@buffer << message
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Write a header to the log.
|
76
|
+
def log_header(force = false)
|
77
|
+
if (config.live_logging && !@header_printed) || (!config.live_logging && force)
|
78
|
+
log_line("Benchmark results:")
|
79
|
+
@header_printed = true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Write a footer with summary stats to the log.
|
84
|
+
def log_footer(total_time)
|
85
|
+
ar_instances_count = if config.count_ar_instances
|
86
|
+
", #{total_ar_instances} AR objects"
|
87
|
+
end
|
88
|
+
|
89
|
+
memory_usage_value = if config.count_memory_usage
|
90
|
+
", #{format_memory_size(total_memory_usage)}"
|
91
|
+
end
|
92
|
+
|
93
|
+
log_line("results: %0.4f s#{ar_instances_count}#{memory_usage_value}" % total_time)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Write a log line.
|
97
|
+
def log_line(line)
|
98
|
+
if config.colorize_logging
|
99
|
+
@@row_even, message_color = if @@row_even
|
100
|
+
[false, '4;32;1']
|
101
|
+
else
|
102
|
+
[true, '4;33;1']
|
103
|
+
end
|
104
|
+
|
105
|
+
config.logger.info(" [\e[#{message_color}m%s\e[0m] %s" % [@name, line])
|
106
|
+
else
|
107
|
+
config.logger.info("[%s] %s" % [@name, line])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module EasyProfiler
|
2
|
+
# Base class for profilers.
|
3
|
+
class ProfileInstanceBase
|
4
|
+
attr_reader :name, :config
|
5
|
+
|
6
|
+
@@row_even = true
|
7
|
+
|
8
|
+
# Initializes a new instance of +ProfileInstanceBase+ class.
|
9
|
+
#
|
10
|
+
# Parameters:
|
11
|
+
# * name -- session name.
|
12
|
+
# * options -- a +Hash+ of options (see <tt>EasyProfiler::Profile.start</tt> for details).
|
13
|
+
def initialize(name, config = nil)
|
14
|
+
@name = name
|
15
|
+
@config = case config
|
16
|
+
when Hash: EasyProfiler.configuration.merge(config)
|
17
|
+
when EasyProfiler::Configuration: config
|
18
|
+
else EasyProfiler.configuration
|
19
|
+
end
|
20
|
+
|
21
|
+
@start = @progress = Time.now.to_f
|
22
|
+
|
23
|
+
# Initial number of ActiveRecord::Base objects
|
24
|
+
if @config.count_ar_instances
|
25
|
+
@start_ar_instances = @current_ar_instances = active_record_instances_count
|
26
|
+
end
|
27
|
+
|
28
|
+
# Initial amount of memory used
|
29
|
+
if @config.count_memory_usage
|
30
|
+
@start_memory_usage = @current_memory_usage = process_memory_usage
|
31
|
+
end
|
32
|
+
|
33
|
+
# A buffer where all log messeges will be stored till the
|
34
|
+
# end of the profiling block. We need this because not every
|
35
|
+
# profiling log will be printed (see EasyProf::Configuration.print_limit).
|
36
|
+
@buffer = []
|
37
|
+
end
|
38
|
+
|
39
|
+
# Sets a profiling checkpoint (block execution time will be printed).
|
40
|
+
#
|
41
|
+
# Parameters:
|
42
|
+
# * message -- a message to associate with a check point.
|
43
|
+
#
|
44
|
+
def progress(message)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Sets a profiling checkpoint without execution time printing.
|
48
|
+
#
|
49
|
+
# Parameters:
|
50
|
+
# * message -- a message to associate with a check point.
|
51
|
+
#
|
52
|
+
def debug(message)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Dumps results to the log.
|
56
|
+
def dump_results
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
# Returns a number of ActiveRecord instances in the Object Space.
|
62
|
+
#
|
63
|
+
def active_record_instances_count
|
64
|
+
count = 0
|
65
|
+
ObjectSpace.each_object(::ActiveRecord::Base) { count += 1 }
|
66
|
+
count
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns an amount of memory used by current Ruby process.
|
70
|
+
#
|
71
|
+
def process_memory_usage
|
72
|
+
`ps -o rss= -p #{$$}`.to_i
|
73
|
+
end
|
74
|
+
|
75
|
+
# Formats an amount of memory to print.
|
76
|
+
#
|
77
|
+
def format_memory_size(number)
|
78
|
+
if number > 10 ** 9
|
79
|
+
number = number.to_f / (10 ** 9)
|
80
|
+
suffix = 'G'
|
81
|
+
elsif number > 10 ** 6
|
82
|
+
number = number.to_f / (10 ** 6)
|
83
|
+
suffix = 'M'
|
84
|
+
elsif number > 10 ** 3
|
85
|
+
number = number.to_f / (10 ** 3)
|
86
|
+
suffix = 'K'
|
87
|
+
else
|
88
|
+
suffix = 'B'
|
89
|
+
end
|
90
|
+
"%.2f#{suffix}" % number
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe EasyProfiler::NoProfileInstance do
|
4
|
+
it 'should receive name and options in initialize' do
|
5
|
+
profiler = EasyProfiler::NoProfileInstance.new('myprofiler1')
|
6
|
+
profiler.name.should == 'myprofiler1'
|
7
|
+
profiler.config.should be(EasyProfiler.configuration)
|
8
|
+
|
9
|
+
profiler = EasyProfiler::NoProfileInstance.new('myprofiler2', { :print_limit => 100 })
|
10
|
+
profiler.name.should == 'myprofiler2'
|
11
|
+
profiler.config.print_limit.should == 100
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should respond to :progress' do
|
15
|
+
profiler = EasyProfiler::NoProfileInstance.new('myprofiler')
|
16
|
+
profiler.should respond_to(:progress)
|
17
|
+
lambda {
|
18
|
+
profiler.progress('message')
|
19
|
+
}.should_not raise_error
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should respond to :debug' do
|
23
|
+
profiler = EasyProfiler::NoProfileInstance.new('myprofiler')
|
24
|
+
profiler.should respond_to(:debug)
|
25
|
+
lambda {
|
26
|
+
profiler.debug('message')
|
27
|
+
}.should_not raise_error
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should respond to :dump_results' do
|
31
|
+
profiler = EasyProfiler::NoProfileInstance.new('myprofiler')
|
32
|
+
profiler.should respond_to(:dump_results)
|
33
|
+
lambda {
|
34
|
+
profiler.dump_results
|
35
|
+
}.should_not raise_error
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe EasyProfiler::ProfileInstance do
|
4
|
+
it 'should receive name and options in initialize' do
|
5
|
+
profiler = EasyProfiler::ProfileInstance.new('myprofiler1')
|
6
|
+
profiler.name.should == 'myprofiler1'
|
7
|
+
profiler.config.should be(EasyProfiler.configuration)
|
8
|
+
|
9
|
+
profiler = EasyProfiler::ProfileInstance.new('myprofiler2', { :print_limit => 100 })
|
10
|
+
profiler.name.should == 'myprofiler2'
|
11
|
+
profiler.config.print_limit.should == 100
|
12
|
+
|
13
|
+
config = EasyProfiler::Configuration.new
|
14
|
+
config.print_limit = 100
|
15
|
+
profiler = EasyProfiler::ProfileInstance.new('myprofiler3', config)
|
16
|
+
profiler.name.should == 'myprofiler3'
|
17
|
+
profiler.config.should be(config)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should respond to :progress' do
|
21
|
+
profiler = EasyProfiler::ProfileInstance.new('myprofiler')
|
22
|
+
profiler.should respond_to(:progress)
|
23
|
+
lambda {
|
24
|
+
profiler.progress('message')
|
25
|
+
buffer = profiler.instance_variable_get(:@buffer)
|
26
|
+
buffer.should have(1).item
|
27
|
+
buffer.first.should match(/progress: \d+\.\d+ s \[message\]/)
|
28
|
+
}.should_not raise_error
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should respond to :debug' do
|
32
|
+
profiler = EasyProfiler::ProfileInstance.new('myprofiler')
|
33
|
+
profiler.should respond_to(:debug)
|
34
|
+
lambda {
|
35
|
+
profiler.debug('message')
|
36
|
+
buffer = profiler.instance_variable_get(:@buffer)
|
37
|
+
buffer.should have(1).item
|
38
|
+
buffer.first.should match(/debug: message/)
|
39
|
+
}.should_not raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should respond to :dump_results' do
|
43
|
+
logger = mock('MockLogger')
|
44
|
+
profiler = EasyProfiler::ProfileInstance.new('myprofiler', :logger => logger, :enabled => true, :limit => false, :colorize_logging => false)
|
45
|
+
profiler.should respond_to(:dump_results)
|
46
|
+
|
47
|
+
profiler.progress('progress message')
|
48
|
+
profiler.debug('debug message')
|
49
|
+
|
50
|
+
logger.should_receive(:info).ordered.with(/\[myprofiler\] Benchmark results:/)
|
51
|
+
logger.should_receive(:info).ordered.with(/\[myprofiler\] progress: \d+\.\d+ s \[progress message\]/)
|
52
|
+
logger.should_receive(:info).ordered.with(/\[myprofiler\] debug: debug message/)
|
53
|
+
logger.should_receive(:info).ordered.with(/\[myprofiler\] progress: \d+\.\d+ s \[END\]/)
|
54
|
+
logger.should_receive(:info).ordered.with(/\[myprofiler\] results: \d+\.\d+ s/)
|
55
|
+
|
56
|
+
profiler.dump_results
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should render nothing when time limit not reached' do
|
60
|
+
logger = mock('MockLogger')
|
61
|
+
profiler = EasyProfiler::ProfileInstance.new('myprofiler', :logger => logger, :enabled => true, :limit => 20)
|
62
|
+
logger.should_not_receive(:info)
|
63
|
+
profiler.dump_results
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when live logging is enabled' do
|
67
|
+
before :each do
|
68
|
+
@logger = mock('MockLogger').as_null_object
|
69
|
+
@profiler = EasyProfiler::ProfileInstance.new('myprofiler', :logger => @logger, :enabled => true, :live_logging => true, :colorize_logging => false)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should print header when progress called first time' do
|
73
|
+
@logger.should_receive(:info).with(/\[myprofiler\] Benchmark results:/)
|
74
|
+
@profiler.progress('progress message')
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should print header when debug called first time' do
|
78
|
+
@logger.should_receive(:info).with(/\[myprofiler\] Benchmark results:/)
|
79
|
+
@profiler.debug('progress message')
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should not print header in dump_results' do
|
83
|
+
@profiler.debug('progress message')
|
84
|
+
@logger.should_not_receive(:info).with(/\[myprofiler\] Benchmark results:/)
|
85
|
+
@profiler.dump_results
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should print foter in dump_results' do
|
89
|
+
@profiler.debug('progress message')
|
90
|
+
@logger.should_receive(:info).ordered.with(/\[myprofiler\] results: \d+\.\d+ s/)
|
91
|
+
@profiler.dump_results
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should flush logs on every checkpoing if live logging is enabled' do
|
95
|
+
@logger.should_receive(:info).with(/\[myprofiler\] Benchmark results:/)
|
96
|
+
@logger.should_receive(:info).ordered.with(/\[myprofiler\] progress: \d+\.\d+ s \[progress message\]/)
|
97
|
+
@logger.should_receive(:info).ordered.with(/\[myprofiler\] debug: debug message/)
|
98
|
+
@profiler.progress('progress message')
|
99
|
+
@profiler.debug('debug message')
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should print footer in dump_results' do
|
103
|
+
@logger.should_receive(:info).with(/\[myprofiler\] results: \d+\.\d+ s/)
|
104
|
+
@profiler.dump_results
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should print header in dump_results if did not already' do
|
108
|
+
@logger.should_receive(:info).with(/\[myprofiler\] Benchmark results:/)
|
109
|
+
@logger.should_receive(:info).with(/\[myprofiler\] results: \d+\.\d+ s/)
|
110
|
+
@profiler.dump_results
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe EasyProfiler::Profile do
|
4
|
+
after :each do
|
5
|
+
EasyProfiler.configure do |config|
|
6
|
+
config.enable_profiling = false
|
7
|
+
config.print_limit = 0.01
|
8
|
+
config.count_ar_instances = false
|
9
|
+
config.count_memory_usage = false
|
10
|
+
end
|
11
|
+
EasyProfiler::Profile.reset!
|
12
|
+
end
|
13
|
+
|
14
|
+
context '.start' do
|
15
|
+
it 'should pass name to profile instance' do
|
16
|
+
EasyProfiler::Profile.start('myprofiler').name.should == 'myprofiler'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should pass options to profile instance' do
|
20
|
+
options = { :print_limit => 100 }
|
21
|
+
EasyProfiler::Profile.start('myprofiler', options).config.print_limit.should == 100
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should create a ProfileInstance object when enabled' do
|
25
|
+
options = { :enabled => true }
|
26
|
+
EasyProfiler::Profile.start('myprofiler', options).class.should == EasyProfiler::ProfileInstance
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should create a NoProfileInstance object when disabled' do
|
30
|
+
options = { :enabled => false }
|
31
|
+
EasyProfiler::Profile.start('myprofiler', options).class.should == EasyProfiler::NoProfileInstance
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should raise an error when two profilers with the same name started' do
|
35
|
+
EasyProfiler::Profile.start('myprofiler')
|
36
|
+
lambda {
|
37
|
+
EasyProfiler::Profile.start('myprofiler')
|
38
|
+
}.should raise_error(ArgumentError)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should use global :enabled value' do
|
42
|
+
EasyProfiler::Profile.start('myprofiler1').config.enable_profiling.should be_false
|
43
|
+
EasyProfiler.config.enable_profiling = true
|
44
|
+
EasyProfiler::Profile.start('myprofiler2').config.enable_profiling.should be_true
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should use global :limit value' do
|
48
|
+
EasyProfiler::Profile.start('myprofiler1').config.print_limit.should == 0.01
|
49
|
+
EasyProfiler.config.print_limit = 10
|
50
|
+
EasyProfiler::Profile.start('myprofiler2').config.print_limit.should == 10.0
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should use global :count_ar_instances value' do
|
54
|
+
EasyProfiler::Profile.start('myprofiler1').config.count_ar_instances.should be_false
|
55
|
+
EasyProfiler.config.count_ar_instances = true
|
56
|
+
EasyProfiler::Profile.start('myprofiler2').config.count_ar_instances.should be_true
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should use global :count_memory_usage value' do
|
60
|
+
EasyProfiler::Profile.start('myprofiler1').config.count_memory_usage.should be_false
|
61
|
+
EasyProfiler.config.count_memory_usage = true
|
62
|
+
EasyProfiler::Profile.start('myprofiler2').config.count_memory_usage.should be_true
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should use global :logger value' do
|
66
|
+
logger = mock('MockLogger')
|
67
|
+
EasyProfiler.config.logger = logger
|
68
|
+
EasyProfiler::Profile.start('myprofiler2').config.logger.should be(logger)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should use global :live_logging value' do
|
72
|
+
EasyProfiler::Profile.start('myprofiler1').config.live_logging.should be_false
|
73
|
+
EasyProfiler.config.live_logging = true
|
74
|
+
EasyProfiler::Profile.start('myprofiler2').config.live_logging.should be_true
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should disable garbage collector when needed' do
|
78
|
+
options = { :enabled => true, :count_ar_instances => true }
|
79
|
+
GC.should_receive(:disable)
|
80
|
+
EasyProfiler::Profile.start('myprofiler1', options)
|
81
|
+
|
82
|
+
options = { :enabled => true, :count_memory_usage => true }
|
83
|
+
GC.should_receive(:disable)
|
84
|
+
EasyProfiler::Profile.start('myprofiler2', options)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context '.stop' do
|
89
|
+
it 'should raise an error when profiler is not started' do
|
90
|
+
lambda {
|
91
|
+
EasyProfiler::Profile.stop('myprofiler')
|
92
|
+
}.should raise_error(ArgumentError)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should call dump_results method on profiler' do
|
96
|
+
profiler = mock_profile_start('myprofiler', :enabled => true)
|
97
|
+
profiler.should_receive(:dump_results)
|
98
|
+
EasyProfiler::Profile.stop('myprofiler')
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should enable back garbage collector when needed' do
|
102
|
+
profiler = mock_profile_start('myprofiler1', :enabled => true, :count_ar_instances => true)
|
103
|
+
profiler.stub!(:dump_results)
|
104
|
+
GC.should_receive(:enable)
|
105
|
+
EasyProfiler::Profile.stop('myprofiler1')
|
106
|
+
|
107
|
+
profiler = mock_profile_start('myprofiler2', :enabled => true, :count_memory_usage => true)
|
108
|
+
profiler.stub!(:dump_results)
|
109
|
+
GC.should_receive(:enable)
|
110
|
+
EasyProfiler::Profile.stop('myprofiler2')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_record'
|
3
|
+
require File.join(File.dirname(__FILE__), "../lib/easy_prof")
|
4
|
+
|
5
|
+
def mock_profile_start(name, options = {})
|
6
|
+
config = EasyProfiler.configuration.merge(options)
|
7
|
+
profiler = mock 'MockProfiler', :name => name, :config => config
|
8
|
+
EasyProfiler::Profile.send(:class_variable_get, :@@profile_results)[name] = profiler
|
9
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: easy-prof
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Dmytro Shteflyuk
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-02-23 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Simple Ruby code profiler to use both in Rails applications and generic Ruby scripts.
|
22
|
+
email: kpumuk@kpumuk.info
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README.rdoc
|
29
|
+
files:
|
30
|
+
- .gitignore
|
31
|
+
- MIT-LICENSE
|
32
|
+
- README.rdoc
|
33
|
+
- Rakefile
|
34
|
+
- VERSION.yml
|
35
|
+
- init.rb
|
36
|
+
- lib/easy_prof.rb
|
37
|
+
- lib/easy_profiler/action_controller_extensions.rb
|
38
|
+
- lib/easy_profiler/configuration.rb
|
39
|
+
- lib/easy_profiler/firebug_logger.rb
|
40
|
+
- lib/easy_profiler/no_profile_instance.rb
|
41
|
+
- lib/easy_profiler/profile.rb
|
42
|
+
- lib/easy_profiler/profile_instance.rb
|
43
|
+
- lib/easy_profiler/profile_instance_base.rb
|
44
|
+
- spec/easy_profiler_spec.rb
|
45
|
+
- spec/no_profile_instance_spec.rb
|
46
|
+
- spec/profile_instance_spec.rb
|
47
|
+
- spec/profile_spec.rb
|
48
|
+
- spec/spec_helper.rb
|
49
|
+
has_rdoc: true
|
50
|
+
homepage: http://github.com/kpumuk/easy-prof
|
51
|
+
licenses: []
|
52
|
+
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --charset=UTF-8
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
version: "0"
|
72
|
+
requirements: []
|
73
|
+
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 1.3.6
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: Simple and easy to use Ruby code profiler
|
79
|
+
test_files:
|
80
|
+
- spec/easy_profiler_spec.rb
|
81
|
+
- spec/no_profile_instance_spec.rb
|
82
|
+
- spec/profile_instance_spec.rb
|
83
|
+
- spec/profile_spec.rb
|
84
|
+
- spec/spec_helper.rb
|