forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Log caller for each Thread.new for
rake spec
MSP-11147
- Loading branch information
1 parent
96990fd
commit 097aa33
Showing
3 changed files
with
84 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
require 'metasploit/framework/spec/threads/suite' | ||
|
||
original_thread_new = Thread.method(:new) | ||
|
||
# Patches `Thread.new` so that if logs `caller` so thread leaks can be traced | ||
Thread.define_singleton_method(:new) { |*args, &block| | ||
lines = ['BEGIN Thread.new caller'] | ||
|
||
caller.each do |frame| | ||
lines << " #{frame}" | ||
end | ||
|
||
lines << 'END Thread.new caller' | ||
|
||
Metasploit::Framework::Spec::Threads::Suite::LOG_PATHNAME.parent.mkpath | ||
|
||
Metasploit::Framework::Spec::Threads::Suite::LOG_PATHNAME.open('a') { |f| | ||
# single puts so threads can't write in between each other. | ||
f.puts lines.join("\n") | ||
} | ||
|
||
original_thread_new.call(*args, &block) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,65 @@ | ||
module Metasploit::Framework::Spec::Threads::Suite | ||
# | ||
# CONSTANTS | ||
# | ||
|
||
EXPECTED_THREAD_COUNT_BEFORE_SUITE = 1 | ||
|
||
# | ||
# Module Methods | ||
# | ||
|
||
# Configures `before(:suite)` and `after(:suite)` callback to detect thread leaks. | ||
# | ||
# @return [void] | ||
def self.configure! | ||
unless @configured | ||
RSpec.configure do |config| | ||
config.before(:suite) do | ||
thread_count = Thread.list.count | ||
|
||
expect(thread_count).to( | ||
(be <= EXPECTED_THREAD_COUNT_BEFORE_SUITE), | ||
"#{thread_count} #{'thread'.pluralize(thread_count)} exist(s) when " \ | ||
"only #{EXPECTED_THREAD_COUNT_BEFORE_SUITE} #{'thread'.pluralize(EXPECTED_THREAD_COUNT_BEFORE_SUITE)} " \ | ||
"expected before suite runs" | ||
) | ||
require 'pathname' | ||
|
||
# @note needs to use explicit nesting. so this file can be loaded directly without loading 'metasploit/framework' which | ||
# allows for faster loading of rake tasks. | ||
module Metasploit | ||
module Framework | ||
module Spec | ||
module Threads | ||
module Suite | ||
# | ||
# CONSTANTS | ||
# | ||
|
||
# Number of allowed threads when threads are counted in `before(:suite)` | ||
EXPECTED_THREAD_COUNT_BEFORE_SUITE = 1 | ||
# `caller` for all Thread.new calls | ||
LOG_PATHNAME = Pathname.new('log/metasploit/framework/spec/threads/suite.log') | ||
|
||
# | ||
# Module Methods | ||
# | ||
|
||
# Configures `before(:suite)` and `after(:suite)` callback to detect thread leaks. | ||
# | ||
# @return [void] | ||
def self.configure! | ||
unless @configured | ||
RSpec.configure do |config| | ||
config.before(:suite) do | ||
thread_count = Thread.list.count | ||
|
||
# check with if first so that error message can be constructed lazily | ||
if thread_count > EXPECTED_THREAD_COUNT_BEFORE_SUITE | ||
log = LOG_PATHNAME.read() | ||
|
||
raise RuntimeError, | ||
"#{thread_count} #{'thread'.pluralize(thread_count)} exist(s) when " \ | ||
"only #{EXPECTED_THREAD_COUNT_BEFORE_SUITE} " \ | ||
"#{'thread'.pluralize(EXPECTED_THREAD_COUNT_BEFORE_SUITE)} expected before suite runs:\n#{log}" | ||
end | ||
end | ||
end | ||
|
||
@configured = true | ||
end | ||
|
||
@configured | ||
end | ||
|
||
def self.define_task | ||
Rake::Task.define_task('metasploit:framework:spec:threads:suite') do | ||
parent_pathname = Pathname.new(__FILE__).parent | ||
threads_logger_pathname = parent_pathname.join('logger') | ||
load_pathname = parent_pathname.parent.parent.parent.parent.expand_path | ||
|
||
ENV['RUBYOPT'] = "-I#{load_pathname} -r#{threads_logger_pathname} #{ENV['RUBYOPT']}" | ||
end | ||
|
||
Rake::Task.define_task(spec: 'metasploit:framework:spec:threads:suite') | ||
end | ||
end | ||
end | ||
|
||
@configured = true | ||
end | ||
|
||
@configured | ||
end | ||
end |