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

yk9xbfzb  于 2023-11-18  发布在  Ruby
关注(0)|答案(4)|浏览(173)

这是一个深奥的请求,对我来说只是理论上的兴趣(换句话说,我没有在生产代码中使用这个)。我想知道是否有可能找出运行ruby程序时涉及的所有文件。也就是说,在执行过程中,该程序涉及的文件require d(或其他方式,例如在程序中途由Rails执行的autoload ed)。
我知道可以使用ObjectSpace并循环遍历对象,排除除File及其后代之外的所有对象,并且在禁用垃圾收集时,我使用此方法取得了部分成功(否则列表会随机收缩,这是可以理解的)。然而,在程序开始时这样做会忽略中间需要的那些文件,所以我可以在最后运行相同的代码,并获得所述文件的联合?

pobjuy32

pobjuy321#

几个月前我也做了同样的事情。下面是我的小程序:

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

dauxcl2d

dauxcl2d2#

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)
....


它有571行用于“Hello World”脚本,112302(!)用于“rails runner hello_world.rb”脚本。
输出文件有许多“ENOENT(No such file or directory)”行,因此您可能需要对其进行解析和过滤。
对于macOS和其他 *nix,应该有dtrace

hrysbysz

hrysbysz3#

有关所需文件,请参阅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打开的普通文件,你可以覆盖IO.open和/或File.open以及姐妹方法read,binread等,就像Karaszi在他的回答中所展示的那样。

8ljdwjyq

8ljdwjyq4#

基于KARAZI的答案,这里有一个显示文件路径的版本:

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
      path = ''
      $:.each do |p|
        if File.exists? File.join(p, name)
          path = File.join(p, name)
          break
        end
      end
      File.write(REQUIRE_FILE, [name, path, time.to_f].join(',').concat("\n"), mode: 'a')
    end
  end
end

字符串
然后运行你的程序:

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

相关问题