heapy 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/check_changelog.yml +13 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +8 -0
- data/README.md +38 -4
- data/bin/heapy +1 -2
- data/heapy.gemspec +4 -4
- data/lib/heapy.rb +90 -55
- data/lib/heapy/analyzer.rb +15 -6
- data/lib/heapy/diff.rb +105 -0
- data/lib/heapy/version.rb +1 -1
- metadata +30 -35
- data/lib/heapy/alive.rb +0 -269
- data/scratch.rb +0 -64
- data/weird_memory/run.rb +0 -31
- data/weird_memory/singleton_class/singleton_class.rb +0 -26
- data/weird_memory/singleton_class/singleton_class_in_class.rb +0 -29
- data/weird_memory/singleton_class/singleton_class_in_proc.rb +0 -28
- data/weird_memory/singleton_class/singleton_class_method_in_proc.rb +0 -26
- data/weird_memory/singleton_class_instance_eval/singleton_class_instance_eval.rb +0 -27
- data/weird_memory/singleton_class_instance_eval/singleton_class_instance_eval_in_class.rb +0 -29
- data/weird_memory/singleton_class_instance_eval/singleton_class_instance_eval_in_proc.rb +0 -29
- data/weird_memory/singleton_class_instance_eval/singleton_class_instance_eval_method_in_proc.rb +0 -27
- data/weird_memory/string/string.rb +0 -25
- data/weird_memory/string/string_in_class.rb +0 -27
- data/weird_memory/string/string_in_proc.rb +0 -26
- data/weird_memory/string/string_method_in_proc.rb +0 -25
- data/weird_memory/times_map/times_map.rb +0 -28
- data/weird_memory/times_map/times_map_in_class.rb +0 -29
- data/weird_memory/times_map/times_map_in_proc.rb +0 -30
- data/weird_memory/times_map/times_map_method_in_proc.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a84bd36786d08056d5e172044cbbe30547ca5fc911aeee1a6a46905daef45c7d
|
4
|
+
data.tar.gz: b87cd42efd90097653dbe311955e66c45b961828e8c4538a2604d23f66bc8ba1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc703b95887ec3343cf21fc75e68ade763a763c357b7ef4e3e6dcc94d18751559241a587ebb9160321dfb2a975e51707ac5a030ba450dd76b8cf3ba31f385660
|
7
|
+
data.tar.gz: b13485e91fdbe6922e7777c7a8acfd48d3be5433891e2e994d38fa6c6c59881ae6085e532e9b9a04e82d6a80a005ef73afbe684ee2efe27fcbc85a402aeb5a17
|
@@ -0,0 +1,13 @@
|
|
1
|
+
name: Check Changelog
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request:
|
5
|
+
types: [opened, reopened, edited, synchronize]
|
6
|
+
jobs:
|
7
|
+
build:
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
steps:
|
10
|
+
- uses: actions/checkout@v1
|
11
|
+
- name: Check that CHANGELOG is touched
|
12
|
+
run: |
|
13
|
+
cat $GITHUB_EVENT_PATH | jq .pull_request.title | grep -i '\[\(\(changelog skip\)\|\(ci skip\)\)\]' || git diff remotes/origin/${{ github.base_ref }} --name-only | grep CHANGELOG.md
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## HEAD
|
2
|
+
|
3
|
+
## 0.2.0
|
4
|
+
|
5
|
+
- Heapy::Alive is removed (https://github.com/schneems/heapy/pull/27)
|
6
|
+
- New command `heapy diff` (https://github.com/schneems/heapy/pull/26)
|
7
|
+
- The read command now takes a --lines flag that can be used to limit output when showing generational data
|
8
|
+
|
1
9
|
## 0.1.4 - 2018-07-25
|
2
10
|
|
3
11
|
- Bundler is no longer required so heapy can now be used via a simple
|
data/README.md
CHANGED
@@ -21,6 +21,34 @@ Or install it yourself as:
|
|
21
21
|
|
22
22
|
## Usage
|
23
23
|
|
24
|
+
### Diff 2 heap dumps
|
25
|
+
|
26
|
+
Run with two inputs to output the values of today.dump that are not present in yesterday.dump
|
27
|
+
|
28
|
+
```
|
29
|
+
$ heapy diff tmp/yesterday.dump tmp/today_morning.dump
|
30
|
+
Allocated STRING 9991 objects of size 399640/491264 (in bytes) at: scratch.rb:24
|
31
|
+
```
|
32
|
+
|
33
|
+
Run with three inputs to show the diff between the first two, but only if the objects are still retained in the third
|
34
|
+
|
35
|
+
```
|
36
|
+
$ heapy diff tmp/yesterday.dump tmp/today_morning.dump tmp/today_afternoon.dump
|
37
|
+
Retained STRING 9991 objects of size 399640/491264 (in bytes) at: scratch.rb:24
|
38
|
+
# ...
|
39
|
+
```
|
40
|
+
|
41
|
+
Pass in the name of an output file and the objects present in today.dump that aren't in yesterday.dump will be written to that file
|
42
|
+
|
43
|
+
```
|
44
|
+
$ heapy diff tmp/yesterday.dump tmp/today.dump --output_diff=output.json
|
45
|
+
Allocated STRING 9991 objects of size 399640/491264 (in bytes) at: scratch.rb:24
|
46
|
+
# ...
|
47
|
+
Writing heap dump diff to output.json
|
48
|
+
```
|
49
|
+
|
50
|
+
### Read a Heap Dump
|
51
|
+
|
24
52
|
Step 1) Generate a heap dump. You could [do this manually](http://samsaffron.com/archive/2015/03/31/debugging-memory-leaks-in-ruby). Or you can use a tool like [derailed_benchmarks](https://github.com/schneems/derailed_benchmarks)
|
25
53
|
|
26
54
|
Step 2) Once you've got the heap dump, you can analyze it using this CLI:
|
@@ -54,11 +82,10 @@ $ ruby -I ./ -r trace script_name.rb
|
|
54
82
|
|
55
83
|
If the last line of your file is invalid JSON, make sure that you are closing the file after writing the ruby heap dump to it.
|
56
84
|
|
57
|
-
|
85
|
+
### Digging into a Generation
|
58
86
|
|
59
87
|
You can drill down into a specific generation. In the previous example, the 17'th generation looks strangely large, you can drill into it:
|
60
88
|
|
61
|
-
|
62
89
|
```
|
63
90
|
$ heapy read tmp/2015-10-01T10:18:59-05:00-heap.dump 17
|
64
91
|
Analyzing Heap (Generation: 17)
|
@@ -74,6 +101,14 @@ $ heapy read tmp/2015-10-01T10:18:59-05:00-heap.dump 17
|
|
74
101
|
92200 /app/vendor/bundle/ruby/2.2.0/gems/activesupport-4.2.3/lib/active_support/core_ext/numeric/conversions.rb:131
|
75
102
|
```
|
76
103
|
|
104
|
+
You can limit the output by passing in a `--lines` value:
|
105
|
+
|
106
|
+
```
|
107
|
+
$ heapy read tmp/2015-10-01T10:18:59-05:00-heap.dump 17 --lines=6
|
108
|
+
```
|
109
|
+
|
110
|
+
> Note: Default lines value is 50
|
111
|
+
|
77
112
|
### Reviewing all generations
|
78
113
|
|
79
114
|
If you want to read all generations you can use the "all" directive
|
@@ -82,7 +117,7 @@ If you want to read all generations you can use the "all" directive
|
|
82
117
|
$ heapy read tmp/2015-10-01T10:18:59-05:00-heap.dump all
|
83
118
|
```
|
84
119
|
|
85
|
-
You can also use T-Lo's online JS based [Heap Analyzer](http://tenderlove.github.io/heap-analyzer/) for visualizations.
|
120
|
+
You can also use T-Lo's online JS based [Heap Analyzer](http://tenderlove.github.io/heap-analyzer/) for visualizations. Another tool is [HARB](https://github.com/csfrancis/harb)
|
86
121
|
|
87
122
|
## Development
|
88
123
|
|
@@ -94,7 +129,6 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
94
129
|
|
95
130
|
Bug reports and pull requests are welcome on GitHub at https://github.com/schneems/heapy. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
96
131
|
|
97
|
-
|
98
132
|
## License
|
99
133
|
|
100
134
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/bin/heapy
CHANGED
data/heapy.gemspec
CHANGED
@@ -19,9 +19,9 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.
|
23
|
-
|
22
|
+
spec.add_dependency "thor"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "> 1"
|
25
|
+
spec.add_development_dependency "rake", "> 10.0"
|
24
26
|
spec.add_development_dependency "rspec"
|
25
|
-
|
26
|
-
spec.required_ruby_version = '~> 2.3'
|
27
27
|
end
|
data/lib/heapy.rb
CHANGED
@@ -1,70 +1,105 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'thor'
|
2
3
|
|
3
4
|
require "heapy/version"
|
4
5
|
|
5
6
|
module Heapy
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@cmd = argv.shift
|
10
|
-
@file = argv.shift
|
11
|
-
@number = argv.shift
|
12
|
-
@args = argv
|
7
|
+
class CLI < Thor
|
8
|
+
def self.exit_on_failure?
|
9
|
+
true
|
13
10
|
end
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
When run with a file and a number it will output detailed information for that
|
30
|
-
generation:
|
31
|
-
|
32
|
-
$ heapy read tmp/2015-09-30-heap.dump 17
|
33
|
-
|
34
|
-
Analyzing Heap (Generation: 17)
|
35
|
-
-------------------------------
|
36
|
-
|
37
|
-
allocated by memory (44061517) (in bytes)
|
38
|
-
==============================
|
39
|
-
39908512 /app/vendor/ruby-2.2.3/lib/ruby/2.2.0/timeout.rb:79
|
40
|
-
1284993 /app/vendor/ruby-2.2.3/lib/ruby/2.2.0/openssl/buffering.rb:182
|
41
|
-
201068 /app/vendor/bundle/ruby/2.2.0/gems/json-1.8.3/lib/json/common.rb:223
|
42
|
-
189272 /app/vendor/bundle/ruby/2.2.0/gems/newrelic_rpm-3.13.2.302/lib/new_relic/agent/stats_engine/stats_hash.rb:39
|
43
|
-
172531 /app/vendor/ruby-2.2.3/lib/ruby/2.2.0/net/http/header.rb:172
|
44
|
-
92200 /app/vendor/bundle/ruby/2.2.0/gems/activesupport-4.2.3/lib/active_support/core_ext/numeric/conversions.rb:131
|
45
|
-
HALP
|
46
|
-
end
|
12
|
+
desc "read <file> <generation> --lines <number_of_lines>", "Read heap dump file"
|
13
|
+
long_desc <<-DESC
|
14
|
+
When run with only a file input, it will output the generation and count pairs:
|
15
|
+
|
16
|
+
$ heapy read tmp/2015-09-30-heap.dump\x5
|
17
|
+
Generation: nil object count: 209191\x5
|
18
|
+
Generation: 14 object count: 407\x5
|
19
|
+
Generation: 15 object count: 638\x5
|
20
|
+
Generation: 16 object count: 748\x5
|
21
|
+
Generation: 17 object count: 1023\x5
|
22
|
+
Generation: 18 object count: 805\x5
|
23
|
+
|
24
|
+
When run with a file and a number it will output detailed information for that\x5
|
25
|
+
generation:\x5
|
47
26
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
27
|
+
$ heapy read tmp/2015-09-30-heap.dump 17\x5
|
28
|
+
|
29
|
+
Analyzing Heap (Generation: 17)\x5
|
30
|
+
-------------------------------\x5
|
31
|
+
|
32
|
+
allocated by memory (44061517) (in bytes)\x5
|
33
|
+
==============================\x5
|
34
|
+
39908512 /app/vendor/ruby-2.2.3/lib/ruby/2.2.0/timeout.rb:79\x5
|
35
|
+
1284993 /app/vendor/ruby-2.2.3/lib/ruby/2.2.0/openssl/buffering.rb:182\x5
|
36
|
+
201068 /app/vendor/bundle/ruby/2.2.0/gems/json-1.8.3/lib/json/common.rb:223\x5
|
37
|
+
189272 /app/vendor/bundle/ruby/2.2.0/gems/newrelic_rpm-3.13.2.302/lib/new_relic/agent/stats_engine/stats_hash.rb:39\x5
|
38
|
+
172531 /app/vendor/ruby-2.2.3/lib/ruby/2.2.0/net/http/header.rb:172\x5
|
39
|
+
92200 /app/vendor/bundle/ruby/2.2.0/gems/activesupport-4.2.3/lib/active_support/core_ext/numeric/conversions.rb:131\x5
|
40
|
+
DESC
|
41
|
+
option :lines, required: false, :type => :numeric
|
42
|
+
def read(file_name, generation = nil)
|
43
|
+
if generation
|
44
|
+
Analyzer.new(file_name).drill_down(generation, options[:lines] || 50)
|
61
45
|
else
|
62
|
-
|
46
|
+
Analyzer.new(file_name).analyze
|
63
47
|
end
|
64
48
|
end
|
49
|
+
|
50
|
+
long_desc <<-DESC
|
51
|
+
Run with two inputs to output the values of today.dump that are not present in yesterday.dump
|
52
|
+
|
53
|
+
$ heapy diff tmp/yesterday.dump tmp/today.dump\x5
|
54
|
+
|
55
|
+
Run with three inputs to show the diff between the first two, but only if the objects are still retained in the third
|
56
|
+
|
57
|
+
$ heapy diff tmp/yesterday.dump tmp/today_morning.dump tmp/today_afternoon.dump\x5
|
58
|
+
|
59
|
+
Pass in the name of an output file and the objects present in today.dump that aren't in yesterday.dump will be written to that file
|
60
|
+
|
61
|
+
$ heapy diff tmp/yesterday.dump tmp/today.dump --output_diff=output.json\x5
|
62
|
+
|
63
|
+
DESC
|
64
|
+
desc "diff <before_file> <after_file> <retained_file (optional)> --output_diff=output.json", "Diffs 2 heap dumps"
|
65
|
+
option :output_diff, required: false, :type => :string
|
66
|
+
def diff(before, after, retained = nil)
|
67
|
+
Diff.new(before: before, after: after, retained: retained, output_diff: options[:output_diff] || nil).call
|
68
|
+
end
|
69
|
+
|
70
|
+
map %w[--version -v] => :version
|
71
|
+
desc "version", "Show heapy version"
|
72
|
+
def version
|
73
|
+
puts Heapy::VERSION
|
74
|
+
end
|
75
|
+
|
76
|
+
desc "wat", "Outputs instructions on how to make a manual heap dump"
|
77
|
+
def wat
|
78
|
+
puts <<-HELP
|
79
|
+
|
80
|
+
To get a heap dump do this:
|
81
|
+
|
82
|
+
require 'objspace'
|
83
|
+
ObjectSpace.trace_object_allocations_start
|
84
|
+
|
85
|
+
# Your code here
|
86
|
+
|
87
|
+
p ObjectSpace.dump_all
|
88
|
+
|
89
|
+
# => #<File:/path/to/output/heap/dump/here.json>
|
90
|
+
|
91
|
+
This will print the file name of your heap dump.
|
92
|
+
|
93
|
+
If you prefer you can manually pass in an IO object to `ObjectSpace.dump_all`
|
94
|
+
|
95
|
+
io = File.open("/tmp/my_dump.json", "w+")
|
96
|
+
ObjectSpace.dump_all(output: io);
|
97
|
+
io.close
|
98
|
+
|
99
|
+
HELP
|
100
|
+
end
|
65
101
|
end
|
66
102
|
end
|
67
103
|
|
68
104
|
require 'heapy/analyzer'
|
69
|
-
require 'heapy/
|
70
|
-
|
105
|
+
require 'heapy/diff'
|
data/lib/heapy/analyzer.rb
CHANGED
@@ -1,4 +1,14 @@
|
|
1
1
|
module Heapy
|
2
|
+
|
3
|
+
# Used for inspecting contents of a heap dump
|
4
|
+
#
|
5
|
+
# To glance all contents at a glance run:
|
6
|
+
#
|
7
|
+
# Analyzer.new(file_name).analyze
|
8
|
+
#
|
9
|
+
# To inspect contents of a specific generation run:
|
10
|
+
#
|
11
|
+
# Analyzer.new(file_name).drill_down(generation, Float::INFINITY)
|
2
12
|
class Analyzer
|
3
13
|
def initialize(filename)
|
4
14
|
@filename = filename
|
@@ -17,7 +27,7 @@ module Heapy
|
|
17
27
|
end
|
18
28
|
end
|
19
29
|
|
20
|
-
def drill_down(generation_to_inspect)
|
30
|
+
def drill_down(generation_to_inspect, max_items_to_display)
|
21
31
|
puts ""
|
22
32
|
puts "Analyzing Heap (Generation: #{generation_to_inspect})"
|
23
33
|
puts "-------------------------------"
|
@@ -25,7 +35,6 @@ module Heapy
|
|
25
35
|
|
26
36
|
generation_to_inspect = Integer(generation_to_inspect) unless generation_to_inspect == "all"
|
27
37
|
|
28
|
-
#
|
29
38
|
memsize_hash = Hash.new { |h, k| h[k] = 0 }
|
30
39
|
count_hash = Hash.new { |h, k| h[k] = 0 }
|
31
40
|
string_count = Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = 0 } }
|
@@ -58,7 +67,7 @@ module Heapy
|
|
58
67
|
# /Users/richardschneeman/Documents/projects/codetriage/app/views/layouts/application.html.slim:1"=>[{"address"=>"0x7f8a4fbf2328", "type"=>"STRING", "class"=>"0x7f8a4d5dec68", "bytesize"=>223051, "capacity"=>376832, "encoding"=>"UTF-8", "file"=>"/Users/richardschneeman/Documents/projects/codetriage/app/views/layouts/application.html.slim", "line"=>1, "method"=>"new", "generation"=>36, "memsize"=>377065, "flags"=>{"wb_protected"=>true, "old"=>true, "long_lived"=>true, "marked"=>true}}]}
|
59
68
|
puts "allocated by memory (#{total_memsize}) (in bytes)"
|
60
69
|
puts "=============================="
|
61
|
-
memsize_hash = memsize_hash.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.first(
|
70
|
+
memsize_hash = memsize_hash.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.first(max_items_to_display)
|
62
71
|
longest = memsize_hash.first[1].to_s.length
|
63
72
|
memsize_hash.each do |file_line, memsize|
|
64
73
|
puts " #{memsize.to_s.rjust(longest)} #{file_line}"
|
@@ -69,7 +78,7 @@ module Heapy
|
|
69
78
|
puts ""
|
70
79
|
puts "object count (#{total_count})"
|
71
80
|
puts "=============================="
|
72
|
-
count_hash = count_hash.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.first(
|
81
|
+
count_hash = count_hash.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.first(max_items_to_display)
|
73
82
|
longest = count_hash.first[1].to_s.length
|
74
83
|
count_hash.each do |file_line, memsize|
|
75
84
|
puts " #{memsize.to_s.rjust(longest)} #{file_line}"
|
@@ -80,7 +89,7 @@ module Heapy
|
|
80
89
|
puts "=============================="
|
81
90
|
puts ""
|
82
91
|
|
83
|
-
reference_hash = reference_hash.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.first(
|
92
|
+
reference_hash = reference_hash.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.first(max_items_to_display)
|
84
93
|
longest = count_hash.first[1].to_s.length
|
85
94
|
|
86
95
|
reference_hash.each do |file_line, count|
|
@@ -99,7 +108,7 @@ module Heapy
|
|
99
108
|
value_count[string] = location_count_hash.values.inject(&:+)
|
100
109
|
end
|
101
110
|
|
102
|
-
value_count = value_count.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.first(
|
111
|
+
value_count = value_count.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.first(max_items_to_display)
|
103
112
|
longest = value_count.first[1].to_s.length
|
104
113
|
|
105
114
|
value_count.each do |string, c1|
|
data/lib/heapy/diff.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
module Heapy
|
5
|
+
# Diff 2 dumps example:
|
6
|
+
#
|
7
|
+
# Heapy::Diff.new(before: 'my_dump_1.json', after: 'my_dump_2.json').call
|
8
|
+
#
|
9
|
+
# This will find objects that are present in my_dump_2 that are not present in my_dump_1
|
10
|
+
# this means they were allocated sometime between the two heap dumps.
|
11
|
+
#
|
12
|
+
# Diff 3 dumps example:
|
13
|
+
#
|
14
|
+
# Heapy::Diff.new(before: 'my_dump_1.json', after: 'my_dump_2.json', retained: 'my_dump_3.json').call
|
15
|
+
#
|
16
|
+
# This will find objects that are present in my_dump_2 that are not present in my_dump_1
|
17
|
+
# but only if the objects are still present at the time that my_dump_3 was taken. This does
|
18
|
+
# not guarantee that they're retained forever, but were still present at the time the last
|
19
|
+
# dump was taken.
|
20
|
+
#
|
21
|
+
# You can output the diff of heap dumps by passing in a filename as `output_diff` for example
|
22
|
+
#
|
23
|
+
# Heapy::Diff.new(before: 'my_dump_1.json', after: 'my_dump_2.json', outpu_diff: 'out.json').call
|
24
|
+
class Diff
|
25
|
+
attr_reader :diff
|
26
|
+
|
27
|
+
def initialize(before:, after:, retained: nil, io: STDOUT, output_diff: nil)
|
28
|
+
@before_file = before
|
29
|
+
@after_file = after
|
30
|
+
@retained_file = retained
|
31
|
+
@output_diff_file = output_diff ? File.open(output_diff, "w+") : nil
|
32
|
+
@io = io
|
33
|
+
@diff = Hash.new { |hash, k|
|
34
|
+
hash[k] = {}
|
35
|
+
hash[k]["count"] = 0
|
36
|
+
hash[k]["memsize"] = 0
|
37
|
+
hash[k]
|
38
|
+
}
|
39
|
+
|
40
|
+
@before_address_hash = {}
|
41
|
+
@retained_address_hash = {}
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def call
|
46
|
+
read(@before_file) { |parsed| @before_address_hash[parsed['address']] = true }
|
47
|
+
read(@retained_file) { |parsed| @retained_address_hash[parsed['address']] = true } if @retained_file
|
48
|
+
|
49
|
+
read(@after_file) do |parsed, original_line|
|
50
|
+
address = parsed['address']
|
51
|
+
next if previously_allocated?(address)
|
52
|
+
next if not_retained?(address)
|
53
|
+
|
54
|
+
@output_diff_file.puts original_line if @output_diff_file
|
55
|
+
|
56
|
+
hash = diff["#{parsed['type']},#{parsed['file']},#{parsed['line']}"]
|
57
|
+
hash["count"] += 1
|
58
|
+
hash["memsize"] += parsed["memsize"] || 0
|
59
|
+
hash["type"] ||= parsed["type"]
|
60
|
+
hash["file"] ||= parsed["file"]
|
61
|
+
hash["line"] ||= parsed["line"]
|
62
|
+
end
|
63
|
+
|
64
|
+
@output_diff_file.close if @output_diff_file
|
65
|
+
@before_address_hash.clear
|
66
|
+
@retained_address_hash.clear
|
67
|
+
|
68
|
+
total_memsize = diff.inject(0){|sum,(_,v)| sum + v["memsize"] }
|
69
|
+
|
70
|
+
diff.sort_by do |k,v|
|
71
|
+
v["count"]
|
72
|
+
end.reverse.each do |key, data|
|
73
|
+
@io.puts "#{@retained_file ? "Retained" : "Allocated"} #{data['type']} #{data['count']} objects of size #{data['memsize']}/#{total_memsize} (in bytes) at: #{data['file']}:#{data['line']}"
|
74
|
+
end
|
75
|
+
|
76
|
+
@io.puts "\nWriting heap dump diff to #{@output_diff_file.path}\n" if @output_diff_file
|
77
|
+
end
|
78
|
+
|
79
|
+
private def is_retained?(address)
|
80
|
+
return true if @retained_file.nil?
|
81
|
+
@retained_address_hash[address]
|
82
|
+
end
|
83
|
+
|
84
|
+
private def not_retained?(address)
|
85
|
+
!is_retained?(address)
|
86
|
+
end
|
87
|
+
|
88
|
+
private def previously_allocated?(address)
|
89
|
+
@before_address_hash[address]
|
90
|
+
end
|
91
|
+
|
92
|
+
private def read(filename)
|
93
|
+
File.open(filename) do |f|
|
94
|
+
f.each_line do |line|
|
95
|
+
begin
|
96
|
+
parsed = JSON.parse(line)
|
97
|
+
yield parsed, line
|
98
|
+
rescue JSON::ParserError
|
99
|
+
puts "Could not parse #{line}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|