适用于 Linux 的 opensnoop

这是另一个我认为不可能的工具:在我的 Linux 系统上打开了哪些文件?

./opensnoop

跟踪 open()s。Ctrl-C 结束。
通讯 PID FD 文件
[…]
sshd 6503 -1 /usr/share/ssh/blacklist.RSA-2048
sshd 6503 -1 /etc/ssh/blacklist.RSA-2048
sshd 6503 0x4 /root/.ssh/authorized_keys
sshd 6503 -1 /var/run/nologin
sshd 6503 -1 /etc/nologin
sshd 6503 0x4 /etc/passwd
sshd 6503 0x4 /etc/shadow
sshd 6503 0x4 /etc/localtime
sshd 6503 0x4 /etc/security/capability.conf
sshd 6503 0x4 /etc/login.defs
[…]
这是在系统范围内跟踪所有进程,并使用比strace更有效的跟踪器。
只包含“conf”的文件怎么样,看看哪些配置文件被主动打开?

./opensnoop 配置

跟踪包含“conf”的文件名的 open()s。Ctrl-C 结束。
通讯 PID FD 文件
运行 19107 0x3 /etc/nsswitch.conf
状态 19111 0x3 /etc/nsswitch.conf
webapp 19119 0x4 /home/webapp/3.7/rel/conf/logging.conf
webapp 19119 0x7 /etc/nsswitch.conf
网络应用程序 19119 0x7 /etc/host.conf
webapp 19119 0x7 /etc/resolv.conf
webapp 19119 0x8 /home/webapp/3.7/rel/conf/application.properties
catalina.sh 19107 0x3 /etc/nsswitch.conf
运行 19122 0x3 /etc/nsswitch.conf
[…]
好的!这就是我的 webapp 配置文件所在的位置…
以“log”结尾的文件怎么样?

./opensnoop ‘log$’

跟踪包含“log$”的文件名的 open()s。Ctrl-C 结束。
通讯 PID FD 文件
webapp 21144 0x4 /var/tmp/webapp/3.7/stash/webapp.log
webapp 21159 0x4 /var/tmp/webapp/3.7/stash/webapp.log
webapp 21174 0x4 /var/tmp/webapp/3.7/stash/webapp.log
bash 21301 0x4 /var/log/lastlog
[…]
Oh, no wonder I couldn’t find them, I wasn’t looking in /var/tmp!..
And open()s that returned an error (eg, file not found)?

./opensnoop -x

Tracing open()s. Ctrl-C to end.
COMM PID FD FILE
smtp 2690 -1 /usr/lib/tls/x86_64/libnss_db.so.2
smtp 2690 -1 /usr/lib/tls/libnss_db.so.2
smtp 2690 -1 /usr/lib/x86_64/libnss_db.so.2
smtp 2690 -1 /usr/lib/libnss_db.so.2
java 4680 -1 /home/tomcat/conf/Standalone/localhost
java 4680 -1 /home/tomcat/conf/Standalone/localhost
java 4680 -1 /home/tomcat/conf/Standalone/localhost
[…]
Catching a process hunt around its library path is normal, like we see here for smtp. But I wonder if that java app was supposed to be finding that file…
Options
opensnoop options are summarized by the USAGE message (there’s also a man page and examples file):

./opensnoop -h

USAGE: opensnoop [-htx] [-d secs] [-p PID] [-n name] [filename]
-d seconds # trace duration, and use buffers
-n name # process name to match on I/O issue
-p PID # PID to match on I/O issue
-t # include time (seconds)
-x # only show failed opens
-h # this usage message
filename # match filename (partials, REs, ok)
eg,
opensnoop # watch open()s live (unbuffered)
opensnoop -d 1 # trace 1 sec (buffered)
opensnoop -p 181 # trace I/O issued by PID 181 only
opensnoop conf # trace filenames containing “conf”
opensnoop ‘log$’ # filenames ending in “log”
The -p option employs an in-kernel filter for efficiency.
opensnoop traces events as they happen, which for very frequent open()s can begin to cost measurable overhead. The “-d” mode buffers and prints the buffer at the end, reducing overheads if needed.
What, Why, and How
This is another ftrace-based hack for my perf-tools collection.
有一些很好的方法可以在 Linux 上使用诸如 perf_events(“perf”命令)和内核调试信息的跟踪器,或者使用 SystemTap 或 ktap 来实现 opensnoop。但我的目标是现在在一组运行 Linux 3.2 且没有内核调试信息的 AWS EC2 云实例上使用它(在这个动态环境中到处安装是不切实际的,因为它可能是 100 兆字节)。
without-debuginfo 要求使这变得特别棘手。一个问题是我不认为 ftrace(或 perf_events)能够取消引用字符串,只看到带有十六进制地址的示例。事实证明他们确实有这种能力,事实上,opensnoop 可以是一些单行代码:

perf probe --add ‘do_sys_open filename:string’

[…]

perf record --no-buffering -e probe:do_sys_open -o - -a | PAGER=cat perf 脚本 -i -

多日志 19075 [001] 5586.323974:探针:do_sys_open:(ffffffff811af8b0)文件名字符串 =“。”
多日志 19075 [001] 5586.324013:探针:do_sys_open:(ffffffff811af8b0)filename_string=“./main”
多日志 19075 [001] 5586.324028:探针:do_sys_open:(ffffffff811af8b0)文件名字符串=“锁定”
snmpd 1255 [000] 5586.576142:探针:do_sys_open:(ffffffff811af8b0)文件名字符串=“/proc/net/dev”
snmpd 1255 [000] 5586.576278:探针:do_sys_open:(ffffffff811af8b0)文件名字符串=“/proc/net/if_inet6”
[…]

perf probe --del do_sys_open

这是内核函数 do_sys_open() 的动态跟踪示例,并将参数(文件名)作为字符串检查。除了这需要内核调试信息来识别“文件名”符号。
请注意,在旧系统(包括 Linux 3.2)上,使用 -D 代替 --no-buffering。
如果没有内核调试信息,我可以使用 CPU 寄存器而不是符号来制作类似的单线。我一直在努力让字符串来处理这个问题,并开始认为这是不可能的,然后在perf-users mailing list上寻求帮助。幸运的是,这是可能的!
我最终没有在 do_sys_open() 的条目上使用寄存器,而是跟踪 getname() 的返回值(do_sys_open() 调用),并根据内核版本将其处理为 char * 或 struct filename *。这是一个脆弱的 hack,但我认为这比在不同平台上猜测 do_sys_open() 的寄存器要好一些。
结论
跟踪 open() 可以告诉你很多关于运行应用程序的信息:它们的配置、日志和数据文件的位置,以及文件打开错误。这是我流行的 opensnoop 工具的 Linux 端口,我多年来一直使用它来帮助进行性能分析和故障排除。
这也是旧 Linux 内核(如iosnoop)使用现有 ftrace 和 kprobes 跟踪框架的另一个概念证明。如果你碰巧有内核调试信息,你也可以只使用我上面包含的单行代码。警告适用:opensnoop 和那些单行程序在 Linux 上使用动态跟踪,过去曾出现过内核恐慌错误,所以要知道你在做什么,先测试,使用风险自负。

转载翻译自: https://www.brendangregg.com