|
24 | 24 |
|
25 | 25 | # arguments |
26 | 26 | examples = """examples: |
27 | | - ./filetop # file I/O top, 1 second refresh |
28 | | - ./filetop -C # don't clear the screen |
29 | | - ./filetop -p 181 # PID 181 only |
30 | | - ./filetop -d /home/user # trace files in /home/user directory only |
31 | | - ./filetop 5 # 5 second summaries |
32 | | - ./filetop 5 10 # 5 second summaries, 10 times only |
33 | | - ./filetop 5 --read-only # 5 second summaries, only read operations traced |
34 | | - ./filetop 5 --write-only # 5 second summaries, only write operations traced |
| 27 | + ./filetop # file I/O top, 1 second refresh |
| 28 | + ./filetop -C # don't clear the screen |
| 29 | + ./filetop -p 181 # PID 181 only |
| 30 | + ./filetop -d /home/user # trace files in /home/user directory only |
| 31 | + ./filetop -d /home/user -R # trace files in /home/user and subdirectories |
| 32 | + ./filetop 5 # 5 second summaries |
| 33 | + ./filetop 5 10 # 5 second summaries, 10 times only |
| 34 | + ./filetop 5 --read-only # 5 second summaries, only read operations traced |
| 35 | + ./filetop 5 --write-only # 5 second summaries, only write operations traced |
35 | 36 | """ |
36 | 37 | parser = argparse.ArgumentParser( |
37 | 38 | description="File reads and writes by process", |
|
60 | 61 | help=argparse.SUPPRESS) |
61 | 62 | parser.add_argument("-d", "--directory", type=str, |
62 | 63 | help="trace this directory only") |
| 64 | +parser.add_argument("-R", "--recursive", action="store_true", |
| 65 | + help="when used with -d, also trace files in subdirectories") |
63 | 66 |
|
64 | 67 | args = parser.parse_args() |
65 | 68 | interval = int(args.interval) |
|
180 | 183 | if args.directory: |
181 | 184 | try: |
182 | 185 | directory_inode = os.lstat(args.directory)[stat.ST_INO] |
183 | | - print(f'Tracing directory: {args.directory} (Inode: {directory_inode})') |
184 | | - bpf_text = bpf_text.replace('DIRECTORY_FILTER', 'file->f_path.dentry->d_parent->d_inode->i_ino != %d' % directory_inode) |
| 186 | + if args.recursive: |
| 187 | + print(f'Tracing directory recursively: {args.directory} (Inode: {directory_inode})') |
| 188 | + directory_filter = ("({ struct dentry *pde = de; int found = 0; " |
| 189 | + "for (int i = 0; i < 50; i++) { " |
| 190 | + "if (!pde->d_parent) break; pde = pde->d_parent; " |
| 191 | + "if (pde->d_inode->i_ino == %d) { found = 1; break; } } " |
| 192 | + "!found; })") % directory_inode |
| 193 | + else: |
| 194 | + print(f'Tracing directory: {args.directory} (Inode: {directory_inode})') |
| 195 | + directory_filter = "file->f_path.dentry->d_parent->d_inode->i_ino != %d" % directory_inode |
| 196 | + |
| 197 | + bpf_text = bpf_text.replace('DIRECTORY_FILTER', directory_filter) |
185 | 198 | except (FileNotFoundError, PermissionError) as e: |
186 | 199 | print(f'Error accessing directory {args.directory}: {e}') |
187 | 200 | exit(1) |
188 | 201 | else: |
| 202 | + if args.recursive: |
| 203 | + print("Error: --recursive can only be used with -d/--directory option") |
| 204 | + exit(1) |
189 | 205 | bpf_text = bpf_text.replace('DIRECTORY_FILTER', '0') |
190 | 206 |
|
191 | 207 | if debug or args.ebpf: |
|
0 commit comments