stackprof 0.2.0 → 0.2.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 +4 -4
- data/README.md +59 -1
- data/bin/stackprof +16 -11
- data/lib/stackprof/middleware.rb +17 -6
- data/lib/stackprof/report.rb +2 -6
- data/stackprof.gemspec +1 -2
- data/test/test_stackprof.rb +4 -3
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba362f7b8c12e89a30cc376280443aef51ce7ac6
|
4
|
+
data.tar.gz: 561f31e9f8b90a728dec5c9d6090f4bd7701228a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85a79f6edbf81e520b90d4f35d615c475297acdfe7b5db4277b93bc1e342ac8cc2aebdd56990bca1e82258020f270d6c28bd2098df27f90f320c005da07e9cae
|
7
|
+
data.tar.gz: 7d17bca5d3f356f63d1eec962e40dd4e50520e4a7b2c602b748f94779a9fb147566cc4439ff3cc0c619a687e4eff6e2da5a997240f1542e09849802a4ee7e09a
|
data/README.md
CHANGED
@@ -5,6 +5,64 @@ a sampling call-stack profiler for ruby 2.1+
|
|
5
5
|
inspired heavily by [gperftools](https://code.google.com/p/gperftools/),
|
6
6
|
and written as a replacement for [perftools.rb](https://github.com/tmm1/perftools.rb)
|
7
7
|
|
8
|
+
### getting started
|
9
|
+
|
10
|
+
in ruby:
|
11
|
+
|
12
|
+
``` ruby
|
13
|
+
profile = StackProf.run(mode: :cpu) do
|
14
|
+
...
|
15
|
+
end
|
16
|
+
File.open('tmp/stackprof-cpu-myapp.dump', 'wb'){ |f| f.write Marshal.dump(profile) }
|
17
|
+
```
|
18
|
+
|
19
|
+
via rack:
|
20
|
+
|
21
|
+
``` ruby
|
22
|
+
use StackProf::Middleware, enabled: true,
|
23
|
+
mode: :cpu,
|
24
|
+
interval: 1000,
|
25
|
+
save_every: 5
|
26
|
+
```
|
27
|
+
|
28
|
+
reporting:
|
29
|
+
|
30
|
+
```
|
31
|
+
$ stackprof tmp/stackprof-cpu-*.dump --text --limit 1
|
32
|
+
==================================
|
33
|
+
Mode: cpu(1000)
|
34
|
+
Samples: 60395 (1.09% miss rate)
|
35
|
+
GC: 2851 (4.72%)
|
36
|
+
==================================
|
37
|
+
TOTAL (pct) SAMPLES (pct) FRAME
|
38
|
+
1660 (2.7%) 1595 (2.6%) String#blank?
|
39
|
+
|
40
|
+
$ stackprof tmp/stackprof-cpu-*.dump --method 'String#blank?'
|
41
|
+
String#blank? (gems/activesupport-2.3.14.github30/lib/active_support/core_ext/object/blank.rb:80)
|
42
|
+
samples: 1595 self (2.6%) / 1660 total (2.7%)
|
43
|
+
callers:
|
44
|
+
373 ( 41.0%) ApplicationHelper#current_user
|
45
|
+
192 ( 21.1%) ApplicationHelper#current_repository
|
46
|
+
callers:
|
47
|
+
803 ( 48.4%) Object#present?
|
48
|
+
code:
|
49
|
+
| 80 | def blank?
|
50
|
+
1225 (2.0%) / 1225 (2.0%) | 81 | self !~ /[^[:space:]]/
|
51
|
+
| 82 | end
|
52
|
+
|
53
|
+
$ stackprof tmp/stackprof-cpu-*.dump --method 'Object#present?'
|
54
|
+
Object#present? (gems/activesupport-2.3.14.github30/lib/active_support/core_ext/object/blank.rb:20)
|
55
|
+
samples: 59 self (0.1%) / 910 total (1.5%)
|
56
|
+
callees (851 total):
|
57
|
+
803 ( 94.4%) String#blank?
|
58
|
+
32 ( 3.8%) Object#blank?
|
59
|
+
16 ( 1.9%) NilClass#blank?
|
60
|
+
code:
|
61
|
+
| 20 | def present?
|
62
|
+
910 (1.5%) / 59 (0.1%) | 21 | !blank?
|
63
|
+
| 22 | end
|
64
|
+
```
|
65
|
+
|
8
66
|
### sampling
|
9
67
|
|
10
68
|
four sampling modes are supported:
|
@@ -68,7 +126,7 @@ the sum of the outbound edge weights is equal to total samples collected on that
|
|
68
126
|
|
69
127
|
### reporting
|
70
128
|
|
71
|
-
|
129
|
+
multiple reporting modes are supported:
|
72
130
|
- text
|
73
131
|
- dotgraph
|
74
132
|
- source annotation
|
data/bin/stackprof
CHANGED
@@ -8,19 +8,22 @@ options = {
|
|
8
8
|
:limit => 30
|
9
9
|
}
|
10
10
|
|
11
|
-
OptionParser.new(ARGV) do |o|
|
12
|
-
o.banner =
|
11
|
+
parser = OptionParser.new(ARGV) do |o|
|
12
|
+
o.banner = "Usage: stackprof [file.dump]+ [--text|--method=NAME|--callgrind|--graphviz]"
|
13
13
|
|
14
|
-
o.on('--text', 'Text
|
15
|
-
o.on('--
|
16
|
-
o.on('--
|
17
|
-
o.on('--file [grep]'){ |f| options[:format] = :file; options[:filter] = f }
|
18
|
-
o.on('--
|
19
|
-
o.on('--
|
14
|
+
o.on('--text', 'Text summary (default)'){ options[:format] = :text }
|
15
|
+
o.on('--method [grep]', 'Zoom into specified method'){ |f| options[:format] = :method; options[:filter] = f }
|
16
|
+
o.on('--files', 'List of files'){ |f| options[:format] = :files }
|
17
|
+
o.on('--file [grep]', 'Show annotated code for specified file'){ |f| options[:format] = :file; options[:filter] = f }
|
18
|
+
o.on('--callgrind', 'Callgrind output (use with kcachegrind, gprof2dot)'){ options[:format] = :callgrind }
|
19
|
+
o.on('--graphviz', 'Graphviz output (use with dot)'){ options[:format] = :graphviz }
|
20
20
|
o.on('--debug'){ options[:format] = :debug }
|
21
|
-
o.on('--
|
22
|
-
o.on('--
|
23
|
-
end
|
21
|
+
o.on('--limit [num]', Integer, 'Limit --text output to N lines'){ |n| options[:limit] = n }
|
22
|
+
o.on('--sort-total', 'Sort --text output on total samples'){ options[:sort] = true }
|
23
|
+
end
|
24
|
+
|
25
|
+
parser.parse!
|
26
|
+
parser.abort(parser.help) if ARGV.empty?
|
24
27
|
|
25
28
|
reports = []
|
26
29
|
while ARGV.size > 0
|
@@ -31,6 +34,8 @@ report = reports.inject(:+)
|
|
31
34
|
case options[:format]
|
32
35
|
when :text
|
33
36
|
report.print_text(options[:sort], options[:limit])
|
37
|
+
when :debug
|
38
|
+
report.print_debug
|
34
39
|
when :callgrind
|
35
40
|
report.print_callgrind
|
36
41
|
when :graphviz
|
data/lib/stackprof/middleware.rb
CHANGED
@@ -2,26 +2,37 @@ require 'fileutils'
|
|
2
2
|
|
3
3
|
module StackProf
|
4
4
|
class Middleware
|
5
|
-
def initialize(app)
|
5
|
+
def initialize(app, options = {})
|
6
6
|
@app = app
|
7
|
-
|
7
|
+
@options = options
|
8
|
+
@num_reqs = options[:save_every] || nil
|
9
|
+
Middleware.mode = options[:mode] || :cpu
|
10
|
+
Middleware.interval = options[:interval] || 1000
|
11
|
+
Middleware.enabled = options[:enabled]
|
12
|
+
at_exit{ Middleware.save? } if options[:save_at_exit]
|
8
13
|
end
|
9
14
|
|
10
15
|
def call(env)
|
11
|
-
StackProf.start(mode:
|
16
|
+
StackProf.start(mode: Middleware.mode, interval: Middleware.interval) if Middleware.enabled?
|
12
17
|
@app.call(env)
|
13
18
|
ensure
|
14
|
-
|
19
|
+
if Middleware.enabled?
|
20
|
+
StackProf.stop
|
21
|
+
if @num_reqs && (@num_reqs-=1) == 0
|
22
|
+
@num_reqs = @options[:save_every]
|
23
|
+
Middleware.save
|
24
|
+
end
|
25
|
+
end
|
15
26
|
end
|
16
27
|
|
17
28
|
class << self
|
18
|
-
attr_accessor :enabled
|
29
|
+
attr_accessor :enabled, :mode, :interval
|
19
30
|
alias enabled? enabled
|
20
31
|
|
21
32
|
def save
|
22
33
|
if results = StackProf.results
|
23
34
|
FileUtils.mkdir_p('tmp')
|
24
|
-
File.open("tmp/stackprof-#{results[:mode]}-#{Process.pid}-#{Time.now.to_i}.dump", '
|
35
|
+
File.open("tmp/stackprof-#{results[:mode]}-#{Process.pid}-#{Time.now.to_i}.dump", 'wb') do |f|
|
25
36
|
f.write Marshal.dump(results)
|
26
37
|
end
|
27
38
|
end
|
data/lib/stackprof/report.rb
CHANGED
@@ -5,10 +5,6 @@ module StackProf
|
|
5
5
|
class Report
|
6
6
|
def initialize(data)
|
7
7
|
@data = data
|
8
|
-
|
9
|
-
frames = {}
|
10
|
-
@data[:frames].each{ |k,v| frames[k.to_s] = v }
|
11
|
-
@data[:frames] = frames
|
12
8
|
end
|
13
9
|
attr_reader :data
|
14
10
|
|
@@ -165,7 +161,7 @@ module StackProf
|
|
165
161
|
f.printf "%s (%s:%d)\n", info[:name], file, line
|
166
162
|
f.printf " samples: % 5d self (%2.1f%%) / % 5d total (%2.1f%%)\n", info[:samples], 100.0*info[:samples]/overall_samples, info[:total_samples], 100.0*info[:total_samples]/overall_samples
|
167
163
|
|
168
|
-
if (callers = data[:frames].map{ |id, other| [other[:name], other[:edges][frame
|
164
|
+
if (callers = data[:frames].map{ |id, other| [other[:name], other[:edges][frame]] if other[:edges] && other[:edges].include?(frame) }.compact).any?
|
169
165
|
f.puts " callers:"
|
170
166
|
callers = callers.sort_by(&:last).reverse
|
171
167
|
callers.each do |name, weight|
|
@@ -175,7 +171,7 @@ module StackProf
|
|
175
171
|
|
176
172
|
if callees = info[:edges]
|
177
173
|
f.printf " callees (%d total):\n", info[:total_samples]-info[:samples]
|
178
|
-
callees = callees.map{ |k, weight| [data[:frames][k
|
174
|
+
callees = callees.map{ |k, weight| [data[:frames][k][:name], weight] }
|
179
175
|
callees.each do |name, weight|
|
180
176
|
f.printf " % 5d (% 8s) %s\n", weight, "%3.1f%%" % (100.0*weight/(info[:total_samples]-info[:samples])), name
|
181
177
|
end
|
data/stackprof.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'stackprof'
|
3
|
-
s.version = '0.2.
|
3
|
+
s.version = '0.2.1'
|
4
4
|
s.homepage = 'http://github.com/tmm1/stackprof'
|
5
5
|
|
6
6
|
s.authors = 'Aman Gupta'
|
@@ -17,6 +17,5 @@ Gem::Specification.new do |s|
|
|
17
17
|
|
18
18
|
s.license = 'MIT'
|
19
19
|
|
20
|
-
# s.add_dependency 'yajl-ruby'
|
21
20
|
s.add_development_dependency 'rake-compiler'
|
22
21
|
end
|
data/test/test_stackprof.rb
CHANGED
@@ -58,7 +58,6 @@ class StackProfTest < Test::Unit::TestCase
|
|
58
58
|
assert_operator profile[:samples], :>, 1
|
59
59
|
frame = profile[:frames].values.first
|
60
60
|
assert_equal "block in StackProfTest#math", frame[:name]
|
61
|
-
File.open('/tmp/cputime.dump','w'){|f| f.write Marshal.dump(profile) }
|
62
61
|
end
|
63
62
|
|
64
63
|
def test_walltime
|
@@ -99,8 +98,10 @@ class StackProfTest < Test::Unit::TestCase
|
|
99
98
|
end
|
100
99
|
|
101
100
|
def test_gc
|
102
|
-
profile = StackProf.run(
|
103
|
-
|
101
|
+
profile = StackProf.run(interval: 100) do
|
102
|
+
5.times do
|
103
|
+
GC.start
|
104
|
+
end
|
104
105
|
end
|
105
106
|
|
106
107
|
assert_empty profile[:frames]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stackprof
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aman Gupta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-12-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -66,8 +66,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
66
|
version: '0'
|
67
67
|
requirements: []
|
68
68
|
rubyforge_project:
|
69
|
-
rubygems_version: 2.2.0
|
69
|
+
rubygems_version: 2.2.0.rc.1
|
70
70
|
signing_key:
|
71
71
|
specification_version: 4
|
72
72
|
summary: sampling callstack-profiler for ruby 2.1+
|
73
73
|
test_files: []
|
74
|
+
has_rdoc:
|