Skip to content

Instantly share code, notes, and snippets.

@nazarhussain
Created May 19, 2017 18:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nazarhussain/e2f95518cc7e236afbbb501f1e00fbe4 to your computer and use it in GitHub Desktop.
Save nazarhussain/e2f95518cc7e236afbbb501f1e00fbe4 to your computer and use it in GitHub Desktop.
Ruby Heap Diff
require 'set'
require 'json'
if ARGV.length != 2
puts "Usage: detect_leaks [FIRST.json] [SECOND.json]"
exit 1
end
first_addrs = Set.new
second_addrs = Set.new
# Get a list of memory addresses from the first dump
File.open(ARGV[0], "r").each_line do |line|
parsed = JSON.parse(line)
first_addrs << parsed["address"] if parsed && parsed["address"]
end
# Get a list of memory addresses from the last dump
File.open(ARGV[1], "r").each_line do |line|
parsed = JSON.parse(line)
second_addrs << parsed["address"] if parsed && parsed["address"]
end
diff = []
# Get a list of all items present in both the second and
# third dumps but not in the first.
File.open(ARGV[1], "r").each_line do |line|
parsed = JSON.parse(line)
if parsed && parsed["address"]
diff << parsed unless first_addrs.include?(parsed["address"])
end
end
# Group items
diff.group_by do |x|
[x["type"], x["file"], x["line"]]
end.map do |x,y|
# Collect memory size
[x, y.count, y.inject(0){|sum,i| sum + (i['bytesize'] || 0) }, y.inject(0){|sum,i| sum + (i['memsize'] || 0) }]
end.sort do |a,b|
b[1] <=> a[1]
end.each do |x,y,bytesize,memsize|
# Output information about each potential leak
puts "Leaked #{y} #{x[0]} objects of size #{bytesize}/#{memsize} at: #{x[1]}:#{x[2]}"
end
# Also output total memory usage, because why not?
memsize = diff.inject(0){|sum,i| sum + (i['memsize'] || 0) }
bytesize = diff.inject(0){|sum,i| sum + (i['bytesize'] || 0) }
puts "\n\nTotal Diff: #{diff.count}"
puts "\n\nTotal Size: #{bytesize}/#{memsize}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment