Skip to content

Instantly share code, notes, and snippets.

@ydkn
Last active March 13, 2019 08:00
Show Gist options
  • Save ydkn/11dc3458377aa93f7cb900dfea5d25d1 to your computer and use it in GitHub Desktop.
Save ydkn/11dc3458377aa93f7cb900dfea5d25d1 to your computer and use it in GitHub Desktop.
Debugging Shoryuken Memory Leak (https://github.com/phstc/shoryuken/issues/401)
#!/usr/bin/env ruby
require 'set'
require 'json'
# find dumps
args = Dir.glob('/tmp/heap_*.dump').sort
args = [args[0], args[args.length / 2], args[-1]]
if args.nil? || args.length < 3
puts "NOT ENOUGH DUMPS"
exit 1
end
puts args.inspect
first_addrs = Set.new
third_addrs = Set.new
# Get a list of memory addresses from the first dump
File.open(args[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(args[2], "r").each_line do |line|
parsed = JSON.parse(line)
third_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(args[1], "r").each_line do |line|
parsed = JSON.parse(line)
if parsed && parsed["address"]
if !first_addrs.include?(parsed["address"]) && third_addrs.include?(parsed["address"])
diff << parsed
end
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 Size: #{bytesize}/#{memsize}"
# ... normal content
Thread.new do
require 'objspace'
ObjectSpace.trace_object_allocations_start
sleep 10
while true do
filename = "/tmp/heap_#{Time.now.to_i}.dump"
GC.start
f = ObjectSpace.dump_all(output: File.open(filename, 'w'))
f.close
puts "DUMPED: #{fname}"
sleep 60
end
end
concurrency: 32
delay: 30
queues:
- [queue1, 6]
- [queue2, 9]
- [queue3, 1]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment