-
Notifications
You must be signed in to change notification settings - Fork 4k
tools/filetop: Add --recursive for directory filter #5400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,18 +20,22 @@ | |
| import argparse | ||
| import os | ||
| import stat | ||
| import re | ||
| from subprocess import call | ||
|
|
||
| MAX_DENTRY_DEPTH_VAL = 32 | ||
|
|
||
| # arguments | ||
| examples = """examples: | ||
| ./filetop # file I/O top, 1 second refresh | ||
| ./filetop -C # don't clear the screen | ||
| ./filetop -p 181 # PID 181 only | ||
| ./filetop -d /home/user # trace files in /home/user directory only | ||
| ./filetop 5 # 5 second summaries | ||
| ./filetop 5 10 # 5 second summaries, 10 times only | ||
| ./filetop 5 --read-only # 5 second summaries, only read operations traced | ||
| ./filetop 5 --write-only # 5 second summaries, only write operations traced | ||
| examples = f"""examples: | ||
| ./filetop # file I/O top, 1 second refresh | ||
| ./filetop -C # don't clear the screen | ||
| ./filetop -p 181 # PID 181 only | ||
| ./filetop -d /home/user # trace files in /home/user directory only | ||
| ./filetop -d /home/user -R # trace files in /home/user and subdirectories (max depth {MAX_DENTRY_DEPTH_VAL}) | ||
| ./filetop 5 # 5 second summaries | ||
| ./filetop 5 10 # 5 second summaries, 10 times only | ||
| ./filetop 5 --read-only # 5 second summaries, only read operations traced | ||
| ./filetop 5 --write-only # 5 second summaries, only write operations traced | ||
| """ | ||
| parser = argparse.ArgumentParser( | ||
| description="File reads and writes by process", | ||
|
|
@@ -60,6 +64,8 @@ | |
| help=argparse.SUPPRESS) | ||
| parser.add_argument("-d", "--directory", type=str, | ||
| help="trace this directory only") | ||
| parser.add_argument("-R", "--recursive", action="store_true", | ||
| help=f"when used with -d, also trace files in subdirectories (max depth {MAX_DENTRY_DEPTH_VAL})") | ||
|
|
||
| args = parser.parse_args() | ||
| interval = int(args.interval) | ||
|
|
@@ -80,6 +86,28 @@ | |
| #undef DNAME_INLINE_LEN | ||
| #define DNAME_INLINE_LEN __BCC_DNAME_INLINE_LEN | ||
| // Limit dentry parent walk depth to satisfy eBPF verifier loop | ||
| #ifndef MAX_DENTRY_DEPTH | ||
| #define MAX_DENTRY_DEPTH MAX_DENTRY_DEPTH_VALUE | ||
| #endif | ||
| static __always_inline int not_under_inode(struct dentry *de, unsigned long target_ino) | ||
| { | ||
| struct dentry *pde = de; | ||
| int found = 0; | ||
| #pragma unroll | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a blank line after the variable declaration section. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is |
||
| for (int i = 0; i < MAX_DENTRY_DEPTH; i++) { | ||
| if (!pde->d_parent) | ||
| break; | ||
| pde = pde->d_parent; | ||
| if (pde->d_inode && pde->d_inode->i_ino == target_ino) { | ||
| found = 1; | ||
| break; | ||
| } | ||
| } | ||
| return !found; | ||
| } | ||
| // the key for the output summary | ||
| struct info_t { | ||
| unsigned long inode; | ||
|
|
@@ -169,6 +197,9 @@ | |
| } | ||
| """ | ||
| bpf_text = bpf_text.replace('MAX_DENTRY_DEPTH_VALUE', str(MAX_DENTRY_DEPTH_VAL)) | ||
| m = re.search(r'#define\s+MAX_DENTRY_DEPTH\s+(\d+)', bpf_text) | ||
| MAX_DENTRY_DEPTH = int(m.group(1)) if m else 32 | ||
| if args.tgid: | ||
| bpf_text = bpf_text.replace('TGID_FILTER', 'tgid != %d' % args.tgid) | ||
| else: | ||
|
|
@@ -180,12 +211,24 @@ | |
| if args.directory: | ||
| try: | ||
| directory_inode = os.lstat(args.directory)[stat.ST_INO] | ||
| print(f'Tracing directory: {args.directory} (Inode: {directory_inode})') | ||
| bpf_text = bpf_text.replace('DIRECTORY_FILTER', 'file->f_path.dentry->d_parent->d_inode->i_ino != %d' % directory_inode) | ||
| if args.recursive: | ||
| print( | ||
| f'Tracing directory recursively: {args.directory} ' | ||
| f'(Inode: {directory_inode}, max depth: {MAX_DENTRY_DEPTH_VAL})' | ||
| ) | ||
| directory_filter = "not_under_inode(de, %d)" % directory_inode | ||
| else: | ||
| print(f'Tracing directory: {args.directory} (Inode: {directory_inode})') | ||
| directory_filter = "de->d_parent->d_inode->i_ino != %d" % directory_inode | ||
|
|
||
| bpf_text = bpf_text.replace('DIRECTORY_FILTER', directory_filter) | ||
| except (FileNotFoundError, PermissionError) as e: | ||
| print(f'Error accessing directory {args.directory}: {e}') | ||
| exit(1) | ||
| else: | ||
| if args.recursive: | ||
| print("Error: --recursive can only be used with -d/--directory option") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about |
||
| exit(1) | ||
| bpf_text = bpf_text.replace('DIRECTORY_FILTER', '0') | ||
|
|
||
| if debug or args.ebpf: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to make it clearer that this is only available when using -d. The current wording makes it seem like there could be cases where it's possible to use without -d.
For example, "This feature is only supported when used with the -d option, tracing files inside subdirectories."