hotch 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c35cd590d82f32eb2e073d78b2dcf3a59f9dac26
4
- data.tar.gz: 0043e7a301ab0c4ede090e614723ca40657f172e
2
+ SHA256:
3
+ metadata.gz: 8a8f4b895fb76ef2998f7d51ce4a1725723649839b655a46e261e6b5d8bbd7a7
4
+ data.tar.gz: 55e5919e466022d1427d607d12fc8929248ceb4ad9c6716d0cfba9bc1cafe11e
5
5
  SHA512:
6
- metadata.gz: 85a612b48a755637431d2827db351df62ca9667837de967ffe1d7b71b9895b054a5bc46245d1d4432dbfa048a42a1381941c38edf92e77b955458cc8be6f31eb
7
- data.tar.gz: 540d85864f5f6c3a88b6f3de0a04ade4af677d317d2799fc01a8b93de1aa423014ff4b66df68125006b4e90451dd9a585797358c55308f8e5a049a09dd8c1051
6
+ metadata.gz: 21af11cc4cdfbab13d13cbe882cfc0bd28aecc72a746ae613c21031911d07d386cf2bc328ac165861517501681db221564b971b8f3658c354ffb7ac6633f7f0d
7
+ data.tar.gz: 6f43595863fb4975d6fd3fd632e7c920d1628d5a659349f433ec9b16b003d43c4de187ac25011a71d2f0c44bf6591ec4b03f6c9bc0d36ec47685fd9dc0904fec
@@ -1,4 +1,5 @@
1
1
  Copyright (c) 2015 Peter Suschlik
2
+ Copyright (c) 2017 Peter Leitzen (Suschlik)
2
3
 
3
4
  MIT License
4
5
 
data/README.md CHANGED
@@ -62,7 +62,7 @@ dry/struct/class_interface.rb:77 T_OBJECT 2000 0 0 0
62
62
  Add this line to your application's Gemfile:
63
63
 
64
64
  ```ruby
65
- gem 'hotch', '~> 0.4.0'
65
+ gem 'hotch', '~> 0.6.0'
66
66
  ```
67
67
 
68
68
  And then execute:
@@ -138,6 +138,10 @@ Hotch::Minitest.run(options: { limit: 200 })
138
138
 
139
139
  ### Memory profiler
140
140
 
141
+ Shell usage:
142
+
143
+ $ ruby -rhotch/memory/run my_program.rb
144
+
141
145
  Require `hotch/memory` and use `Hotch.memory { ... }` as in:
142
146
 
143
147
  ```ruby
@@ -162,9 +166,7 @@ module Types
162
166
  end
163
167
  end
164
168
 
165
- # For more stable results
166
- GC.disable
167
-
169
+ # For more stable results the GC is disabled by default during runs.
168
170
  Hotch.memory do
169
171
  1000.times do
170
172
  Types::Success.new(
@@ -173,6 +175,43 @@ Hotch.memory do
173
175
  )
174
176
  end
175
177
  end
178
+
179
+ # In order to prevent disabling the GC during runs do:
180
+ Hotch.memory(disable_gc: false) do
181
+ # ...
182
+ end
183
+
184
+ # Disable aggregation between runs:
185
+ Hotch.memory(aggregate: false) do
186
+ # this run is not aggregated
187
+ end
188
+ ```
189
+
190
+ #### Inline reporting
191
+
192
+ This prints two ASCII tables showing the object alloctions two calls:
193
+
194
+ ```ruby
195
+ puts Hotch::Memory.report("memory") {
196
+ # ...
197
+ }
198
+
199
+ puts Hotch::Memory.report("another") {
200
+ # ...
201
+ }
202
+ ```
203
+
204
+ ### Minitest integration for the memory profiler
205
+
206
+ Load `hotch/memory/minitest` in your `test/test_helper.rb` like this:
207
+
208
+ ```ruby
209
+ require 'minitest/autorun'
210
+ require 'hotch/memory/minitest'
211
+
212
+ Hotch::Minitest.run
213
+ Hotch::Minitest.run(name: "my name")
214
+ Hotch::Minitest.run(disable_gc: false) # on by default
176
215
  ```
177
216
 
178
217
 
@@ -198,5 +237,5 @@ Follow these steps to release this gem:
198
237
  edit lib/hotch/version.rb
199
238
  edit README.md
200
239
  # Commit
201
- git commit -m "Release vX.Y.Z"
240
+ git commit -am "Release vX.Y.Z"
202
241
  rake release
@@ -0,0 +1,15 @@
1
+ require 'hotch/memory'
2
+
3
+ ary = []
4
+
5
+ Hotch.memory do
6
+ ary << "string"
7
+ end
8
+
9
+ Hotch.memory do
10
+ ary << "another"
11
+ end
12
+
13
+ Hotch.memory do
14
+ ary.join(" ")
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'hotch/memory'
2
+
3
+ ary = []
4
+
5
+ puts Hotch::Memory.report("1") {
6
+ ary << "string"
7
+ }
8
+
9
+ puts Hotch::Memory.report("2") {
10
+ ary << "another"
11
+ }
12
+
13
+ puts Hotch::Memory.report("3") {
14
+ ary.join(" ")
15
+ }
@@ -6,10 +6,10 @@ require 'hotch/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "hotch"
8
8
  spec.version = Hotch::VERSION
9
- spec.authors = ["Peter Suschlik"]
10
- spec.email = ["peter@suschlik.de"]
9
+ spec.authors = ["Peter Leitzen"]
10
+ spec.email = ["peter@leitzen.de"]
11
11
  spec.summary = %q{Profile helper}
12
- spec.description = %q{Stackprofs your program, generates/display call-stack profile as SVG}
12
+ spec.description = %q{Callstack and memory profiler}
13
13
  spec.homepage = "https://github.com/splattael/hotch"
14
14
  spec.license = "MIT"
15
15
 
@@ -18,9 +18,9 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_runtime_dependency "stackprof", "~> 0.2.10"
21
+ spec.add_runtime_dependency "stackprof", "~> 0.2.15"
22
22
  spec.add_runtime_dependency "allocation_tracer", "~> 0.6.3"
23
23
 
24
- spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "bundler"
25
25
  spec.add_development_dependency "rake", "~> 10.0"
26
26
  end
@@ -1,6 +1,8 @@
1
1
  require 'stackprof'
2
2
  require 'tmpdir'
3
3
 
4
+ require 'hotch/version'
5
+
4
6
  class Hotch
5
7
  attr_reader :name, :viewer, :filter, :options
6
8
 
@@ -44,7 +46,13 @@ class Hotch
44
46
  dot = report_dot(report, dir, "profile.dot")
45
47
  svg = convert_svg(dir, dot, "profile.svg")
46
48
 
47
- yield svg
49
+ @reports.clear
50
+
51
+ if block_given?
52
+ yield report, svg
53
+ else
54
+ return report, svg
55
+ end
48
56
  end
49
57
 
50
58
  def report_at_exit
@@ -53,7 +61,7 @@ class Hotch
53
61
  at_exit do
54
62
  stop
55
63
 
56
- report do |svg|
64
+ report do |_, svg|
57
65
  if viewer
58
66
  puts "Profile SVG: #{svg}"
59
67
  Kernel.system viewer, svg
@@ -86,7 +94,7 @@ class Hotch
86
94
 
87
95
  def convert_svg(dir, dot, file)
88
96
  svg = File.join(dir, file)
89
- system("dot", "-Tsvg", "-o", svg, dot) or raise "dot failed"
97
+ system("dot", "-Tsvg", "-o", svg, dot) or raise "dot: command not found. Please install graphviz"
90
98
  svg
91
99
  end
92
100
 
@@ -1,26 +1,40 @@
1
1
  require 'allocation_tracer'
2
2
 
3
3
  class Hotch
4
- def self.memory(name: $0)
5
- caller = Kernel.caller_locations(1).first
6
- name = "#{name}:#{caller.path}:#{caller.lineno}"
7
- memory = Memory.new(name)
4
+ def self.memory(name: $0, aggregate: true, &block)
5
+ memory = if aggregate
6
+ $hotch_memory ||= Memory.new(name)
7
+ else
8
+ caller = Kernel.caller_locations(1).first
9
+ name = "#{name}:#{caller.path}:#{caller.lineno}"
10
+ Memory.new(name)
11
+ end
8
12
 
