2009-05-18 1 views
10

У меня есть множество потоков и приложений обработки данных, которые я иногда должен отслеживать, то есть мне нужно знать, какие файлы они читают. Это в основном помогает упаковывать тестовые окна, но также может быть полезно при отладке.Как я могу обнаружить доступ к файлам в Linux?

Есть ли способ запустить исполняемые файлы таким образом, чтобы создать такой список?

У меня есть две мысли по этому поводу:

  1. Существует команда, что я могу вызвать и команда вызывает мои приложения. Что-то вроде линий GDB. Я называю GDB, даю ему путь к исполняемому файлу и некоторые аргументы, и GDB называет его для меня. Возможно, есть что-то похожее на то, чтобы сказать мне, как используются системные ресурсы.
  2. Возможно, более интересное (но ненужное решение боковой линии).
    1. создать библиотеку под названием libc.so, который реализует FOPEN (и некоторые другие)
    2. изменения LD_LIBRARY_PATH, чтобы указать на новую библиотеку
    3. сделать копию реального libc.so и переименовать FOPEN (nepof, возможно) в редакторе
    4. моя библиотека загружает копию и вызывает переименованную функцию при необходимости для обеспечения функциональности fopen.
    5. вызовите приложение, которое затем вызывает мой прокси-файл.

Вариант № 1, несомненно, будет предпочтительным, но комментарии о том, как сделать # 2 более легко радушны слишком.

+0

см. Также http://stackoverflow.com/q/2972765/119790 –

ответ

13

Одним из вариантов является использование Трассирования:

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 отдельно, так как внутренний вызов libc из функции fopen() для открытия() не подключен библиотекой LD_PRELOAD.

Аналогичная оговорка применима к strace - есть также sysalla 'openat', и в зависимости от вашей архитектуры могут быть и другие устаревшие системные вызовы. Но не так много, как с крючками LD_PRELOAD, поэтому, если вы не против удара производительности, это может быть более простой вариант.

4
man strace 

пример (предположит, что 2343 является идентификатор процесса):

# logging part 
strace -p 2343 -ff -o strace_log.txt 

# displaying part 
grep ^open strace_log.txt 
2

Что я использую что-то вроде:

strace -o file.txt ./command 

Вы можете

cat file.txt | grep open 

, чтобы получить список всех файлов, открытых в программе.