heapy 0.1.4 → 0.2.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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/check_changelog.yml +13 -0
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +8 -0
  5. data/README.md +38 -4
  6. data/bin/heapy +1 -2
  7. data/heapy.gemspec +4 -4
  8. data/lib/heapy.rb +90 -55
  9. data/lib/heapy/analyzer.rb +15 -6
  10. data/lib/heapy/diff.rb +105 -0
  11. data/lib/heapy/version.rb +1 -1
  12. metadata +30 -35
  13. data/lib/heapy/alive.rb +0 -269
  14. data/scratch.rb +0 -64
  15. data/weird_memory/run.rb +0 -31
  16. data/weird_memory/singleton_class/singleton_class.rb +0 -26
  17. data/weird_memory/singleton_class/singleton_class_in_class.rb +0 -29
  18. data/weird_memory/singleton_class/singleton_class_in_proc.rb +0 -28
  19. data/weird_memory/singleton_class/singleton_class_method_in_proc.rb +0 -26
  20. data/weird_memory/singleton_class_instance_eval/singleton_class_instance_eval.rb +0 -27
  21. data/weird_memory/singleton_class_instance_eval/singleton_class_instance_eval_in_class.rb +0 -29
  22. data/weird_memory/singleton_class_instance_eval/singleton_class_instance_eval_in_proc.rb +0 -29
  23. data/weird_memory/singleton_class_instance_eval/singleton_class_instance_eval_method_in_proc.rb +0 -27
  24. data/weird_memory/string/string.rb +0 -25
  25. data/weird_memory/string/string_in_class.rb +0 -27
  26. data/weird_memory/string/string_in_proc.rb +0 -26
  27. data/weird_memory/string/string_method_in_proc.rb +0 -25
  28. data/weird_memory/times_map/times_map.rb +0 -28
  29. data/weird_memory/times_map/times_map_in_class.rb +0 -29
  30. data/weird_memory/times_map/times_map_in_proc.rb +0 -30
  31. 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: 642cdc83f817b16ef0fc8e35902a37bc197edd8edb0f14a1bab68920d2bf4d89
4
- data.tar.gz: f98f9629ace983a246ff4f2cadf5749e41297ba1cdf2da76d0712c4f4a36fc80
3
+ metadata.gz: a84bd36786d08056d5e172044cbbe30547ca5fc911aeee1a6a46905daef45c7d
4
+ data.tar.gz: b87cd42efd90097653dbe311955e66c45b961828e8c4538a2604d23f66bc8ba1
5
5
  SHA512:
6
- metadata.gz: 219b46ee0b8162fe40faac68d010f6c9867a2242920fd62056a7eac2a4f5739f2c5221e9067aec01328da931d2283feb5541eb571e4f401f8fa8a75f7c30bac8
7
- data.tar.gz: 48e4813a71c561866033544b7ae6f0fcfea4bfda45096dbe3977ed9a3244ccb93741533cb52d6648109f88dd9222486a763ee75deb3e6ae242423bb82ea3aa2b
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
@@ -9,3 +9,4 @@
9
9
  /tmp/
10
10
  tmp
11
11
  .DS_Store
12
+ scratch.rb
@@ -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
- ## Digging into a Generation
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
@@ -3,5 +3,4 @@
3
3
  require "bundler/setup" if defined?(Bundler)
4
4
  require "heapy"
5
5
 
6
-
7
- Heapy::CLI.new(ARGV).run
6
+ Heapy::CLI.start(ARGV)
@@ -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.add_development_dependency "bundler", "~> 1.10"
23
- spec.add_development_dependency "rake", "~> 10.0"
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
@@ -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
- class CLI
8
- def initialize(argv)
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
- def help
16
- puts <<-HALP
17
- $ heapy read <file|command> <number>
18
-
19
- When run with only a file, it will output the generation and count pairs:
20
-
21
- $ heapy read tmp/2015-09-30-heap.dump
22
- Generation: nil object count: 209191
23
- Generation: 14 object count: 407
24
- Generation: 15 object count: 638
25
- Generation: 16 object count: 748
26
- Generation: 17 object count: 1023
27
- Generation: 18 object count: 805
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
- def run
49
-
50
- case @cmd
51
- when "--help"
52
- help
53
- when nil
54
- help
55
- when "read"
56
- if @number
57
- Analyzer.new(@file).drill_down(@number)
58
- else
59
- Analyzer.new(@file).analyze
60
- end
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
- help
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/alive'
70
-
105
+ require 'heapy/diff'
@@ -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(50)
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(50)
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(50)
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(50)
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|
@@ -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