跟踪文件的加载和类的定义:
这里跟踪文件的加载的方法是将系统的require和load方法定义别名方法,然后重写require方法和load方法,来记录文件的加载。
跟踪类的定义呢是给Object定义inherited(有新类继承这个类是自动调用,参数是子类的类名)这个钩子方法来记录。
classtrace.rb:
#encoding: utf-8
module ClassTrace
T = [] #定义数组常量T,存放trace信息
if x = ARGV.index("--traceout") #如果ruby命令后面有--traceout参数,则记录到文件中,否则输出
OUT = File.open(ARGV[x + 1], 'w')
ARGV[x, 2] = nil
else
OUT = STDERR
end
end
alias origin_require require #给require定义别名方法origin_require,下面要重新定义
alias origin_load load #给load方法定义别名方法origin_load,下面要重新定义load方法
def require(file)
ClassTrace::T << [file, caller[0]] #将require方式加载的文件和加载该文件的位置放入一个数组,并添加到T常量中
origin_require(file) #require加载文件
end
def load(*args)
ClassTrace::T << [args[0], caller[0]] #将load方式加载的文件和加载该文件的位置放入一个数组
origin_load(*args) #load加载文件
end
def Object.inherited(c) #定义钩子方法inherited方法,当有新的类定义时调用此方法将类名和定义的位置加入到T常量
ClassTrace::T << [c, caller[0]]
end
at_exit{ #当程序退出执行时
o = ClassTrace::OUT
o.puts '=' * 60
o.puts 'Files Loaded and Classes Defined:'
o.puts '=' * 60
ClassTrace::T.each do |what, where| #遍历trace信息数组T,将信息写入到文件或输出
if what.is_a? Class
o.puts "Defined: #{what.ancestors.join('<-')} at #{where}"
else
o.puts "Loaded: #{what} at #{where}"
end
end
}
index.rb
#encoding: utf-8
require './classtrace'
require './test'
class Test; end
puts "11111"
test.rb
#encoding: utf-8
class TestClass; end
ruby index.rb --traceout /tmp/trace #将index.rb文件中加载的文件和定义的类的trace信息写入到/tmp/trace文件,像这样:
============================================================
Files Loaded and Classes Defined:
============================================================
Loaded: ./test at index.rb:3:in `<main>'
Defined: TestClass<-Object<-Kernel<-BasicObject at /Users/fangxiang/work/ruby/test/test.rb:2:in `<top (required)>'
Defined: Test<-Object<-Kernel<-BasicObject at index.rb:4:in `<main>'
跟踪方法的调用:
这里跟踪方法的调用用的办法就是给Object定义一个名为trace!和一个名为untrace!的方法,trace!方法用来重新定义一个跟原方法同名的方法,在里面加入trace信息,然后使用super调用原来的方法返回结果。untrace!方法使用remove_method来取消我们定义的方法。
test.rb内容如下:
class Object def trace!(*methods)
@_traced ||= [] #已经被跟踪的方法数组
if methods.empty?
methods = public_methods(false) #如果methods参数没有传,默认跟踪所有的public方法
else
methods &= public_methods(false) #如果传了methods参数,为了确保它是当前对象的方法,跟对象所有的public方法做交集
end
methods.map! &:to_sym #将数组每个值转换成symbol类型
methods -= @_traced #去掉那些已经被跟踪的方法
@_traced |= methods #将被跟踪的方法
STDERR << "Tracing: #{methods.join(', ')} on #{ObjectSpace._id2ref(object_id).inspect}\n" #输出跟踪的trace信息
eigenclass = class << self; self; end
methods.each do |m|
eigenclass.class_eval %Q{
def #{m}(*args, &block) #重新定义跟踪的方法,里面加入跟踪信息,然后使用super调用原来的方法返回结果
STDERR << "Entering: #{m}(\#{args.join(', ')})\n"
result = super
STDERR << "Exiting: #{m} with \#{result}\n"
result
rescue
STDERR << "Aborting: #{m}: \#{$!.class}: \#{$!.message}\n"
raise
end
}
end
end def untrace!(*methods)
if methods.size == 0
methods = @_traced
else
methods.map! &:to_sym
methods &= @_traced
end
STDERR << "Untracing: #{methods.join(', ')} on #{ObjectSpace._id2ref(object_id).inspect}\n"
@_traced -= methods
(class << self; self; end).class_eval do
methods.each do |m|
remove_method m #取消我们定义的跟踪方法
end
end
remove_instance_variable(:@_traced) if @_traced.empty?
end
end
str = "fxhover"
str.trace!(:reverse, :split, :chop, :strip)
puts str.reverse
puts str.strip
str.untrace!(:strip)
puts str.strip
输出:
Tracing: reverse, split, chop, strip on "fxhover"
Entering: reverse()
Exiting: reverse with revohxf
revohxf
Entering: strip()
Exiting: strip with fxhover
fxhover
Untracing: strip on "fxhover"
fxhover