9
13
  memory.report_at_exit
10
- memory.run do
11
- yield
14
+
15
+ if block
16
+ memory.run(&block)
17
+ else
18
+ memory.start
12
19
  end
13
20
  end
14
21
 
15
22
  class Memory
16
- def initialize(name, ignore_paths: [])
23
+ def initialize(name, ignore_paths: [], disable_gc: true)
17
24
  @name = name
18
25
  @ignore_paths = Array(ignore_paths || [])
19
- @report = nil
26
+ @reports = []
20
27
  @started = nil
28
+ @disable_gc = disable_gc
29
+ end
30
+
31
+ def self.report(name, **args, &block)
32
+ new(name, **args).run(&block).report
21
33
  end
22
34
 
23
35
  def start
36
+ return if @started
37
+ GC.disable if @disable_gc
24
38
  ObjectSpace::AllocationTracer.setup [:path, :line, :type]
25
39
  ObjectSpace::AllocationTracer.start
26
40
  @started = true
@@ -30,19 +44,28 @@ class Hotch
30
44
  return unless @started
31
45
  results = ObjectSpace::AllocationTracer.stop
32
46
  @started = nil
33
- @report = Report.new(results, @ignore_paths)
47
+ GC.enable if @disable_gc
48
+ @reports << Report.new(results, @ignore_paths)
34
49
  end
35
50
 
36
51
  def run
37
52
  start
38
53
  yield
54
+ self
39
55
  ensure
40
56
  stop
41
57
  end
42
58
 
43
59
  def report
44
60
  # TODO make it persistent (as CSV)
45
- yield @report
61
+ report = @reports.inject(:+)
62
+ @reports.clear
63
+
64
+ if block_given?
65
+ yield report
66
+ else
67
+ report
68
+ end
46
69
  end
47
70
 
48
71
  def report_at_exit
@@ -66,17 +89,28 @@ class Hotch
66
89
  end
67
90
 
68
91
  class Report
92
+ attr_reader :lines
93
+
69
94
  def initialize(results, ignore_paths)
70
95
  @header = Line.new(*Line.members)
71
- @total = Line::Total.new
72
96
  @lines = results.map do |result|
73
- if line = Line.from_result(result, ignore_paths)
74
- @total.sum(line)
75
- line
76
- end
97
+ Line.from_result(result, ignore_paths)
77
98
  end.compact
78
99
  end
79
100
 
101
+ def +(other)
102
+ by_key = Hash[@lines.map { |line| [line.key, line] }]
103
+ other.lines.each do |line|
104
+ if existing = by_key[line.key]
105
+ existing.sum(line)
106
+ else
107
+ by_key[line.key] = line
108
+ @lines << line
109
+ end
110
+ end
111
+ self
112
+ end
113
+
80
114
  def format
81
115
  # TODO refactor
82
116
  max_lengths = Array.new(Line.members.size, 0)
@@ -89,12 +123,28 @@ class Hotch
89
123
  end
90
124
 
91
125
  def puts(io)
126
+ total!
92
127
  fmt = format
93
128
  @header.puts(io, fmt)
94
129
  @lines.sort_by(&:count).each { |line| line.puts(io, fmt) }
95
130
  @total.puts(io, fmt)
96
131
  end
97
132
 
133
+ def to_s
134
+ io = StringIO.new
135
+ puts(io)
136
+ io.string
137
+ end
138
+
139
+ private def total!
140
+ return if defined? @total
141
+
142
+ @total = Line::Total.new
143
+ @lines.each do |line|
144
+ @total.sum(line)
145
+ end
146
+ end
147
+
98
148
  class Line < Struct.new(:filename, :type, :count, :old_count, :total_age,
99
149
  :min_age, :max_age, :total_memsize)
100
150
  # [
@@ -104,10 +154,14 @@ class Hotch
104
154
  def self.from_result(result, ignore_paths)
105
155
  path, line, *args = result.flatten(1)
