heapy 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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