heapy 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 56f148e16da6c3dd3e07b1db360eb39e3ff9d076
4
- data.tar.gz: a00043f1abb083c6902f5ad9a113c877a2de1857
3
+ metadata.gz: ababd2495856e5682c0f7f136ee0fe112304bc1a
4
+ data.tar.gz: db47b39aa2d4207dc27a1753c1444830aba758bb
5
5
  SHA512:
6
- metadata.gz: 7afc522e6d2fe8f666ebf00bfa8f002be1bc38c98891fdfee88f5cb407d45ec3701fa09f4c2adcf1f6a98466f48cd0332f9479888e927bc2c53457560ab1300f
7
- data.tar.gz: ea9e2a37f6e00973b820f526b894cfb4637e15f20135a5aceed2b87ad71634290b004c13c67542f050f6473145a180bf2e2d17207d46e9a1faf98c045f5826b1
6
+ metadata.gz: 5695269c2619549c931188225fd6b52f0d2acd343f92aba069a8e3cd937d0c97d2b6b15f2585bd96ae78c905708b8612a0ff51bcbfb4ce746f6bd6df423c62f4
7
+ data.tar.gz: b70fbde861babd816b71ee8da6c8ecafb06192740dc4dcbdbb19ad930578925d62ffbdf295566f9e2d938366e385281303a4b1c16ad86c9486ff6f4bb8131d13
data/README.md CHANGED
@@ -27,16 +27,35 @@ Step 2) Once you've got the heap dump, you can analyze it using this CLI:
27
27
  ```
28
28
  $ heapy read tmp/2015-10-01T10:18:59-05:00-heap.dump
29
29
 
30
- Generation: 0 object count: 209191
31
- Generation: 14 object count: 407
32
- Generation: 15 object count: 638
33
- Generation: 16 object count: 748
34
- Generation: 17 object count: 1023
35
- Generation: 18 object count: 805
30
+ Generation: nil object count: 209191
31
+ Generation: 14 object count: 407
32
+ Generation: 15 object count: 638
33
+ Generation: 16 object count: 748
34
+ Generation: 17 object count: 1023
35
+ Generation: 18 object count: 805
36
36
  # ...
37
37
  ```
38
38
 
39
- Generally early generations will have a high object count as an app is initialized. Over time however, the object count should stabalize. If however you see it spike up, you can drill down into a specific generation. In the previous example, the 17'th generation looks strangely large, you can drill into it:
39
+ NOTE: The reason you may be getting a "nil" generation is these objects were loaded into memory before your code began tracking the allocations. To ensure all allocations are tracked you can execute your ruby script this trick. First create a file `trace.rb` that only starts allocation tracing:
40
+
41
+ ```
42
+ # trace.rb
43
+ require 'objspace'
44
+
45
+ ObjectSpace.trace_object_allocations_start
46
+ ```
47
+
48
+ Now make sure this command is loaded before you run your script, you can use Ruby's `-I` to specify a load path and `-r` to specify a library to require, in this case our trace file
49
+
50
+ ```
51
+ $ ruby -I ./ -r trace script_name.rb
52
+ ```
53
+
54
+ 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.
55
+
56
+ ## Digging into a Generation
57
+
58
+ You can drill down into a specific generation. In the previous example, the 17'th generation looks strangely large, you can drill into it:
40
59
 
41
60
 
42
61
  ```
@@ -54,6 +73,12 @@ $ heapy read tmp/2015-10-01T10:18:59-05:00-heap.dump 17
54
73
  92200 /app/vendor/bundle/ruby/2.2.0/gems/activesupport-4.2.3/lib/active_support/core_ext/numeric/conversions.rb:131
55
74
  ```
56
75
 
76
+ If you want to read all generations you can use the "all" directive
77
+
78
+ ```
79
+ $ heapy read tmp/2015-10-01T10:18:59-05:00-heap.dump all
80
+ ```
81
+
57
82
  You can also use T-Lo's online JS based [Heap Analyzer](http://tenderlove.github.io/heap-analyzer/) for visualizations.
58
83
 
59
84
  ## Development
@@ -19,12 +19,12 @@ $ heapy read <file|command> <number>
19
19
  When run with only a file, it will output the generation and count pairs:
20
20
 
21
21
  $ heapy read tmp/2015-09-30-heap.dump
22
- Generation: 0 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
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
28
 
29
29
  When run with a file and a number it will output detailed information for that
30
30
  generation:
@@ -69,38 +69,61 @@ HALP
69
69
  @filename = filename
70
70
  end
71
71
 
72
- def drill_down(generation)
73
- puts ""
74
- puts "Analyzing Heap (Generation: #{generation})"
75
- puts "-------------------------------"
76
- puts ""
77
-
78
- generation = Integer(generation)
79
-
80
- #
81
- memsize_hash = Hash.new { |h, k| h[k] = 0 }
82
- count_hash = Hash.new { |h, k| h[k] = 0 }
72
+ def read
83
73
  File.open(@filename) do |f|
84
74
  f.each_line do |line|
85
75
  begin
86
76
  parsed = JSON.parse(line)
87
- if parsed["generation"] == generation
88
- key = "#{ parsed["file"] }:#{ parsed["line"] }"
89
- memsize_hash[key] += parsed["memsize"]
90
- count_hash[key] += 1
91
- end
77
+ yield parsed
92
78
  rescue JSON::ParserError
93
79
  puts "Could not parse #{line}"
94
80
  end
95
81
  end
96
82
  end
83
+ end
84
+
85
+ def drill_down(generation_to_inspect)
86
+ puts ""
87
+ puts "Analyzing Heap (Generation: #{generation_to_inspect})"
88
+ puts "-------------------------------"
89
+ puts ""
90
+
91
+ generation_to_inspect = Integer(generation_to_inspect) unless generation_to_inspect == "all"
92
+
93
+ #
94
+ memsize_hash = Hash.new { |h, k| h[k] = 0 }
95
+ count_hash = Hash.new { |h, k| h[k] = 0 }
96
+ string_count = Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = 0 } }
97
+
98
+ reference_hash = Hash.new { |h, k| h[k] = 0 }
99
+
100
+ read do |parsed|
101
+ generation = parsed["generation"] || 0
102
+ if generation_to_inspect == "all".freeze || generation == generation_to_inspect
103
+ next unless parsed["file"]
104
+
105
+ key = "#{ parsed["file"] }:#{ parsed["line"] }"
106
+ memsize_hash[key] += parsed["memsize"] || 0
107
+ count_hash[key] += 1
108
+
109
+ if parsed["type"] == "STRING".freeze
110
+ string_count[parsed["value"]][key] += 1 if parsed["value"]
111
+ end
112
+
113
+ if parsed["references"]
114
+ reference_hash[key] += parsed["references"].length
115
+ end
116
+ end
117
+ end
118
+
119
+ raise "not a valid Generation: #{generation_to_inspect.inspect}" if memsize_hash.empty?
97
120
 
98
121
  total_memsize = memsize_hash.inject(0){|count, (k, v)| count += v}
99
122
 
100
123
  # /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}}]}
101
124
  puts "allocated by memory (#{total_memsize}) (in bytes)"
102
125
  puts "=============================="
103
- memsize_hash = memsize_hash.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }
126
+ memsize_hash = memsize_hash.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.first(50)
104
127
  longest = memsize_hash.first[1].to_s.length
105
128
  memsize_hash.each do |file_line, memsize|
106
129
  puts " #{memsize.to_s.rjust(longest)} #{file_line}"
@@ -110,37 +133,66 @@ HALP
110
133
 
111
134
  puts ""
112
135
  puts "object count (#{total_count})"
113
- puts "============"
114
- count_hash = count_hash.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }
136
+ puts "=============================="
137
+ count_hash = count_hash.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.first(50)
115
138
  longest = count_hash.first[1].to_s.length
116
139
  count_hash.each do |file_line, memsize|
117
140
  puts " #{memsize.to_s.rjust(longest)} #{file_line}"
