Skip to content

Instantly share code, notes, and snippets.

@tmm1
Last active December 28, 2015 21:09
Show Gist options
  • Save tmm1/7562494 to your computer and use it in GitHub Desktop.
Save tmm1/7562494 to your computer and use it in GitHub Desktop.
# encoding: utf-8
require 'set'
require 'json'
require 'pp'
class HeapDump
def initialize(data)
@objects = data.split("\n").map.with_index{ |line, idx|
begin
JSON.parse(line)
rescue JSON::ParserError => e
STDERR.puts "*** Parse error on line #{idx+1}: #{e.inspect}"
end
}.compact
by_addr = by_address
@objects.each do |o|
addr = o['address']
next unless o['references']
o['references'].each do |ref|
if refd = by_addr[ref]
(refd['parents'] ||= []) << addr
end
end
end
end
attr_reader :objects
def count_files(with_line=false)
objects.inject(Hash.new(0)) do |hash, o|
key = with_line ? "#{o['file']}:#{o['line']}" : o['file']
hash[key] += 1
hash
end.sort_by{ |k,v| -v }
end
def count_objects
objects.inject(Hash.new(0)) do |hash, o|
hash[o['type']] += 1
hash
end
end
def classes
@classes ||= objects.inject(Hash.new) do |hash, o|
hash[o['address']] = o['name'] if o.key?('name')
hash
end
end
def count_instances
Hash[
*objects.inject(Hash.new(0)) do |hash, o|
class_name = classes[o['class']]
hash[class_name] += 1
hash
end.select{ |k,v| v > 1 }.sort_by{ |k,v| v }.flatten(1)
]
end
def by_address
@by_address ||= objects.inject(Hash.new) do |hash, obj|
hash[obj['address']] = obj
hash
end
end
def display_object(obj, level)
addr, type = obj.values_at('address', 'type')
addr, type = obj.values_at('type', 'root') if type == 'ROOT'
type = obj['node_type'] if type == 'NODE'
printf "%s (%s %s)", " "*(level*2), addr, type
case type
when 'STRING'
printf " %s: %s", classes[obj['class']], obj['value'].inspect
when 'CLASS', 'MODULE'
printf " %s", obj['name']
when 'ARRAY', 'HASH'
printf " %s: length:%d", classes[obj['class']] || '(hidden)', obj['size'] || obj['length']
when 'DATA'
printf " %s (struct %s)", classes[obj['class']] || '(hidden)', obj['struct'] || 'unknown'
end
puts
end
def traverse_up(obj, level=0, seen=Set.new)
addr = obj['address']
display_object(obj, level)
seen << addr
parents = (obj['parents'] || []).map{ |ref| by_address[ref] }
parents = parents.select{ |o| o['type'] == 'ROOT' } if toplevel?(obj)
parents.each do |o|
next if seen.include?(o['address'])
traverse_up(o, level+1, seen)
end
#seen.merge parents.map{ |o| o['address'] }
end
def toplevel?(obj)
obj['type'] == 'CLASS' && %w[Object Hash Array String].include?(obj['name'])
end
def traverse_down(obj, level=0, seen=Set.new)
return if seen.include?(obj['address'])
display_object(obj, level)
seen << obj['address']
(obj['references'] || []).each do |addr|
if ref = by_address[addr]
traverse_down(ref, level+1, seen)
end
end
end
end
dump = HeapDump.new(ARGF.read)
pp dump.count_files(true)
#p dump.count_objects
#p dump.count_instances
#dump.objects.select{ |o| o['type'] == "STRING" && o['value'] == '[' }.each do |obj|
# dump.traverse_up(obj)
#end
#dump.traverse_down(dump.by_address['0x7fbc8b02a0e0'])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment