我有一堆流程和数据处理应用程序,我偶尔需要监视,这意味着我需要知道他们读取什么文件.这主要是为了帮助打包测试用例,但在调试时也很有用.
我有两个想法:
>有一个我可以调用的命令,该命令调用我的应用程序.沿着GDB线的东西.我调用GDB,给它一个可执行文件和一些参数的路径,GDB为我调用它.也许有类似的东西告诉我如何使用系统资源.
>也许更有趣(但不必要的侧路)解决方案.
>创建库libc.so实现fopen(和其他)
>将LD_LIBRARY_PATH更改为指向新库
制作真实libc.so的副本,并在编辑器中重命名fopen(nepof,也许)
>我的库加载副本,并根据需要调用重命名的函数来提供fopen功能.
>调用该应用程序,然后调用我的代理fopen.
第一选择肯定会是最好的选择,但是如何更容易地做#2的评论也是欢迎的.
解决方法
一个选择是使用strace:
strace -o logfile -eopen yourapp
这将记录所有文件打开的事件,但它会强加一个可能会显着的性能损失.它具有易于使用的优点.
另一个选项是使用LD_PRELOAD.这对应于您的选项#2.基本的想法是做这样的事情:
#define _GNU_SOURCE #include <stdio.h> #include <dlfcn.h> int open(const char *fn,int flags) { static int (*real_open)(const char *fn,int flags); if (!real_open) { real_open = dlsym(RTLD_NEXT,"open"); } fprintf(stderr,"opened file '%s'\n",fn); return real_open(fn,flags); }
然后用:
gcc -fPIC -shared -ldl -o preload-example.so preload-example.c
并运行您的程序,例如:
$LD_PRELOAD=$PWD/preload-example.so cat /dev/null opened file '/dev/null'
这个开销大大减少了.
但是请注意,还有其他打开文件的入口点,例如fopen(),openat()或许多传统兼容性入口点之一:
00000000000747d0 g DF .text 000000000000071c GLIBC_2.2.5 _IO_file_fopen 0000000000068850 g DF .text 000000000000000a GLIBC_2.2.5 fopen 000000000006fe60 g DF .text 00000000000000e2 GLIBC_2.4 open_wmemstream 00000000001209c0 w DF .text 00000000000000ec GLIBC_2.2.5 posix_openpt 0000000000069e50 g DF .text 00000000000003fb GLIBC_2.2.5 _IO_proc_open 00000000000dcf70 g DF .text 0000000000000021 GLIBC_2.7 __open64_2 0000000000068a10 g DF .text 00000000000000f5 GLIBC_2.2.5 fopencookie 000000000006a250 g DF .text 000000000000009b GLIBC_2.2.5 popen 00000000000d7b10 w DF .text 0000000000000080 GLIBC_2.2.5 __open64 0000000000068850 g DF .text 000000000000000a GLIBC_2.2.5 _IO_fopen 00000000000d7e70 w DF .text 0000000000000020 GLIBC_2.7 __openat64_2 00000000000e1ef0 g DF .text 000000000000005b GLIBC_2.2.5 openlog 00000000000d7b10 w DF .text 0000000000000080 GLIBC_2.2.5 open64 0000000000370c10 g DO .bss 0000000000000008 GLIBC_PRIVATE _dl_open_hook 0000000000031680 g DF .text 0000000000000240 GLIBC_2.2.5 catopen 000000000006a250 g DF .text 000000000000009b GLIBC_2.2.5 _IO_popen 0000000000071af0 g DF .text 000000000000026a GLIBC_2.2.5 freopen64 00000000000723a0 g DF .text 0000000000000183 GLIBC_2.2.5 fmemopen 00000000000a44f0 w DF .text 0000000000000088 GLIBC_2.4 fdopendir 00000000000d7e70 g DF .text 0000000000000020 GLIBC_2.7 __openat_2 00000000000a3d00 w DF .text 0000000000000095 GLIBC_2.2.5 opendir 00000000000dcf40 g DF .text 0000000000000021 GLIBC_2.7 __open_2 00000000000d7b10 w DF .text 0000000000000080 GLIBC_2.2.5 __open 0000000000074370 g DF .text 00000000000000d7 GLIBC_2.2.5 _IO_file_open 0000000000070b40 g DF .text 00000000000000d2 GLIBC_2.2.5 open_memstream 0000000000070450 g DF .text 0000000000000272 GLIBC_2.2.5 freopen 00000000000318c0 g DF .text 00000000000008c4 GLIBC_PRIVATE __open_catalog 00000000000d7b10 w DF .text 0000000000000080 GLIBC_2.2.5 open 0000000000067e80 g DF .text 0000000000000332 GLIBC_2.2.5 fdopen 000000000001e9b0 g DF .text 00000000000003f5 GLIBC_2.2.5 iconv_open 00000000000daca0 g DF .text 000000000000067b GLIBC_2.2.5 fts_open 00000000000d7d60 w DF .text 0000000000000109 GLIBC_2.4 openat 0000000000068850 w DF .text 000000000000000a GLIBC_2.2.5 fopen64 00000000000d7d60 w DF .text 0000000000000109 GLIBC_2.4 openat64 00000000000d6490 g DF .text 00000000000000b6 GLIBC_2.2.5 posix_spawn_file_actions_addopen 0000000000121b80 g DF .text 000000000000008a GLIBC_PRIVATE __libc_dlopen_mode 0000000000067e80 g DF .text 0000000000000332 GLIBC_2.2.5 _IO_fdopen
您可能需要将所有这些都完全挂钩 – 至少没有前缀_的应该被钩住.特别是,请务必单独挂接fopen,因为从fopen()到open()的libc内部调用不被LD_PRELOAD库挂接.
类似的警告也适用于strace – 还有’openat’系统调用,并且根据您的体系结构,也可能会有其他旧系统调用.但不如LD_PRELOAD钩子那么多,所以如果你不介意性能打击,这可能是一个更容易的选择.