hotch 0.4.0 → 0.6.0
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 +5 -5
- data/LICENSE.txt +1 -0
- data/README.md +44 -5
- data/examples/memory_at_exit.rb +15 -0
- data/examples/memory_inline_report.rb +15 -0
- data/hotch.gemspec +5 -5
- data/lib/hotch.rb +11 -3
- data/lib/hotch/memory.rb +76 -22
- data/lib/hotch/memory/minitest.rb +28 -0
- data/lib/hotch/memory/run.rb +3 -0
- data/lib/hotch/version.rb +1 -1
- metadata +16 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8a8f4b895fb76ef2998f7d51ce4a1725723649839b655a46e261e6b5d8bbd7a7
|
4
|
+
data.tar.gz: 55e5919e466022d1427d607d12fc8929248ceb4ad9c6716d0cfba9bc1cafe11e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 21af11cc4cdfbab13d13cbe882cfc0bd28aecc72a746ae613c21031911d07d386cf2bc328ac165861517501681db221564b971b8f3658c354ffb7ac6633f7f0d
|
7
|
+
data.tar.gz: 6f43595863fb4975d6fd3fd632e7c920d1628d5a659349f433ec9b16b003d43c4de187ac25011a71d2f0c44bf6591ec4b03f6c9bc0d36ec47685fd9dc0904fec
|
data/LICENSE.txt
CHANGED
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.
|
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 -
|
240
|
+
git commit -am "Release vX.Y.Z"
|
202
241
|
rake release
|
data/hotch.gemspec
CHANGED
@@ -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
|
10
|
-
spec.email = ["peter@
|
9
|
+
spec.authors = ["Peter Leitzen"]
|
10
|
+
spec.email = ["peter@leitzen.de"]
|
11
11
|
spec.summary = %q{Profile helper}
|
12
|
-
spec.description = %q{
|
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.
|
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"
|
24
|
+
spec.add_development_dependency "bundler"
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
26
|
end
|
data/lib/hotch.rb
CHANGED
@@ -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
|
-
|
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
|
97
|
+
system("dot", "-Tsvg", "-o", svg, dot) or raise "dot: command not found. Please install graphviz"
|
90
98
|
svg
|
91
99
|
end
|
92
100
|
|
data/lib/hotch/memory.rb
CHANGED
@@ -1,26 +1,40 @@
|
|
1
1
|
require 'allocation_tracer'
|
2
2
|
|
3
3
|
class Hotch
|
4
|
-
def self.memory(name: $0)
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
11
|
-
|
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
|
-
@
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/hotch/version.rb
CHANGED
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
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Peter
|
7
|
+
- Peter Leitzen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
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.
|
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.
|
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: '
|
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: '
|
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:
|
69
|
+
description: Callstack and memory profiler
|
70
70
|
email:
|
71
|
-
- peter@
|
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
|
-
|
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
|