查找Ruby在运行程序时打开的所有当前文件

时间:2017-02-01 20:22:43

标签: ruby metaprogramming

这是一个深奥的请求,对我来说只是理论上的兴趣(换句话说,我没有在生产代码中使用它)。我想知道在运行ruby程序时是否有可能找出所涉及的所有文件。也就是说,文件require d在执行期间由所述程序执行(或者以其他方式涉及,例如在程序中途由Rails autoload编辑)。

我知道可以使用ObjectSpace并循环遍历该对象,除了File及其后代以外的所有对象,当我禁用垃圾收集时,我在此方法上取得了部分成功(否则列表会收缩)在随机时间,可以理解)。但是,在程序开始时执行此操作会忽略其中间所需的那些文件,因此我可以在最后运行相同的代码,并获得所述文件的并集?

3 个答案:

答案 0 :(得分:3)

lsof的

在Linux上,您可以使用lsof

puts `lsof -a -p #{Process.pid}`

它为您提供当前进程打开的文件保持

该列表远未完成:一旦执行此行,就会读取,解析,执行和关闭许多文件。

lsof扫描打开的文件时,它们不再出现在列表中。

strace的

strace(也在Linux上)是一个很棒的工具,可以实现您想要实现的目标(请参阅此thread)。它会提供很多(可能太多)信息:

strace -e trace=open -o opened_files.txt ruby hello_world.rb
#=> Hello world

opened_files.txt现在以:

开头
open("/home/ricou/.rvm/rubies/ruby-2.3.1/lib/tls/x86_64/libruby.so.2.3", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/home/ricou/.rvm/rubies/ruby-2.3.1/lib/tls/libruby.so.2.3", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/home/ricou/.rvm/rubies/ruby-2.3.1/lib/x86_64/libruby.so.2.3", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/home/ricou/.rvm/rubies/ruby-2.3.1/lib/libruby.so.2.3", O_RDONLY|O_CLOEXEC) = 3
open("/home/ricou/.rvm/rubies/ruby-2.3.1/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/home/ricou/.rvm/rubies/ruby-2.3.1/lib/libpthread.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/home/ricou/.rvm/rubies/ruby-2.3.1/lib/libgmp.so.10", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
....

“Hello World”脚本有571行,“rails runner hello_world.rb”脚本有112302(!)。

输出文件有许多“ENOENT(没有这样的文件或目录)”行,所以你可能想要解析和过滤它。

对于macOS和其他* nix,应该有dtrace

答案 1 :(得分:2)

几个月前,我做的完全一样。这是我肮脏的小程序:

REQUIRE_FILE = ENV.fetch('REQUIRE_FILE', '/tmp/requires.txt')

File.open(REQUIRE_FILE, 'w')

Kernel.module_eval do
  alias_method :require_without_benchmark, :require

  def require(name)
    start = Time.now
    begin
      require_without_benchmark(name)
    ensure
      time = Time.now - start
      File.write(REQUIRE_FILE, [name, time.to_f].join(',').concat("\n"), mode: 'a')
    end
  end
end

然后将您的程序运行为:

ruby -r ./require-benchmark.rb program.rb

答案 2 :(得分:1)

有关所需文件,请参阅How do I get a list of files that have been `required` in Ruby?。 对于gem的所有依赖项,请参阅How do I find out all the dependencies of a gem?

常量$LOADED_FEATURES包含所有文件的数组,其中包含带有require的路径,不仅包括gem本身,还包含所有涉及的文件。 在脚本末尾尝试$LOADED_FEATURES.dup.uniq

对于File打开的普通文件,你可以用KARASZI在答案中显示的方式覆盖IO.open和/或File.open以及姐妹方法read,binread等。