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 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