Skip to content

Commit 6c63a7d

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

File tree

1 file changed

+54
-11
lines changed

1 file changed

+54
-11
lines changed

tools/filetop.py

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,22 @@
2020
import argparse
2121
import os
2222
import stat
23+
import re
2324
from subprocess import call
2425

26+
MAX_DENTRY_DEPTH_VAL = 32
27+
2528
# arguments
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
29+
examples = f"""examples:
30+
./filetop # file I/O top, 1 second refresh
31+
./filetop -C # don't clear the screen
32+
./filetop -p 181 # PID 181 only
33+
./filetop -d /home/user # trace files in /home/user directory only
34+
./filetop -d /home/user -R # trace files in /home/user and subdirectories (max depth {MAX_DENTRY_DEPTH_VAL})
35+
./filetop 5 # 5 second summaries
36+
./filetop 5 10 # 5 second summaries, 10 times only
37+
./filetop 5 --read-only # 5 second summaries, only read operations traced
38+
./filetop 5 --write-only # 5 second summaries, only write operations traced
3539
"""
3640
parser = argparse.ArgumentParser(
3741
description="File reads and writes by process",
@@ -60,6 +64,8 @@
6064
help=argparse.SUPPRESS)
6165
parser.add_argument("-d", "--directory", type=str,
6266
help="trace this directory only")
67+
parser.add_argument("-R", "--recursive", action="store_true",
68+
help=f"when used with -d, also trace files in subdirectories (max depth {MAX_DENTRY_DEPTH_VAL})")
6369

6470
args = parser.parse_args()
6571
interval = int(args.interval)
@@ -80,6 +86,28 @@
8086
#undef DNAME_INLINE_LEN
8187
#define DNAME_INLINE_LEN __BCC_DNAME_INLINE_LEN
8288
89+
// Limit dentry parent walk depth to satisfy eBPF verifier loop
90+
#ifndef MAX_DENTRY_DEPTH
91+
#define MAX_DENTRY_DEPTH MAX_DENTRY_DEPTH_VALUE
92+
#endif
93+
94+
static __always_inline int not_under_inode(struct dentry *de, unsigned long target_ino)
95+
{
96+
struct dentry *pde = de;
97+
int found = 0;
98+
#pragma unroll
99+
for (int i = 0; i < MAX_DENTRY_DEPTH; i++) {
100+
if (!pde->d_parent)
101+
break;
102+
pde = pde->d_parent;
103+
if (pde->d_inode && pde->d_inode->i_ino == target_ino) {
104+
found = 1;
105+
break;
106+
}
107+
}
108+
return !found;
109+
}
110+
83111
// the key for the output summary
84112
struct info_t {
85113
unsigned long inode;
@@ -169,6 +197,9 @@
169197
}
170198
171199
"""
200+
bpf_text = bpf_text.replace('MAX_DENTRY_DEPTH_VALUE', str(MAX_DENTRY_DEPTH_VAL))
201+
m = re.search(r'#define\s+MAX_DENTRY_DEPTH\s+(\d+)', bpf_text)
202+
MAX_DENTRY_DEPTH = int(m.group(1)) if m else 32
172203
if args.tgid:
173204
bpf_text = bpf_text.replace('TGID_FILTER', 'tgid != %d' % args.tgid)
174205
else:
@@ -180,12 +211,24 @@
180211
if args.directory:
181212
try:
182213
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)
214+
if args.recursive:
215+
print(
216+
f'Tracing directory recursively: {args.directory} '
217+
f'(Inode: {directory_inode}, max depth: {MAX_DENTRY_DEPTH_VAL})'
218+
)
219+
directory_filter = "not_under_inode(de, %d)" % directory_inode
220+
else:
221+
print(f'Tracing directory: {args.directory} (Inode: {directory_inode})')
222+
directory_filter = "de->d_parent->d_inode->i_ino != %d" % directory_inode
223+
224+
bpf_text = bpf_text.replace('DIRECTORY_FILTER', directory_filter)
185225
except (FileNotFoundError, PermissionError) as e:
186226
print(f'Error accessing directory {args.directory}: {e}')
187227
exit(1)
188228
else:
229+
if args.recursive:
230+
print("Error: --recursive can only be used with -d/--directory option")
231+
exit(1)
189232
bpf_text = bpf_text.replace('DIRECTORY_FILTER', '0')
190233

191234
if debug or args.ebpf:

0 commit comments

Comments
 (0)