118
141
  end
142
+
143
+ puts ""
144
+ puts "High Ref Counts"
145
+ puts "=============================="
146
+ puts ""
147
+
148
+ reference_hash = reference_hash.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.first(50)
149
+ longest = count_hash.first[1].to_s.length
150
+
151
+ reference_hash.each do |file_line, count|
152
+ puts " #{count.to_s.rjust(longest)} #{file_line}"
153
+ end
154
+
155
+ puts ""
156
+ puts "Duplicate strings"
157
+ puts "=============================="
158
+ puts ""
159
+ value_count = {}
160
+
161
+ string_count.each do |string, location_count_hash|
162
+ value_count[string] = location_count_hash.values.inject(&:+)
163
+ end
164
+
165
+ value_count = value_count.sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.first(50)
166
+ longest = value_count.first[1].to_s.length
167
+
168
+ value_count.each do |string, c1|
169
+
170
+ puts " #{c1.to_s.rjust(longest)} #{string.inspect}"
171
+ string_count[string].sort {|(k1, v1), (k2, v2)| v2 <=> v1 }.each do |file_line, c2|
172
+ puts " #{c2.to_s.rjust(longest)} #{file_line}"
173
+ end
174
+ puts ""
175
+ end
176
+
119
177
  end
120
178
 
121
179
  def analyze
122
180
  puts ""
123
181
  puts "Analyzing Heap"
124
182
  puts "=============="
183
+ default_key = "nil".freeze
125
184
 
126
185
  # generation number is key, value is count
127
186
  data = Hash.new {|h, k| h[k] = 0 }
128
187
 
129
- File.open(@filename) do |f|
130
- f.each_line do |line|
131
- begin
132
- line = line.chomp
133
- json = JSON.parse(line)
134
- data[json["generation"]||0] += 1
135
- rescue JSON::ParserError
136
- puts "Could not parse #{line}"
137
- end
138
- end
188
+ read do |parsed|
189
+ data[parsed["generation"] || 0] += 1
139
190
  end
140
191
 
141
- data = data.sort
142
- max_length = data.last[0].to_s.length
192
+ data = data.sort {|(k1,v1), (k2,v2)| k1 <=> k2 }
193
+ max_length = [data.last[0].to_s.length, default_key.length].max
143
194
  data.each do |generation, count|
195
+ generation = default_key if generation == 0
144
196
  puts "Generation: #{ generation.to_s.rjust(max_length) } object count: #{ count }"
145
197
  end
146
198
  end
@@ -1,3 +1,3 @@
1
1
  module Heapy
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -0,0 +1,16 @@
1
+ require 'objspace'
2
+
3
+ ObjectSpace.trace_object_allocations_start
4
+
5
+ array = []
6
+ 10_000.times do |x|
7
+ a = "#{x}_foo"
8
+ array << a
9
+ end
10
+
11
+ # GC.start
12
+
13
+ file_name = "/tmp/#{Time.now.to_f}-heap.dump"
14
+ ObjectSpace.dump_all(output: File.open(file_name, 'w'))
15
+
16
+ puts "bin/heapy read #{file_name}"
@@ -0,0 +1,3 @@
1
+ require 'objspace'
2
+
3
+ ObjectSpace.trace_object_allocations_start
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heapy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - schneems
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-15 00:00:00.000000000 Z
11
+ date: 2015-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -74,6 +74,8 @@ files:
74
74
  - heapy.gemspec
75
75
  - lib/heapy.rb
76
76
  - lib/heapy/version.rb
77
+ - scratch.rb
78
+ - trace.rb
77
79
  homepage: https://github.com/schneems/heapy
78
80
  licenses:
79
81
  - MIT
@@ -94,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
96
  version: '0'
95
97
  requirements: []
96
98
  rubyforge_project:
97
- rubygems_version: 2.4.5.1
99
+ rubygems_version: 2.5.0
98
100
  signing_key:
99
101
  specification_version: 4
100
102
  summary: Inspects Ruby heap dumps