tool/lib/leakchecker.rb: show the code location that allocated leaked fd

by using ObjectSpace.trace_object_allocations.

`make test-all LEAK_CHECKER_TRACE_OBJECT_ALLOCATION=true` will print not
only leaked fds but also where it was created.
This commit is contained in:
Yusuke Endoh 2019-11-12 01:30:42 +09:00
parent b72eda3c59
commit c02de30efb
2 changed files with 29 additions and 16 deletions

View File

@ -75,9 +75,13 @@ class LeakChecker
} }
fd_leaked.each {|fd| fd_leaked.each {|fd|
str = ''.dup str = ''.dup
pos = nil
if h[fd] if h[fd]
str << ' :' str << ' :'
h[fd].map {|io, autoclose, inspect| h[fd].map {|io, autoclose, inspect|
if ENV["LEAK_CHECKER_TRACE_OBJECT_ALLOCATION"]
pos = "#{ObjectSpace.allocation_sourcefile(io)}:#{ObjectSpace.allocation_sourceline(io)}"
end
s = ' ' + inspect s = ' ' + inspect
s << "(not-autoclose)" if !autoclose s << "(not-autoclose)" if !autoclose
s s
@ -86,6 +90,7 @@ class LeakChecker
} }
end end
puts "Leaked file descriptor: #{test_name}: #{fd}#{str}" puts "Leaked file descriptor: #{test_name}: #{fd}#{str}"
puts " The IO was created at #{pos}" if pos
} }
#system("lsof -p #$$") if !fd_leaked.empty? #system("lsof -p #$$") if !fd_leaked.empty?
h.each {|fd, list| h.each {|fd, list|

View File

@ -941,28 +941,36 @@ module MiniTest
leakchecker = LeakChecker.new leakchecker = LeakChecker.new
assertions = filtered_test_methods.map { |method| continuation = proc do
inst = suite.new method assertions = filtered_test_methods.map { |method|
inst._assertions = 0 inst = suite.new method
inst._assertions = 0
print "#{suite}##{method} = " if @verbose print "#{suite}##{method} = " if @verbose
start_time = Time.now if @verbose start_time = Time.now if @verbose
result = inst.run self result = inst.run self
print "%.2f s = " % (Time.now - start_time) if @verbose print "%.2f s = " % (Time.now - start_time) if @verbose
print result print result
puts if @verbose puts if @verbose
$stdout.flush $stdout.flush
unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # compiler process is wrongly considered as leak unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # compiler process is wrongly considered as leak
leakchecker.check("#{inst.class}\##{inst.__name__}") leakchecker.check("#{inst.class}\##{inst.__name__}")
end end
inst._assertions inst._assertions
} }
return assertions.size, assertions.inject(0) { |sum, n| sum + n }
end
return assertions.size, assertions.inject(0) { |sum, n| sum + n } if ENV["LEAK_CHECKER_TRACE_OBJECT_ALLOCATION"]
require "objspace"
ObjectSpace.trace_object_allocations(&continuation)
else
continuation.call
end
end end
## ##