railsprof 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dd676c3941cfebbd315805ed26916b228d3851e2
4
+ data.tar.gz: 0e3a3adfd11b13be3370e992d4b3f97e6713527a
5
+ SHA512:
6
+ metadata.gz: 94968394aacd9891748bc669235f6fea34760e08aef2fe7a56b02e3ed2f2c02a5b3085d95b13342c809c5af81dfb96bd94cd69d8408c15b7f1f3acd9c8c402ad
7
+ data.tar.gz: c6bb5b06f0b9928194699ef571c7dca8e8a22a8da3fe9b336465deee9e49bc40f3f5ab66749583fc73afcfd02ea75d049fec507022faaee0779871bb2e4b5d6f
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in railsprof.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Clifton King
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # Railsprof
2
+
3
+ Rails CLI utility for profiling via rblineprof with good HTML output.
4
+
5
+ ![image](https://f.cloud.github.com/assets/14217/2202180/a8cd5ab4-98fc-11e3-8c1a-3ca127f26ae2.png)
6
+
7
+ ```
8
+ $ be railsprof -n5 -g hasherdashery -q key=REDACTED /api/v3/communities/123/portals
9
+ info App loading...
10
+ info App loaded in 12.02 secs
11
+ info Warmup #1 completed in 1133.25ms
12
+ info Profiling....
13
+ info Profile #1 completed in 467.55ms
14
+ info Profile #2 completed in 463.84ms
15
+ info Profile #3 completed in 447.02ms
16
+ info Profile #4 completed in 458.12ms
17
+ info Profile #5 completed in 448.42ms
18
+ info All profiles completed in 2287.82ms
19
+
20
+ -- Top files by execution time (total / child / excl / filename) --
21
+ 2230.80ms 2229.39ms 1.39ms app/controllers/api/v3/base_controller.rb
22
+ 2163.61ms 4285.14ms 0.59ms app/controllers/api/v3/communities_controller.rb
23
+ 2162.76ms 4174.57ms 110.10ms lib/api/interaction_responder.rb
24
+ 1988.38ms 1986.27ms 2.11ms lib/api/utilities.rb
25
+ 1986.27ms 3698.37ms 920.62ms hasherdashery-200a6c732704/lib/hasherdashery.rb
26
+ 1917.27ms 3330.16ms 545.64ms hasherdashery-200a6c732704/lib/hasherdashery/tailor.rb
27
+ 1668.67ms 2632.86ms 888.21ms hasherdashery-200a6c732704/lib/hasherdashery/property.rb
28
+ 1629.22ms 2307.68ms 1155.85ms hasherdashery-200a6c732704/lib/hasherdashery/value.rb
29
+ 1461.89ms 1849.78ms 1440.31ms hasherdashery-200a6c732704/lib/hasherdashery/data_type/base.rb
30
+ 1416.28ms 1838.26ms 301.43ms hasherdashery-200a6c732704/lib/hasherdashery/data_type/pattern.rb
31
+ 594.30ms 492.44ms 101.86ms hasherdashery-200a6c732704/lib/hasherdashery/dsl/pattern_maker.rb
32
+ 457.08ms 406.78ms 50.31ms lib/patterns/api/v3/common_patterns.rb
33
+ 424.95ms 530.18ms 402.26ms hasherdashery-200a6c732704/lib/hasherdashery/dsl/common_methods.rb
34
+ 311.47ms 0.00ms 311.47ms lib/api/router.rb
35
+ 274.65ms 329.48ms 55.15ms lib/patterns/api/v3/portal_patterns.rb
36
+ 247.06ms 121.02ms 126.04ms hasherdashery-200a6c732704/lib/hasherdashery/dsl/simple_type_methods.rb
37
+ 229.35ms 142.72ms 86.63ms hasherdashery-200a6c732704/lib/hasherdashery/pattern_rack.rb
38
+ 176.25ms 182.78ms 100.57ms hasherdashery-200a6c732704/lib/hasherdashery/dsl/pin_method.rb
39
+ 161.32ms 20.78ms 140.54ms hasherdashery-200a6c732704/lib/hasherdashery/type_label.rb
40
+ 127.24ms 4.97ms 122.27ms hasherdashery-200a6c732704/lib/hasherdashery/type_universe.rb
41
+ 66.29ms 0.00ms 66.29ms hasherdashery-200a6c732704/lib/hasherdashery/label.rb
42
+ ```
43
+
44
+ ## Installation
45
+
46
+ Add this line to your application's Gemfile:
47
+
48
+ gem 'railsprof'
49
+
50
+ And then execute:
51
+
52
+ $ bundle
53
+
54
+ Or install it yourself as:
55
+
56
+ $ gem install railsprof
57
+
58
+ ## Usage
59
+
60
+ `bundle exec railsprof` from your Rails root.
61
+
62
+ ```
63
+ $ be railsprof
64
+ Usage: railsprof [options] [method] /path
65
+ -h, --help Show this message
66
+ --version Show version
67
+ -v, --verbose Run verbosely
68
+ -e, --environment ENV Environment (defaults to RAILS_ENV)
69
+ -q, --query-param KEY=VAL Add query paramter (-q key=val {key: "val"}
70
+ -w, --warmups N Number of warmup runs on stack, default 1
71
+ -n, --num-runs N Number of runs in profiling mode, default 1
72
+ -t, --threshold N Threshold for file output in millis, default: 0.5
73
+ -d, --directory DIR Local paths to profile, default: app, lib, config
74
+ -g, --gem GEM Gems to profile, default:
75
+ ```
76
+
77
+ ## Contributing
78
+
79
+ 1. Fork it ( http://github.com/orgsync/railsprof/fork )
80
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
81
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
82
+ 4. Push to the branch (`git push origin my-new-feature`)
83
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/railsprof ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'railsprof'
3
+ require 'railsprof/cli'
4
+
5
+ Railsprof::CLI.start
@@ -0,0 +1,137 @@
1
+ require 'optparse'
2
+
3
+ class Railsprof::CLI
4
+ def self.start
5
+ logger = Railsprof::Logger.new(STDOUT)
6
+ logger.level = Logger::INFO
7
+ options = {}
8
+
9
+ opt_parser = OptionParser.new do |opts|
10
+ opts.banner = 'Usage: railsprof [options] /path'
11
+
12
+ opts.on('-h', '--help', 'Show this message') do
13
+ puts opts.help
14
+ exit
15
+ end
16
+
17
+ opts.on('--version', 'Show version') do
18
+ puts "Version #{Railsprof::VERSION}"
19
+ exit
20
+ end
21
+
22
+ opts.on('-v', '--verbose',
23
+ 'Run verbosely') do
24
+ logger.level = options[:log_level] = Logger::DEBUG
25
+ end
26
+
27
+ opts.on('-e', '--environment ENV',
28
+ 'Environment (defaults to RAILS_ENV)') do |env|
29
+ ENV['RAILS_ENV'] = env
30
+ end
31
+
32
+ opts.on('-q', '--query-param KEY=VAL',
33
+ 'Add query paramter (-q key=val {key: "val"}') do |q|
34
+ options[:params] ||= {}
35
+ key, val = q.split('=', 2)
36
+ options[:params][key] = val
37
+ end
38
+
39
+ # TODO add session support
40
+ # opts.on('-s', '--session KEY=VAL',
41
+ # 'Session info (-s user=3 --> {user: 3})') do |s|
42
+ # options[:session] ||= {}
43
+ # key, val = s.split('=', 2)
44
+
45
+ # # parse session value in ruby if possible
46
+ # options[:session][key.to_sym] =
47
+ # begin
48
+ # eval val
49
+ # rescue NameError
50
+ # val
51
+ # end
52
+
53
+ # logger.debug "Added to session: {#{key.to_sym.inspect} => #{val.inspect}}"
54
+ # end
55
+
56
+ # TODO add cookie support
57
+ # opts.on('-c', '--cookie KEY=VAL',
58
+ # 'Add cookie (-c remember=all --> {:remember => "all"})') do |s|
59
+ # options[:cookies] ||= {}
60
+ # key, val = s.split('=', 2)
61
+
62
+ # options[:cookies][key.to_sym] = val
63
+
64
+ # logger.debug "Added to cookiejar: {#{key.to_sym.inspect} => #{val.inspect}}"
65
+ # end
66
+
67
+ # TODO add host support
68
+ # opts.on('--host HOST',
69
+ # 'Host for request (--host www.blah.com)') do |h|
70
+ # options[:host] = h
71
+ # end
72
+
73
+ # TODO add port support
74
+ # opts.on('--port PORT',
75
+ # Integer,
76
+ # 'Port for request (--port 3000)') do |p|
77
+ # options[:port] = p
78
+ # end
79
+
80
+ opts.on('-w', '--warmups N', Integer,
81
+ 'Number of warmup runs on stack, default ' +
82
+ Railsprof::DEFAULT_OPTIONS[:warmups].to_s) do |w|
83
+ options[:warmups] = w
84
+ end
85
+
86
+ opts.on('-n', '--num-runs N', Integer,
87
+ 'Number of runs in profiling mode, default ' +
88
+ Railsprof::DEFAULT_OPTIONS[:runs].to_s) do |r|
89
+ options[:runs] = r
90
+ end
91
+
92
+ opts.on('-t', '--threshold N', Float,
93
+ 'Threshold for file output in millis, default: ' +
94
+ Railsprof::DEFAULT_OPTIONS[:threshold].to_s) do |t|
95
+ options[:threshold] = t
96
+ end
97
+
98
+ opts.on('-d', '--directory DIR',
99
+ 'Local paths to profile, default: ' +
100
+ Railsprof::DEFAULT_OPTIONS[:app_paths].join(', ')) do |d|
101
+ options[:app_dirs] ||= []
102
+ options[:app_dirs] << d
103
+ end
104
+
105
+ opts.on('-g', '--gem GEM',
106
+ 'Gems to profile, default: ' +
107
+ Railsprof::DEFAULT_OPTIONS[:gems].join(', ')) do |d|
108
+ options[:gems] ||= []
109
+ options[:gems] << d
110
+ end
111
+
112
+ # for capturing args before '--'
113
+ # opts.on do |h|
114
+ # puts "head: #{h.inspect}"
115
+ # end
116
+
117
+ # # for capturing args after '--'
118
+ # opts.on_tail do |t|
119
+ # puts "tail: #{t.inspect}"
120
+ # end
121
+
122
+ opts.on_tail("-h", "--help", "Show this message") do
123
+ puts opts
124
+ exit
125
+ end
126
+ end
127
+
128
+ args = opt_parser.parse!
129
+
130
+ if args.empty?
131
+ puts opt_parser.help
132
+ exit
133
+ end
134
+
135
+ Railsprof::Profiler.new(args, options).profile!
136
+ end
137
+ end
@@ -0,0 +1,78 @@
1
+ require 'erb'
2
+
3
+ class Railsprof::LineprofParser
4
+ # rblineprof formatting
5
+ # {
6
+ # "/path/to/file" => [
7
+ # # File stats (Line 0)
8
+ # [total_time, child_time, excl_time, total_cpu, child_cpu, excl_cpu],
9
+ # # Line 1 stats
10
+ # [wall, cpu, calls, allocations]
11
+ # # Line 2 stats
12
+ # [wall, cpu, calls, allocations]
13
+ # ...
14
+ # ]
15
+ # }
16
+
17
+ def initialize(profile, paths, threshold_ms: 0.5)
18
+ @threshold = threshold_ms * 1000
19
+ @paths = paths
20
+ @profile = profile
21
+ @friendly_paths = {}
22
+ @profiled_source = {}
23
+
24
+ @profiled_files = profile
25
+ .reduce([]) do |files, (file, lines)|
26
+ total = lines[0][0]
27
+ if total > @threshold # && !file[/benchmark|\.rake$/] # time in micros
28
+ @friendly_paths[file] = @paths.relative_path_for(file)
29
+ files << [file, *lines[0]]
30
+ end
31
+ files
32
+ end
33
+ .sort_by { |f| -f[1] }
34
+ end
35
+
36
+ def cli_report
37
+ puts "\n-- Top files by execution time (total / child / excl / filename) --\n"
38
+
39
+ @profiled_files.each do |file, total, child, excl|
40
+ printf "%9.2fms %9.2fms %9.2fms %s\n" %
41
+ [total / 1000.0, child / 1000.0, excl / 1000.0, @friendly_paths[file]]
42
+ end
43
+ end
44
+
45
+ def html_report
46
+ filename = [
47
+ '/tmp/railsprof',
48
+ File.basename(Dir.pwd),
49
+ Time.now.to_s(:number),
50
+ ].join('-') + '.html'
51
+
52
+ @profiled_files.each { |f, _| file_output(f) }
53
+
54
+ b = binding
55
+ template = File.read(File.dirname(__FILE__) +
56
+ '/views/railsprof-tmpl.html.erb')
57
+ ERB.new(template, 0, "", "@html_output").result(b)
58
+ File.open(filename, 'w') { |f| f.write(@html_output) }
59
+
60
+ `open #{filename}`
61
+ end
62
+
63
+ private
64
+
65
+ def file_output(file)
66
+ @profiled_source[file] = "\n % 8s + % 8s (called)\n" % %w(cpu idle)
67
+ File.readlines(file).each_with_index do |line, num|
68
+ wall, cpu, calls, _allocations = @profile[file][num + 1]
69
+ @profiled_source[file] <<
70
+ if calls && calls > 0
71
+ '% 8.1fms + % 8.1fms % 8s | %s' %
72
+ [cpu / 1000.0, (wall - cpu) / 1000.0, "(#{calls})", line]
73
+ else
74
+ ' | %s' % line
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,22 @@
1
+ require 'logger'
2
+
3
+ class Railsprof::Logger < ::Logger
4
+ SEVERITY_TO_COLOR_MAP = Hash[*%w(
5
+ DEBUG 0;37
6
+ INFO 32
7
+ WARN 33
8
+ FATAL 31
9
+ UKNOWN 37
10
+ )]
11
+
12
+ def initialize(*_)
13
+ super
14
+ self.formatter = ->(severity, datetime, progname, msg) {
15
+ [
16
+ "\033[#{SEVERITY_TO_COLOR_MAP[severity]}m",
17
+ '%-6s' % "#{severity.downcase}",
18
+ "\033[0m #{msg.strip}\n"
19
+ ].join
20
+ }
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ class Railsprof::Paths
2
+ def initialize(gems: [], app_paths: [])
3
+ @pwd = Pathname.new(Dir.pwd)
4
+ @gem_roots = Set.new
5
+ @paths =
6
+ app_paths.map { |d| [Dir.pwd, d].join('/') } +
7
+ gems.map { |g|
8
+ gem_dir = Gem::Specification.find_by_name(g).gem_dir
9
+ @gem_roots << Pathname.new(gem_dir).parent
10
+ gem_dir
11
+ }
12
+ end
13
+
14
+ def regexp
15
+ Regexp.new(
16
+ @paths.map { |p| Regexp.escape(p) }.join('|')
17
+ )
18
+ end
19
+
20
+ def relative_path_for(file)
21
+ path = Pathname.new(file)
22
+ if file[@pwd.to_s]
23
+ path.relative_path_from(@pwd)
24
+ elsif gem_root = @gem_roots.detect { |r| file[r.to_s] }
25
+ path.relative_path_from(gem_root)
26
+ else
27
+ file
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,230 @@
1
+ require 'active_support'
2
+ require 'active_support/benchmarkable'
3
+ require 'active_support/core_ext/hash'
4
+ require 'benchmark'
5
+ require 'rblineprof'
6
+ require 'railsprof/paths'
7
+ require 'railsprof/lineprof_parser'
8
+
9
+ class Railsprof::Profiler
10
+ attr_reader :logger, :options
11
+ attr_accessor :gem_roots
12
+
13
+ def initialize(args, opts = {})
14
+ @options = Railsprof::DEFAULT_OPTIONS.merge(opts)
15
+
16
+ @logger = options[:logger] || Railsprof::Logger.new(STDOUT)
17
+ @logger.level = options[:log_level] || Logger::INFO
18
+
19
+ @paths = Railsprof::Paths.new(options.slice(:gems, :app_paths))
20
+
21
+ args.unshift('GET') if args.size == 1
22
+
23
+ if args.size == 2
24
+ args.first.upcase!
25
+ @method, @path = args
26
+ else
27
+ fail ArgumentError "excepted METHOD PATH, received: #{args.join(' ')}"
28
+ end
29
+
30
+ logger.debug "Request: #@method #@path"
31
+ logger.debug "Options: #{options.inspect}"
32
+ end
33
+
34
+ def profile!
35
+ load_app!
36
+ options[:warmups].times do |i|
37
+ say_with_time("Warmup ##{i + 1}") { run }
38
+ end
39
+ if options[:runs] > 0
40
+ logger.info 'Profiling....'
41
+
42
+ say_with_time('All profiles') do
43
+ @profile = lineprof(@paths.regexp) do
44
+ options[:runs].times do |i|
45
+ say_with_time("Profile ##{i + 1}") { run }
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ parser = Railsprof::LineprofParser.new(
52
+ @profile, @paths,
53
+ threshold_ms: options[:threshold]
54
+ )
55
+
56
+ parser.cli_report
57
+ parser.html_report
58
+ end
59
+
60
+ def run
61
+ load_app!
62
+
63
+ begin
64
+ ret = Rails.application.routes.call(mock_request)
65
+ rescue Exception => e
66
+ if logger.level == Logger::DEBUG
67
+ raise e
68
+ else
69
+ logger.info "#{e.class.name} raised: #{e.message}"
70
+ logger.info "run railsprof with -v to see stacktrace"
71
+ exit 1
72
+ end
73
+ end
74
+
75
+ status, _ = ret
76
+ logger.warn "Status code #{status} received" if status != 200
77
+
78
+ ret
79
+ end
80
+
81
+ private
82
+
83
+ def mock_request
84
+ Rack::MockRequest.env_for(
85
+ @path,
86
+ method: @method,
87
+ params: options[:params]
88
+ # input: form body data
89
+ )
90
+ .merge({
91
+ # 'rack.session' => options[:session]
92
+ })
93
+ end
94
+
95
+ def load_app!
96
+ return if defined? @loaded
97
+
98
+ logger.info 'App loading...'
99
+ env_file = Dir.pwd + '/config/environment.rb'
100
+ if File.exists?(env_file)
101
+ ms = Benchmark.ms { load env_file }
102
+ logger.info 'App loaded in %.2f secs' % (ms / 1000.0)
103
+ else
104
+ puts 'Exiting... an application with config/environment.rb was expected'
105
+ exit 1
106
+ end
107
+ @loaded = true
108
+ end
109
+
110
+ def say_with_time(msg, level: 'info', &block)
111
+ ret = nil
112
+ ms = Benchmark.ms { ret = block.call }
113
+ logger.send(level, '%s completed in %.2fms' % [msg, ms])
114
+ ret
115
+ end
116
+ end
117
+
118
+ =begin
119
+ namespace :api do
120
+ GEMS = %w[hasherdashery active_interaction]
121
+ APP_DIRS = %w[lib app config gems]
122
+
123
+ desc 'profile an API endpoint, return rblineprof data'
124
+
125
+ task :profile, [:route] => :environment do |t, args|
126
+ route = args[:route]
127
+ status, headers, body = nil
128
+ gem_roots = Set.new
129
+
130
+ paths =
131
+ APP_DIRS.map { |d| Rails.root.join(d).to_s } +
132
+ GEMS.map { |g|
133
+ gem_dir = Gem::Specification.find_by_name(g).gem_dir
134
+ gem_roots << Pathname.new(gem_dir).parent
135
+ gem_dir
136
+ }
137
+
138
+ path_pattern = Regexp.new paths.map { |p| Regexp.escape(p) }.join('|')
139
+
140
+ print "Warming up request... "
141
+ warmup_ms = Benchmark.ms { status, headers, body = internal_request(route) }
142
+ printf "finished in %dms with status %s\n" % [warmup_ms, status]
143
+
144
+
145
+ profile = lineprof(path_pattern) do
146
+ status, headers, body = nil
147
+ print "Profiling request... "
148
+ profiled_ms = Benchmark.ms { status, headers, body = internal_request(route) }
149
+ printf "finished in %dms with status %s\n" % [profiled_ms, status]
150
+ end
151
+
152
+ # rblineprof formatting
153
+ # {
154
+ # "/path/to/file" => [
155
+ # # File stats
156
+ # [total_time, child_time, exclusive_time, allocations],
157
+ # # Line 1 stats
158
+ # [wall, cpu, calls, allocations]
159
+ # # Line 2 stats
160
+ # [wall, cpu, calls, allocations]
161
+ # ...
162
+ # ]
163
+ # }
164
+
165
+ friendly_paths = {}
166
+ profiled_source = {}
167
+
168
+ profiled_files = profile
169
+ .inject([]) do |files, (file, ((total, _, exclusive, allocs), _))|
170
+ if total > 500 && !file[/benchmark|\.rake$/] # time in micros
171
+ files << [file, total, exclusive, allocs]
172
+
173
+ path = Pathname.new(file)
174
+ friendly_paths[file] =
175
+ if file[Rails.root.to_s]
176
+ path.relative_path_from(Rails.root)
177
+ elsif gem_root = gem_roots.detect { |r| file[r.to_s] }
178
+ path.relative_path_from(gem_root)
179
+ else
180
+ file
181
+ end
182
+
183
+ profiled_source[file] = "\n"
184
+ File.readlines(file).each_with_index do |line, num|
185
+ wall, cpu, calls, allocations = profile[file][num + 1]
186
+ profiled_source[file] <<
187
+ if calls && calls > 0
188
+ '% 8.1fms + % 8.1fms (% 5d) | %s' %
189
+ [cpu / 1000.0, (wall - cpu) / 1000.0, calls, line]
190
+ else
191
+ ' | %s' % line
192
+ end
193
+ end
194
+ end
195
+ files
196
+ end
197
+ .sort_by { |f| -f[1] }
198
+
199
+ puts "\n-- Top files by execution time (ms / filename) --\n"
200
+
201
+ profiled_files.each do |file, total, exclusive, allocs|
202
+ printf "%8.3fms total %8.3fms excl %9d allocs %s \n" %
203
+ [total / 1000.0, exclusive / 1000.0, allocs, friendly_paths[file]]
204
+ end
205
+
206
+ filename = [
207
+ '/tmp/railsprof',
208
+ Rails.root.basename,
209
+ Time.now.to_s(:number),
210
+ ].join('-') + '.html'
211
+
212
+ b = binding
213
+ template = File.read('railsprof-tmpl.html.erb')
214
+ ERB.new(template, 0, "", "@html_output").result(b)
215
+
216
+ File.open(filename, 'w') { |f| f.write(@html_output) }
217
+
218
+ `open #{filename}`
219
+ end
220
+
221
+ def internal_request(path, params={})
222
+ request_env = Rack::MockRequest.env_for(path, params: params.to_query).merge({
223
+ # 'rack.session' => session
224
+ })
225
+
226
+ Rails.application.routes.call(request_env)
227
+ end
228
+ end
229
+ =end
230
+
@@ -0,0 +1,3 @@
1
+ module Railsprof
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,61 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= Rails.root.basename %> railsprof run</title>
5
+ <link href="http://clifton.is/css/normalize.css" rel="stylesheet" type="text/css">
6
+ <link href="http://clifton.is/css/highlight.github.css" rel="stylesheet" type="text/css">
7
+ <style>
8
+ pre {
9
+ overflow: auto;
10
+ word-wrap: normal;
11
+ white-space: pre;
12
+ }
13
+ </style>
14
+ </head>
15
+ <body>
16
+ <table>
17
+ <thead>
18
+ <tr>
19
+ <td width='120px'>Wall Total</td>
20
+ <td width='110px'>Wall Child</td>
21
+ <td width='110px'>Wall Excl</td>
22
+ <td width='90px'>CPU Total</td>
23
+ <td width='90px'>CPU Child</td>
24
+ <td width='90px'>CPU Excl</td>
25
+ <td>File</td>
26
+ </tr>
27
+ </thead>
28
+ <tbody>
29
+ <% @profiled_files.each do |file, *times| %>
30
+ <tr>
31
+ <% times.each do |t| %>
32
+ <td><%= t / 1000.0 %></td>
33
+ <% end %>
34
+ <td>
35
+ <a href='#' class='js-toggle-code' data-file='<%= file %>'>
36
+ <%= @friendly_paths[file] %>
37
+ </a>
38
+ </td>
39
+ </tr>
40
+ <tr style='display:none'>
41
+ <td colspan='7' style='overflow: auto'>
42
+ <br style='clear:both'>
43
+ <pre>
44
+ <code class="ruby"><%= @profiled_source[file] %></code>
45
+ </pre>
46
+ </td>
47
+ </tr>
48
+ <% end %>
49
+ </tbody>
50
+ </table>
51
+ <script src='http://code.jquery.com/jquery-1.11.0.min.js'></script>
52
+ <script src='http://clifton.is/js/highlight.js'></script>
53
+ <script>
54
+ hljs.initHighlightingOnLoad();
55
+ $('.js-toggle-code').on('click', function () {
56
+ $(this).parents('tr').next().toggle();
57
+ return false;
58
+ });
59
+ </script>
60
+ </body>
61
+ </html>
data/lib/railsprof.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'railsprof/version'
2
+ require 'railsprof/logger'
3
+ require 'railsprof/profiler'
4
+
5
+ module Railsprof
6
+ DEFAULT_OPTIONS = {
7
+ session: {},
8
+ cookies: {},
9
+ params: {},
10
+ warmups: 1,
11
+ runs: 1,
12
+ log_level: Logger::INFO,
13
+ threshold: 0.5,
14
+ app_paths: %w(app lib config),
15
+ gems: []
16
+ }
17
+ end
data/railsprof.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'railsprof/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'railsprof'
8
+ spec.version = Railsprof::VERSION
9
+ spec.authors = ['Clifton King']
10
+ spec.email = ['cliftonk@gmail.com']
11
+ spec.summary = 'command line rbline prof for rails apps'
12
+ # spec.description = %q{TODO: Write a longer description. Optional.}
13
+ spec.homepage = 'http://orgsync.github.io'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split("\n")
17
+ spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ spec.test_files = `git ls-files -- spec/*`.split("\n").map{ |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '> 1.3'
22
+ # spec.add_development_dependency 'rspec', '~> 1.5'
23
+ spec.add_runtime_dependency 'rake', '~> 10.1.1'
24
+ spec.add_runtime_dependency 'rails', '> 3.0'
25
+ spec.add_runtime_dependency 'activesupport', '> 3.0'
26
+ spec.add_runtime_dependency 'rblineprof', '~> 0.3.6'
27
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: railsprof
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Clifton King
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>'
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>'
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 10.1.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 10.1.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>'
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>'
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>'
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>'
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rblineprof
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 0.3.6
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 0.3.6
83
+ description:
84
+ email:
85
+ - cliftonk@gmail.com
86
+ executables:
87
+ - railsprof
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - bin/railsprof
97
+ - lib/railsprof.rb
98
+ - lib/railsprof/cli.rb
99
+ - lib/railsprof/lineprof_parser.rb
100
+ - lib/railsprof/logger.rb
101
+ - lib/railsprof/paths.rb
102
+ - lib/railsprof/profiler.rb
103
+ - lib/railsprof/version.rb
104
+ - lib/railsprof/views/railsprof-tmpl.html.erb
105
+ - railsprof.gemspec
106
+ homepage: http://orgsync.github.io
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.1.11
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: command line rbline prof for rails apps
130
+ test_files: []