Skip to content

Commit eb878c4

Browse files
author
abushwang
committed
tools/filetop: Add --recursive for directory filter
Signed-off-by: abushwang <[email protected]>
1 parent 2b73f76 commit eb878c4

File tree

1 file changed

+44
-10
lines changed

1 file changed

+44
-10
lines changed

tools/filetop.py

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@
2424

2525
# arguments
2626
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
3536
"""
3637
parser = argparse.ArgumentParser(
3738
description="File reads and writes by process",
@@ -60,6 +61,8 @@
6061
help=argparse.SUPPRESS)
6162
parser.add_argument("-d", "--directory", type=str,
6263
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")
6366

6467
args = parser.parse_args()
6568
interval = int(args.interval)
@@ -80,6 +83,28 @@
8083
#undef DNAME_INLINE_LEN
8184
#define DNAME_INLINE_LEN __BCC_DNAME_INLINE_LEN
8285
86+
// Limit dentry parent walk depth to satisfy eBPF verifier loop
87+
#ifndef FILETOP_MAX_DENTRY_DEPTH
88+
#define FILETOP_MAX_DENTRY_DEPTH 32
89+
#endif
90+
91+
static __always_inline int not_under_inode(struct dentry *de, unsigned long target_ino)
92+
{
93+
struct dentry *pde = de;
94+
int found = 0;
95+
#pragma unroll
96+
for (int i = 0; i < FILETOP_MAX_DENTRY_DEPTH; i++) {
97+
if (!pde->d_parent)
98+
break;
99+
pde = pde->d_parent;
100+
if (pde->d_inode && pde->d_inode->i_ino == target_ino) {
101+
found = 1;
102+
break;
103+
}
104+
}
105+
return !found;
106+
}
107+
83108
// the key for the output summary
84109
struct info_t {
85110
unsigned long inode;
@@ -180,12 +205,21 @@
180205
if args.directory:
181206
try:
182207
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)
208+
if args.recursive:
209+
print(f'Tracing directory recursively: {args.directory} (Inode: {directory_inode})')
210+
directory_filter = "not_under_inode(de, %d)" % directory_inode
211+
else:
212+
print(f'Tracing directory: {args.directory} (Inode: {directory_inode})')
213+
directory_filter = "de->d_parent->d_inode->i_ino != %d" % directory_inode
214+
215+
bpf_text = bpf_text.replace('DIRECTORY_FILTER', directory_filter)
185216
except (FileNotFoundError, PermissionError) as e:
186217
print(f'Error accessing directory {args.directory}: {e}')
187218
exit(1)
188219
else:
220+
if args.recursive:
221+
print("Error: --recursive can only be used with -d/--directory option")
222+
exit(1)
189223
bpf_text = bpf_text.replace('DIRECTORY_FILTER', '0')
190224

191225
if debug or args.ebpf:

0 commit comments

Comments
 (0)