active-profiling 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +124 -0
- data/Rakefile +39 -0
- data/active-profiling.gemspec +27 -0
- data/lib/active-profiling.rb +26 -0
- data/lib/active-profiling/action_controller/action_profiling.rb +37 -0
- data/lib/active-profiling/gc_statistics.rb +105 -0
- data/lib/active-profiling/log_subscriber.rb +58 -0
- data/lib/active-profiling/railtie.rb +98 -0
- data/lib/active-profiling/ruby_profiler.rb +103 -0
- data/lib/active-profiling/version.rb +4 -0
- metadata +106 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 J Smith <dark.panda@gmail.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
|
2
|
+
= ActiveProfiling
|
3
|
+
|
4
|
+
ActiveProfiling is a gem I've slapped together over the years to help with
|
5
|
+
profiling Rails applications. It is mostly aimed at profiling actions but
|
6
|
+
can also be used to profile generic blocks of code.
|
7
|
+
|
8
|
+
== Profiler ActionController Filters
|
9
|
+
|
10
|
+
=== +action_profiler+
|
11
|
+
|
12
|
+
This filter wraps up the functionality of RubyProf for each action and can be
|
13
|
+
used to spit out the results to +stdout+, your log file or to files in the
|
14
|
+
+Rails.root/log/profiling+ directory.
|
15
|
+
|
16
|
+
Options and their defaults for +action_profiler+ are listed in
|
17
|
+
ActiveProfiling::Railtie::DEFAULT_PROFILER_OPTIONS, but the gist of it is that
|
18
|
+
you can basically use any of RubyProf's various options and control where the
|
19
|
+
output is directed. For some the +:call_tree+ output type, results are written
|
20
|
+
to individual files in +Rails.root/log/profiling+, while for all other output
|
21
|
+
types the results are written to the Rails log. You can of course redirect
|
22
|
+
all logging to files if you prefer, or to the standard output.
|
23
|
+
|
24
|
+
To enable this filter, add the following to your application configuration:
|
25
|
+
|
26
|
+
config.active_profiling.profiler.enabled = true
|
27
|
+
|
28
|
+
=== +action_gc_statistics+
|
29
|
+
|
30
|
+
This filter wraps up the functionality of either GC::Profiler in Ruby 1.9+ or
|
31
|
+
the GC statistics patches available for 1.8.7 if installed, either through
|
32
|
+
Ruby Enterprise Edition or by patching your own Ruby. When enabled, information
|
33
|
+
on the Ruby garbage collector will be written to the Rails log at the end of
|
34
|
+
each action response. Options for controlling how the information is collected
|
35
|
+
and logged can be found in ActiveProfiling::Railtie::DEFAULT_GC_STATISTICS_OPTIONS.
|
36
|
+
|
37
|
+
Note that the output for Ruby 1.9+ is a bit different from REE or a patched
|
38
|
+
Ruby due to the manner in which they collect and report on GC statistics. In
|
39
|
+
the REE patches you can access a number of options that aren't currently
|
40
|
+
available in GC::Profiler. I've tried to make some sensible output for REE,
|
41
|
+
but there are going to be differences based solely on the manner in which the
|
42
|
+
statistics are collected.
|
43
|
+
|
44
|
+
To enable this filter, add the following to your application configuration:
|
45
|
+
|
46
|
+
config.active_profiling.gc_statistics.enabled = true
|
47
|
+
|
48
|
+
== Profiling Blocks
|
49
|
+
|
50
|
+
You can also profile individual blocks of code by using the
|
51
|
+
ActiveProfiling.ruby_profiler and ActiveProfiling.gc_statistics methods or
|
52
|
+
by including the ActiveProfiling::RubyProfiler or ActiveProfiling::GCStatistics
|
53
|
+
modules and using the methods in your own classes and modules. In both cases,
|
54
|
+
these methods take the same options as found in the ActionController filters
|
55
|
+
minus the +:enabled+ option.
|
56
|
+
|
57
|
+
# Will spit out the GC statistics if GC is run, same as with the action
|
58
|
+
# filter.
|
59
|
+
ActiveProfiling.gc_statistics do
|
60
|
+
# ...
|
61
|
+
end
|
62
|
+
|
63
|
+
# Wraps the block in a RubyProf session and logs the results accordingly.
|
64
|
+
ActiveProfiling.ruby_profiler(options) do
|
65
|
+
# ...
|
66
|
+
end
|
67
|
+
|
68
|
+
== Using In A Rails Project
|
69
|
+
|
70
|
+
Profiling should be performed in a production environment, as profiling in
|
71
|
+
development really isn't reflective of how your application is actually going
|
72
|
+
to be running in the Real World. At the same time, you don't necessarily want
|
73
|
+
to clutter up your application settings and +Gemfile+ with unnecessary code
|
74
|
+
and have to worry about accidentally committing profiling settings to your
|
75
|
+
source code repository and the like.
|
76
|
+
|
77
|
+
A semi-neat trick to try is to set up a file for local application settings
|
78
|
+
thusly:
|
79
|
+
|
80
|
+
First, add the following to your +application.rb+. You generally want this near
|
81
|
+
the end of the file.
|
82
|
+
|
83
|
+
if File.exists?("#{Rails.root}/config/application_local.rb")
|
84
|
+
require "#{Rails.root}/config/application_local.rb"
|
85
|
+
end
|
86
|
+
|
87
|
+
Add an entry for +application_local.rb+ to your +.gitignore+ file or otherwise
|
88
|
+
ignore it with your SCM.
|
89
|
+
|
90
|
+
In +application_local.rb+, you can set up additional application settings
|
91
|
+
like so:
|
92
|
+
|
93
|
+
Bundler.require(:profiling, Rails.env) if defined?(Bundler)
|
94
|
+
|
95
|
+
module FooBarApp
|
96
|
+
class Application < Rails::Application
|
97
|
+
# config.log_level = :debug
|
98
|
+
config.active_profiling.profiler.enabled = false
|
99
|
+
config.active_profiling.profiler.output = :file
|
100
|
+
config.active_profiling.profiler.printer = :graph_html
|
101
|
+
config.active_profiling.profiler.printer_options = {
|
102
|
+
:min_percent => 1,
|
103
|
+
:print_file => true
|
104
|
+
}
|
105
|
+
config.active_profiling.gc_statistics.enabled = true
|
106
|
+
|
107
|
+
# etc...
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
This allows you to tweak some settings without affecting your main
|
112
|
+
+application.rb+ file.
|
113
|
+
|
114
|
+
You'll also want to add the following to your +Gemfile+:
|
115
|
+
|
116
|
+
group :profiling do
|
117
|
+
gem 'active-profiling'
|
118
|
+
gem 'ruby-prof'
|
119
|
+
end
|
120
|
+
|
121
|
+
== License
|
122
|
+
|
123
|
+
This gem is licensed under an MIT-style license. See the +MIT-LICENSE+ file for
|
124
|
+
details.
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
|
6
|
+
gem 'rdoc', '~> 3.12'
|
7
|
+
|
8
|
+
require 'rubygems/package_task'
|
9
|
+
require 'rake/testtask'
|
10
|
+
require 'rdoc/task'
|
11
|
+
require 'bundler/gem_tasks'
|
12
|
+
|
13
|
+
if RUBY_VERSION >= '1.9'
|
14
|
+
begin
|
15
|
+
gem 'psych'
|
16
|
+
rescue Exception => e
|
17
|
+
# it's okay, fall back on the bundled psych
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
$:.push 'lib'
|
22
|
+
|
23
|
+
version = ActiveProfiling::VERSION
|
24
|
+
|
25
|
+
desc 'Test ActiveProfiling interface'
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.test_files = FileList['test/**/*_tests.rb']
|
28
|
+
t.verbose = !!ENV['VERBOSE_TESTS']
|
29
|
+
t.warning = !!ENV['WARNINGS']
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'Build docs'
|
33
|
+
Rake::RDocTask.new do |t|
|
34
|
+
t.title = "ActiveProfiling #{version}"
|
35
|
+
t.main = 'README.rdoc'
|
36
|
+
t.rdoc_dir = 'doc'
|
37
|
+
t.rdoc_files.include('README.rdoc', 'MIT-LICENSE', 'lib/**/*.rb')
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path('../lib/active-profiling/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "active-profiling"
|
7
|
+
s.version = ActiveProfiling::VERSION
|
8
|
+
|
9
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
10
|
+
s.authors = ["J Smith"]
|
11
|
+
s.description = "A Rails profiling suite."
|
12
|
+
s.summary = s.description
|
13
|
+
s.email = "dark.panda@gmail.com"
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"README.rdoc"
|
16
|
+
]
|
17
|
+
s.files = `git ls-files`.split($\)
|
18
|
+
s.executables = s.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
19
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
20
|
+
s.homepage = "http://github.com/dark-panda/active-profiling"
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
s.add_dependency("rails", [">= 3.0"])
|
24
|
+
s.add_dependency("rdoc")
|
25
|
+
s.add_dependency("rake", ["~> 0.9"])
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
module ActiveProfiling
|
3
|
+
class << self
|
4
|
+
def gc_statistics?
|
5
|
+
if defined?(@has_gc_statistics)
|
6
|
+
@has_gc_statistics
|
7
|
+
else
|
8
|
+
@has_gc_statistics = defined?(GC::Profiler) || GC.respond_to?(:enable_stats)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def ruby_prof?
|
13
|
+
if defined?(@has_ruby_prof)
|
14
|
+
@has_ruby_prof
|
15
|
+
else
|
16
|
+
@has_ruby_prof = defined?(RubyProf)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'active-profiling/railtie'
|
23
|
+
require 'active-profiling/log_subscriber'
|
24
|
+
require 'active-profiling/gc_statistics'
|
25
|
+
require 'active-profiling/ruby_profiler'
|
26
|
+
require 'active-profiling/action_controller/action_profiling'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
require 'digest/md5'
|
3
|
+
|
4
|
+
module ActionController
|
5
|
+
module ActionProfiling
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
include ActiveProfiling::RubyProfiler
|
10
|
+
include ActiveProfiling::GCStatistics
|
11
|
+
|
12
|
+
around_filter :action_profiler, :if => proc {
|
13
|
+
Rails.application.config.active_profiling.profiler.enabled && ActiveProfiling.ruby_prof?
|
14
|
+
}
|
15
|
+
|
16
|
+
around_filter :action_gc_statistics, :if => proc {
|
17
|
+
Rails.application.config.active_profiling.gc_statistics.enabled && ActiveProfiling.gc_statistics?
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def action_profiler(*args)
|
22
|
+
ruby_profiler do
|
23
|
+
yield
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def action_gc_statistics(*args)
|
28
|
+
gc_statistics do
|
29
|
+
yield
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Base
|
35
|
+
include ActionController::ActionProfiling
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
|
2
|
+
module ActiveProfiling
|
3
|
+
module GCStatistics
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
if defined?(GC::Profiler)
|
7
|
+
def gc_statistics_report(options = {})
|
8
|
+
options = {
|
9
|
+
:disable_gc => false
|
10
|
+
}.merge(options)
|
11
|
+
|
12
|
+
GC.disable if options[:disable_gc]
|
13
|
+
GC::Profiler.enable
|
14
|
+
GC::Profiler.clear
|
15
|
+
|
16
|
+
retval = yield
|
17
|
+
|
18
|
+
if options[:disable_gc]
|
19
|
+
GC.enable
|
20
|
+
end
|
21
|
+
|
22
|
+
result = GC::Profiler.result
|
23
|
+
|
24
|
+
return [ retval, result ]
|
25
|
+
|
26
|
+
ensure
|
27
|
+
GC.enable if options[:disable_gc]
|
28
|
+
GC::Profiler.disable
|
29
|
+
GC::Profiler.clear
|
30
|
+
end
|
31
|
+
elsif GC.respond_to?(:enable_stats)
|
32
|
+
def gc_statistics_report(options = {})
|
33
|
+
options = {
|
34
|
+
:disable_gc => false
|
35
|
+
}.merge(options)
|
36
|
+
|
37
|
+
GC.disable if options[:disable_gc]
|
38
|
+
GC.enable_stats
|
39
|
+
GC.clear_stats
|
40
|
+
|
41
|
+
retval = yield
|
42
|
+
|
43
|
+
result = [
|
44
|
+
"Allocated size: #{GC.allocated_size}",
|
45
|
+
"Number of allocations: #{GC.num_allocations}",
|
46
|
+
"Collections: #{GC.collections}",
|
47
|
+
"Time (ms): #{GC.time / 1000.0}"
|
48
|
+
].join("\n")
|
49
|
+
|
50
|
+
return [ retval, result ]
|
51
|
+
|
52
|
+
ensure
|
53
|
+
GC.enable if options[:disable_gc]
|
54
|
+
GC.disable_stats
|
55
|
+
GC.clear_stats
|
56
|
+
end
|
57
|
+
else
|
58
|
+
$stderr.puts "NOTICE: this version of Ruby cannot report on GC statistics."
|
59
|
+
|
60
|
+
def gc_statistics_report(*args)
|
61
|
+
[ yield, nil ]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private :gc_statistics_report
|
66
|
+
|
67
|
+
# Profiles a block, capturing information on the garbage collector. The
|
68
|
+
# return value is an Array containing the result of the yielded block
|
69
|
+
# and a String with a report on the profiling results.
|
70
|
+
#
|
71
|
+
# Options:
|
72
|
+
#
|
73
|
+
# * +:disable_gc+ - disables garbage collection for the duration of the
|
74
|
+
# block and then renables it immediately afterwards. This allows you to
|
75
|
+
# control when GC is run and see the results.
|
76
|
+
# * +:title+ - a title to use for logging.
|
77
|
+
#
|
78
|
+
# More options for this method can be found in the default settings,
|
79
|
+
# located in ActiveProfiling::Railtie::DEFAULT_GC_STATISTICS_OPTIONS.
|
80
|
+
#
|
81
|
+
# This method only works with versions of Ruby that implement
|
82
|
+
# GC::Profiler or that have been patched to implement some additional
|
83
|
+
# garbage collection statistics. In older versions, such as version
|
84
|
+
# 1.8.7, you can either use Ruby Enterprise Edition or patch your build
|
85
|
+
# with the GC statistics patch found here:
|
86
|
+
#
|
87
|
+
# http://blog.pluron.com/2008/02/memory-profilin.html
|
88
|
+
def gc_statistics(*args)
|
89
|
+
options = Rails.application.config.active_profiling.gc_statistics.merge(args.extract_options!)
|
90
|
+
|
91
|
+
result, gc_report = gc_statistics_report(options) do
|
92
|
+
yield
|
93
|
+
end
|
94
|
+
|
95
|
+
ActiveSupport::Notifications.instrument('gc_statistics.active_profiling', {
|
96
|
+
:report => gc_report,
|
97
|
+
:title => options[:title] || args.first
|
98
|
+
})
|
99
|
+
|
100
|
+
result
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
self.extend(GCStatistics)
|
105
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
module ActiveProfiling
|
3
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
4
|
+
def profiler_output(event)
|
5
|
+
return unless logger &&
|
6
|
+
logger.send("#{config.profiler.log_level}?")
|
7
|
+
|
8
|
+
report = self.indent(event.payload[:profiler_output])
|
9
|
+
|
10
|
+
logger.send(
|
11
|
+
config.profiler.log_level,
|
12
|
+
"#{color("Profiler Output", YELLOW, true)}\n#{report}"
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def profiler_output_to_file(event)
|
17
|
+
return unless logger &&
|
18
|
+
logger.send("#{config.profiler.log_level}?")
|
19
|
+
|
20
|
+
logger.send(
|
21
|
+
config.profiler.log_level,
|
22
|
+
color("Wrote profiling information to #{event.payload[:file_name]}", YELLOW, true)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def gc_statistics(event)
|
27
|
+
return unless logger &&
|
28
|
+
logger.send("#{config.gc_statistics.log_level}?")
|
29
|
+
|
30
|
+
unless event.payload[:report].blank?
|
31
|
+
title = event.payload[:title]
|
32
|
+
report = self.indent(event.payload[:report])
|
33
|
+
|
34
|
+
logger.send(
|
35
|
+
config.gc_statistics.log_level,
|
36
|
+
"#{color("GC Statistics: #{title}", YELLOW, true)}\n#{report}"
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def logger
|
42
|
+
::Rails.logger
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
def config
|
47
|
+
Rails.application.config.active_profiling
|
48
|
+
end
|
49
|
+
|
50
|
+
def indent(text)
|
51
|
+
text.split("\n").collect { |line|
|
52
|
+
" #{line}"
|
53
|
+
}.join("\n")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
ActiveProfiling::LogSubscriber.attach_to :active_profiling
|
@@ -0,0 +1,98 @@
|
|
1
|
+
|
2
|
+
module ActiveProfiling
|
3
|
+
class Railtie < Rails::Railtie
|
4
|
+
# These settings are the default values used when running RubyProf on
|
5
|
+
# every request. You can also use the profiler directly by using the
|
6
|
+
# ActiveProfiling.ruby_profiler method.
|
7
|
+
#
|
8
|
+
# * +:enabled+ - Enable profiler on requests.
|
9
|
+
# * +:measure_mode+ - The ruby-prof measure mode. The default is
|
10
|
+
# RubyProf::PROCESS_TIME. Use Symbols for this instead of the actual
|
11
|
+
# RubyProf constants (i.e. +:memory+ instead of RubyProf::MEMORY) so
|
12
|
+
# you can have your configuration options in your environment set up
|
13
|
+
# without having to require ruby-prof.
|
14
|
+
# * +:disable_gc+ - Whether or not we're disabling garbage collection
|
15
|
+
# during profiling. This is useful when you're profiling memory usage,
|
16
|
+
# where you should disable garbage collection as ruby-prof will
|
17
|
+
# otherwise record information on GC on the method that causes GC rather
|
18
|
+
# than the methods that allocate the memory.
|
19
|
+
# * +:printer+ - The RubyProf printer to use. See the RubyProf docs for
|
20
|
+
# details. The default is RubyProf::FlatPrinter. Use Symbols in this
|
21
|
+
# option, i.e. +:flat+ instead of RubyProf::FlatPrinter. (Note that the
|
22
|
+
# FlatPrinterWithLineNumbers printer is called +:flat_with_line_numbers+
|
23
|
+
# here.)
|
24
|
+
# * +:call_tree_prefix+ - The prefix to use for +:call_tree+ files. Some
|
25
|
+
# programs like to look for files named "callgrind.out.*", while others
|
26
|
+
# like to look for "cachegrind.out.*".
|
27
|
+
# * +:output+ - Where to direct the output from the profiler. If set to
|
28
|
+
# +:stdout+, well, then it's sent to $stdout. When set to +:log+, it
|
29
|
+
# shows up in the log file. When set to +:file+, it gets put in
|
30
|
+
# +Rails.root/log/profiling/+ in a sensible manner. The default depends
|
31
|
+
# on the +:printer+ being used:
|
32
|
+
#
|
33
|
+
# * +:file+ - +:graph_html+, +:call_stack+, +:call_tree+, +:dot+
|
34
|
+
# * +:log+ - +:flat+, +:flat_with_line_numbers+, +:graph+ and anything
|
35
|
+
# else
|
36
|
+
# * +:log_level+ - The log level to spit the logging information into
|
37
|
+
# when using +:log+ for +:output+. The default is +:info+.
|
38
|
+
# * +:printer_options+ - Options to pass to the profiler printer. The
|
39
|
+
# default is:
|
40
|
+
#
|
41
|
+
# :printer_options => {
|
42
|
+
# :min_percent => 1,
|
43
|
+
# :print_file => true
|
44
|
+
# }
|
45
|
+
DEFAULT_PROFILER_OPTIONS = {
|
46
|
+
:enabled => false,
|
47
|
+
|
48
|
+
:measure_mode => :process_time,
|
49
|
+
|
50
|
+
:disable_gc => true,
|
51
|
+
|
52
|
+
:printer => :graph,
|
53
|
+
|
54
|
+
:call_tree_prefix => 'callgrind.out.',
|
55
|
+
|
56
|
+
:output => nil,
|
57
|
+
|
58
|
+
:log_level => :info,
|
59
|
+
|
60
|
+
:printer_options => {
|
61
|
+
:min_percent => 1,
|
62
|
+
:print_file => true
|
63
|
+
}
|
64
|
+
}.freeze
|
65
|
+
|
66
|
+
# These settings are the default values used when profiling GC statistics.
|
67
|
+
# When enabled, statistics are gathered for every request. You can also
|
68
|
+
# gather statistics on a per-block basis using ActiveProfiling.gc_statistics
|
69
|
+
# directly.
|
70
|
+
#
|
71
|
+
# * +:enabled+ - For logging GC stats. Output is dumped into the log on
|
72
|
+
# each request.
|
73
|
+
# * +:disable_gc+ - Whether or not we're disabling actual garbage
|
74
|
+
# collection during statistics gathering. If set to true, the garbage
|
75
|
+
# collector will be disabled for the duration of the action and then
|
76
|
+
# re-enabled at the end of the action. This allows you to call GC.start
|
77
|
+
# whenever you need to and see results as you go.
|
78
|
+
# * +:log_level+ - The log level to spit the GC statistics to.
|
79
|
+
DEFAULT_GC_STATISTICS_OPTIONS = {
|
80
|
+
:enabled => false,
|
81
|
+
|
82
|
+
:disable_gc => false,
|
83
|
+
|
84
|
+
:log_level => :info
|
85
|
+
}.freeze
|
86
|
+
|
87
|
+
config.active_profiling = ActiveSupport::OrderedOptions.new
|
88
|
+
config.active_profiling.profiler = ActiveSupport::OrderedOptions.new
|
89
|
+
config.active_profiling.gc_statistics = ActiveSupport::OrderedOptions.new
|
90
|
+
|
91
|
+
initializer "active_profiling.set_profiling_config" do |app|
|
92
|
+
options = app.config.active_profiling
|
93
|
+
|
94
|
+
options.profiler.reverse_merge!(DEFAULT_PROFILER_OPTIONS)
|
95
|
+
options.gc_statistics.reverse_merge!(DEFAULT_GC_STATISTICS_OPTIONS)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
|
2
|
+
module ActiveProfiling
|
3
|
+
module RubyProfiler
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
# Runs a block of Ruby code through ruby-prof and returns the profiler
|
7
|
+
# output. Returns an Array containing the the result of the block yielded
|
8
|
+
# and the profiler data.
|
9
|
+
#
|
10
|
+
# For details on the various options, see the default options located in
|
11
|
+
# ActiveProfiling::Railtie::DEFAULT_PROFILER_OPTIONS.
|
12
|
+
def ruby_profiler(*args)
|
13
|
+
options = Rails.application.config.active_profiling.profiler.merge(args.extract_options!)
|
14
|
+
|
15
|
+
printer_class = case options[:printer]
|
16
|
+
when :flat_with_line_numbers
|
17
|
+
RubyProf::FlatPrinterWithLineNumbers
|
18
|
+
else
|
19
|
+
RubyProf.const_get("#{options[:printer].to_s.camelize}Printer")
|
20
|
+
end
|
21
|
+
|
22
|
+
output = if options[:output]
|
23
|
+
options[:output]
|
24
|
+
else
|
25
|
+
case options[:printer]
|
26
|
+
when :call_tree, :call_stack, :graph_html, :dot
|
27
|
+
:file
|
28
|
+
else
|
29
|
+
:log
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
return yield if output == :log && !ActiveProfiling::LogSubscriber.logger
|
34
|
+
|
35
|
+
RubyProf.measure_mode = RubyProf.const_get(options[:measure_mode].to_s.upcase)
|
36
|
+
GC.disable if options[:disable_gc]
|
37
|
+
|
38
|
+
result = nil
|
39
|
+
profiler_result = RubyProf.profile do
|
40
|
+
result = yield
|
41
|
+
end
|
42
|
+
|
43
|
+
case output
|
44
|
+
when :stdout
|
45
|
+
printer_class.new(profiler_result).print($stdout, options.printer_options)
|
46
|
+
when :log
|
47
|
+
str = StringIO.new
|
48
|
+
printer_class.new(profiler_result).print(str, options.printer_options)
|
49
|
+
str.rewind
|
50
|
+
|
51
|
+
ActiveSupport::Notifications.instrument('profiler_output.active_profiling', {
|
52
|
+
:profiler_output => str.read
|
53
|
+
})
|
54
|
+
when :file
|
55
|
+
path, file_name = if args.first
|
56
|
+
[ File.dirname(args.first), args.first ]
|
57
|
+
elsif options[:file_name]
|
58
|
+
[ File.dirname(options[:file_name]), options[:file_name] ]
|
59
|
+
else
|
60
|
+
time = Time.now.strftime('%Y-%m-%d-%H:%M:%S')
|
61
|
+
hash = Digest::MD5.hexdigest(rand.to_s)[0..6]
|
62
|
+
path = Rails.root.join('log/profiling', self.class.name.underscore)
|
63
|
+
ext = case options[:printer]
|
64
|
+
when :graph_html, :call_stack
|
65
|
+
'html'
|
66
|
+
when :dot
|
67
|
+
'dot'
|
68
|
+
else
|
69
|
+
'log'
|
70
|
+
end
|
71
|
+
|
72
|
+
file_name = [
|
73
|
+
self.action_name,
|
74
|
+
options[:measure_mode],
|
75
|
+
options[:printer],
|
76
|
+
time,
|
77
|
+
hash,
|
78
|
+
ext
|
79
|
+
].join('.')
|
80
|
+
|
81
|
+
if options[:printer] == :call_tree && !options[:call_tree_prefix].blank?
|
82
|
+
file_name = "#{options[:call_tree_prefix]}#{file_name}"
|
83
|
+
end
|
84
|
+
|
85
|
+
[ path.to_s, path.join(file_name) ]
|
86
|
+
end
|
87
|
+
|
88
|
+
ActiveSupport::Notifications.instrument('profiler_output_to_file.active_profiling', {
|
89
|
+
:file_name => file_name
|
90
|
+
})
|
91
|
+
|
92
|
+
FileUtils.mkdir_p(path)
|
93
|
+
printer_class.new(profiler_result).print(File.open(file_name, 'w'), options[:printer_options])
|
94
|
+
end
|
95
|
+
|
96
|
+
result
|
97
|
+
ensure
|
98
|
+
GC.enable if options[:disable_gc]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
self.extend(RubyProfiler)
|
103
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active-profiling
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- J Smith
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rdoc
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.9'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.9'
|
62
|
+
description: A Rails profiling suite.
|
63
|
+
email: dark.panda@gmail.com
|
64
|
+
executables: []
|
65
|
+
extensions: []
|
66
|
+
extra_rdoc_files:
|
67
|
+
- README.rdoc
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- MIT-LICENSE
|
72
|
+
- README.rdoc
|
73
|
+
- Rakefile
|
74
|
+
- active-profiling.gemspec
|
75
|
+
- lib/active-profiling.rb
|
76
|
+
- lib/active-profiling/action_controller/action_profiling.rb
|
77
|
+
- lib/active-profiling/gc_statistics.rb
|
78
|
+
- lib/active-profiling/log_subscriber.rb
|
79
|
+
- lib/active-profiling/railtie.rb
|
80
|
+
- lib/active-profiling/ruby_profiler.rb
|
81
|
+
- lib/active-profiling/version.rb
|
82
|
+
homepage: http://github.com/dark-panda/active-profiling
|
83
|
+
licenses: []
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 1.8.23
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: A Rails profiling suite.
|
106
|
+
test_files: []
|