active-profiling 0.0.1
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 +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: []
|