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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c4d9be2e4ad16929b326e3a1e86c841ad0e13dc6
4
- data.tar.gz: 826eff5e56b4ae6c03c613dd9937a5ae396efa98
3
+ metadata.gz: ba362f7b8c12e89a30cc376280443aef51ce7ac6
4
+ data.tar.gz: 561f31e9f8b90a728dec5c9d6090f4bd7701228a
5
5
  SHA512:
6
- metadata.gz: 6153df14d977ffa0b1445950de2e779a8e8dd6606e665ad22bad348619b16659862bce078d1c12eb0d48f6bb90a5c0bd8ae73bc2c4d861466d6f16cda285d4c1
7
- data.tar.gz: 06ccf3dc44113e69b544aab7d9c4820daec59ec4b0d3b5e9784960d52b6ead75907f320d66cf4084319567f890862e4ab6954211e1b5c09d114f8553942e51ad
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
- three reporting modes are supported:
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 = 'stackprof-report [file.dump]+'
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 output (default)'){ options[:format] = :text }
15
- o.on('--callgrind'){ options[:format] = :callgrind }
16
- o.on('--graphviz'){ options[:format] = :graphviz }
17
- o.on('--file [grep]'){ |f| options[:format] = :file; options[:filter] = f }
18
- o.on('--files'){ |f| options[:format] = :files }
19
- o.on('--method [grep]'){ |f| options[:format] = :method; options[:filter] = f }
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('--sort-total'){ options[:sort] = true }
22
- o.on('--limit [num]', Integer){ |n| options[:limit] = n }
23
- end.parse!
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
@@ -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
- at_exit{ Middleware.save if Middleware.enabled? }
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: :cpu, interval: 1000) if self.class.enabled?
16
+ StackProf.start(mode: Middleware.mode, interval: Middleware.interval) if Middleware.enabled?
12
17
  @app.call(env)
13
18
  ensure
14
- StackProf.stop if self.class.enabled?
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", 'w') do |f|
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
@@ -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.to_i]] if other[:edges] && other[:edges].include?(frame.to_i) }.compact).any?
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.to_s][:name], weight] }
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.0'
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
@@ -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(mode: :cpu, interval: 100) do
103
- GC.start
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.0
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-23 00:00:00.000000000 Z
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: