Skip to content

Commit 79eb655

Browse files
committed
image-based gadgets: Add top file
Signed-off-by: Jose Blanquicet <[email protected]>
1 parent 78058fb commit 79eb655

File tree

4 files changed

+237
-0
lines changed

4 files changed

+237
-0
lines changed

gadgets/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ GADGETS = \
1616
trace_tcpconnect \
1717
trace_tcpdrop \
1818
trace_tcpretrans \
19+
top_file \
1920
snapshot_process \
2021
snapshot_socket \
2122
ci/sched_cls_drop \

gadgets/top_file/gadget.yaml

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: file
2+
description: Periodically report read/write activity by file
3+
toppers:
4+
file:
5+
mapName: stats
6+
structName: file_stat
7+
structs:
8+
file_stat:
9+
fields:
10+
- name: pid
11+
description: Process ID
12+
attributes:
13+
template: pid
14+
- name: tid
15+
description: Thread ID
16+
attributes:
17+
hidden: true
18+
template: pid
19+
- name: comm
20+
description: Command name
21+
attributes:
22+
template: comm
23+
- name: reads
24+
description: Reads by file
25+
attributes:
26+
width: 8
27+
- name: writes
28+
description: Writes by file
29+
attributes:
30+
width: 8
31+
- name: rbytes
32+
description: Bytes read by file
33+
attributes:
34+
width: 8
35+
- name: wbytes
36+
description: Bytes written by file
37+
attributes:
38+
width: 8
39+
- name: t
40+
description: Type of file, 'R' for regular files, 'S' for sockets and 'O' for
41+
others (including pipes). By default only regular files are shown; use the
42+
--all-files option to show all file types.
43+
attributes:
44+
width: 1
45+
- name: file
46+
description: File name
47+
attributes:
48+
width: 30
49+
- name: mntns_id
50+
description: Mount namespace ID
51+
attributes:
52+
template: ns
53+
ebpfParams:
54+
all_files:
55+
key: all-files
56+
defaultValue: "false"
57+
description: Trace all files types. By default, only regular files are traced
58+
target_pid:
59+
key: pid
60+
defaultValue: "0"
61+
description: Show only events generated by process with this PID

gadgets/top_file/program.bpf.c

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2+
/* Copyright (c) 2021 Hengqi Chen */
3+
/* Copyright (c) 2023-2024 The Inspektor Gadget authors */
4+
5+
#include <vmlinux.h>
6+
#include <bpf/bpf_helpers.h>
7+
#include <bpf/bpf_core_read.h>
8+
#include <bpf/bpf_tracing.h>
9+
10+
#include <gadget/mntns_filter.h>
11+
#include <gadget/types.h>
12+
#include <gadget/macros.h>
13+
14+
#include "stat.h"
15+
16+
#define PATH_MAX 4096
17+
#define TASK_COMM_LEN 16
18+
19+
enum op {
20+
READ,
21+
WRITE,
22+
};
23+
24+
enum type {
25+
R, // Regular file
26+
S, // Socket
27+
O, // Other (including pipes)
28+
};
29+
30+
struct file_id {
31+
__u64 inode;
32+
__u32 dev;
33+
__u32 pid;
34+
__u32 tid;
35+
};
36+
37+
struct file_stat {
38+
gadget_mntns_id mntns_id;
39+
__u64 reads;
40+
__u64 rbytes;
41+
__u64 writes;
42+
__u64 wbytes;
43+
__u32 pid;
44+
__u32 tid;
45+
__u8 file[PATH_MAX];
46+
__u8 comm[TASK_COMM_LEN];
47+
enum type t;
48+
};
49+
50+
#define MAX_ENTRIES 10240
51+
52+
const volatile pid_t target_pid = 0;
53+
GADGET_PARAM(target_pid);
54+
55+
// By default, only regular files are traced
56+
const volatile bool all_files = false;
57+
GADGET_PARAM(all_files);
58+
59+
static struct file_stat zero_value = {};
60+
61+
struct {
62+
__uint(type, BPF_MAP_TYPE_HASH);
63+
__uint(max_entries, MAX_ENTRIES);
64+
__type(key, struct file_id);
65+
__type(value, struct file_stat);
66+
} stats SEC(".maps");
67+
68+
GADGET_TOPPER(file, stats);
69+
70+
static void get_file_path(struct file *file, __u8 *buf, size_t size)
71+
{
72+
struct qstr dname;
73+
74+
dname = BPF_CORE_READ(file, f_path.dentry, d_name);
75+
bpf_probe_read_kernel(buf, size, dname.name);
76+
}
77+
78+
static int probe_entry(struct pt_regs *ctx, struct file *file, size_t count,
79+
enum op op)
80+
{
81+
__u64 pid_tgid = bpf_get_current_pid_tgid();
82+
__u32 pid = pid_tgid >> 32;
83+
__u32 tid = (__u32)pid_tgid;
84+
int mode;
85+
struct file_id key = {};
86+
struct file_stat *valuep;
87+
u64 mntns_id;
88+
89+
if (target_pid && target_pid != pid)
90+
return 0;
91+
92+
mntns_id = gadget_get_mntns_id();
93+
94+
if (gadget_should_discard_mntns_id(mntns_id))
95+
return 0;
96+
97+
mode = BPF_CORE_READ(file, f_inode, i_mode);
98+
if (!all_files && !S_ISREG(mode))
99+
return 0;
100+
101+
key.dev = BPF_CORE_READ(file, f_inode, i_rdev);
102+
key.inode = BPF_CORE_READ(file, f_inode, i_ino);
103+
key.pid = pid;
104+
key.tid = tid;
105+
valuep = bpf_map_lookup_elem(&stats, &key);
106+
if (!valuep) {
107+
bpf_map_update_elem(&stats, &key, &zero_value, BPF_ANY);
108+
valuep = bpf_map_lookup_elem(&stats, &key);
109+
if (!valuep)
110+
return 0;
111+
valuep->pid = pid;
112+
valuep->tid = tid;
113+
valuep->mntns_id = mntns_id;
114+
bpf_get_current_comm(&valuep->comm, sizeof(valuep->comm));
115+
get_file_path(file, valuep->file, sizeof(valuep->file));
116+
if (S_ISREG(mode)) {
117+
valuep->t = R;
118+
} else if (S_ISSOCK(mode)) {
119+
valuep->t = S;
120+
} else {
121+
valuep->t = O;
122+
}
123+
}
124+
if (op == READ) {
125+
valuep->reads++;
126+
valuep->rbytes += count;
127+
} else { /* op == WRITE */
128+
valuep->writes++;
129+
valuep->wbytes += count;
130+
}
131+
return 0;
132+
};
133+
134+
SEC("kprobe/vfs_read")
135+
int BPF_KPROBE(ig_topfile_rd_e, struct file *file, char *buf, size_t count,
136+
loff_t *pos)
137+
{
138+
return probe_entry(ctx, file, count, READ);
139+
}
140+
141+
SEC("kprobe/vfs_write")
142+
int BPF_KPROBE(ig_topfile_wr_e, struct file *file, const char *buf,
143+
size_t count, loff_t *pos)
144+
{
145+
return probe_entry(ctx, file, count, WRITE);
146+
}
147+
148+
char LICENSE[] SEC("license") = "Dual BSD/GPL";

gadgets/top_file/stat.h

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2+
#ifndef __STAT_H
3+
#define __STAT_H
4+
5+
/* From include/uapi/linux/stat.h */
6+
7+
#define S_IFMT 00170000
8+
#define S_IFSOCK 0140000
9+
#define S_IFLNK 0120000
10+
#define S_IFREG 0100000
11+
#define S_IFBLK 0060000
12+
#define S_IFDIR 0040000
13+
#define S_IFCHR 0020000
14+
#define S_IFIFO 0010000
15+
#define S_ISUID 0004000
16+
#define S_ISGID 0002000
17+
#define S_ISVTX 0001000
18+
19+
#define S_ISLNK(m) (((m)&S_IFMT) == S_IFLNK)
20+
#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
21+
#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
22+
#define S_ISCHR(m) (((m)&S_IFMT) == S_IFCHR)
23+
#define S_ISBLK(m) (((m)&S_IFMT) == S_IFBLK)
24+
#define S_ISFIFO(m) (((m)&S_IFMT) == S_IFIFO)
25+
#define S_ISSOCK(m) (((m)&S_IFMT) == S_IFSOCK)
26+
27+
#endif /* __STAT_H */

0 commit comments

Comments
 (0)