106
156
  return if ignore_paths.any? { |ip| ip == path || ip === path }
107
- filename = "#{strip_path(path)}:#{line}"
157
+ filename = "#{strip_path(path || "?")}:#{line}"
108
158
  new(filename, *args)
109
159
  end
110
160
 
161
+ def key
162
+ [filename, type]
163
+ end
164
+
111
165
  def puts(io, fmt)
112
166
  send = method(:send)
113
167
  io.puts fmt % members.map(&send)
@@ -117,6 +171,12 @@ class Hotch
117
171
  members.map { |member| self[member].to_s.size }
118
172
  end
119
173
 
174
+ def sum(other)
175
+ other.to_a.each.with_index do |value, i|
176
+ self[i] += value if Numeric === value
177
+ end
178
+ end
179
+
120
180
  private
121
181
 
122
182
  MAX_PATH_LENGTH = 50
@@ -135,12 +195,6 @@ class Hotch
135
195
  def initialize
136
196
  super("TOTAL", "", 0, 0, 0, 0, 0, 0)
137
197
  end
138
-
139
- def sum(other)
140
- other.to_a.each.with_index do |value, i|
141
- self[i] += value if Numeric === value
142
- end
143
- end
144
198
  end
145
199
  end
146
200
  end
@@ -0,0 +1,28 @@
1
+ require "hotch/memory"
2
+
3
+ class Hotch
4
+ class Memory
5
+ module Minitest
6
+ # Usage in test/test_helper.rb:
7
+ #
8
+ # require 'hotch/memory/minitest'
9
+ #
10
+ # Hotch::Memory::Minitest.run
11
+ # Hotch::Memory::Minitest.run(name: "my name")
12
+ def self.run(**options)
13
+ ::Minitest.singleton_class.prepend Hotch::Memory::Minitest.aggregate(**options)
14
+ end
15
+
16
+ def self.aggregate(**options)
17
+ Module.new do
18
+ define_method(:run_one_method) do |*args|
19
+ options[:aggregate] = true
20
+ Hotch.memory(**options) do
21
+ super(*args)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ require 'hotch/memory'
2
+
3
+ Hotch.memory(name: $0)
@@ -1,3 +1,3 @@
1
1
  class Hotch
2
- VERSION = "0.4.0"
2
+ VERSION = "0.6.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hotch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
- - Peter Suschlik
7
+ - Peter Leitzen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-17 00:00:00.000000000 Z
11
+ date: 2020-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: stackprof
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.2.10
19
+ version: 0.2.15
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.2.10
26
+ version: 0.2.15
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: allocation_tracer
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '1.7'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '1.7'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -66,9 +66,9 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '10.0'
69
- description: Stackprofs your program, generates/display call-stack profile as SVG
69
+ description: Callstack and memory profiler
70
70
  email:
71
- - peter@suschlik.de
71
+ - peter@leitzen.de
72
72
  executables: []
73
73
  extensions: []
74
74
  extra_rdoc_files: []
@@ -79,12 +79,16 @@ files:
79
79
  - README.md
80
80
  - Rakefile
81
81
  - examples/bundler.rb
82
+ - examples/memory_at_exit.rb
83
+ - examples/memory_inline_report.rb
82
84
  - examples/simple.rb
83
85
  - hotch.gemspec
84
86
  - images/dry-validation.profile_schema_call_valid.png
85
87
  - images/dry-validation.profile_schema_call_valid.svg
86
88
  - lib/hotch.rb
87
89
  - lib/hotch/memory.rb
90
+ - lib/hotch/memory/minitest.rb
91
+ - lib/hotch/memory/run.rb
88
92
  - lib/hotch/minitest.rb
89
93
  - lib/hotch/run.rb
90
94
  - lib/hotch/version.rb
@@ -107,8 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
111
  - !ruby/object:Gem::Version
108
112
  version: '0'
109
113
  requirements: []
110
- rubyforge_project:
111
- rubygems_version: 2.5.1
114
+ rubygems_version: 3.0.3
112
115
  signing_key:
113
116
  specification_version: 4
114
117
  summary: Profile helper