selftests/bpf: Add test for bpf_iter_task_vma
The test dumps information similar to /proc/pid/maps. The first line of the output is compared against the /proc file to make sure they match. Signed-off-by: Song Liu <songliubraving@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Yonghong Song <yhs@fb.com> Link: https://lore.kernel.org/bpf/20210212183107.50963-4-songliubraving@fb.com
This commit is contained in:
Родитель
3d06f34aa8
Коммит
e8168840e1
|
@ -7,6 +7,7 @@
|
|||
#include "bpf_iter_task.skel.h"
|
||||
#include "bpf_iter_task_stack.skel.h"
|
||||
#include "bpf_iter_task_file.skel.h"
|
||||
#include "bpf_iter_task_vma.skel.h"
|
||||
#include "bpf_iter_task_btf.skel.h"
|
||||
#include "bpf_iter_tcp4.skel.h"
|
||||
#include "bpf_iter_tcp6.skel.h"
|
||||
|
@ -64,6 +65,22 @@ free_link:
|
|||
bpf_link__destroy(link);
|
||||
}
|
||||
|
||||
static int read_fd_into_buffer(int fd, char *buf, int size)
|
||||
{
|
||||
int bufleft = size;
|
||||
int len;
|
||||
|
||||
do {
|
||||
len = read(fd, buf, bufleft);
|
||||
if (len > 0) {
|
||||
buf += len;
|
||||
bufleft -= len;
|
||||
}
|
||||
} while (len > 0);
|
||||
|
||||
return len < 0 ? len : size - bufleft;
|
||||
}
|
||||
|
||||
static void test_ipv6_route(void)
|
||||
{
|
||||
struct bpf_iter_ipv6_route *skel;
|
||||
|
@ -177,7 +194,7 @@ static int do_btf_read(struct bpf_iter_task_btf *skel)
|
|||
{
|
||||
struct bpf_program *prog = skel->progs.dump_task_struct;
|
||||
struct bpf_iter_task_btf__bss *bss = skel->bss;
|
||||
int iter_fd = -1, len = 0, bufleft = TASKBUFSZ;
|
||||
int iter_fd = -1, err;
|
||||
struct bpf_link *link;
|
||||
char *buf = taskbuf;
|
||||
int ret = 0;
|
||||
|
@ -190,14 +207,7 @@ static int do_btf_read(struct bpf_iter_task_btf *skel)
|
|||
if (CHECK(iter_fd < 0, "create_iter", "create_iter failed\n"))
|
||||
goto free_link;
|
||||
|
||||
do {
|
||||
len = read(iter_fd, buf, bufleft);
|
||||
if (len > 0) {
|
||||
buf += len;
|
||||
bufleft -= len;
|
||||
}
|
||||
} while (len > 0);
|
||||
|
||||
err = read_fd_into_buffer(iter_fd, buf, TASKBUFSZ);
|
||||
if (bss->skip) {
|
||||
printf("%s:SKIP:no __builtin_btf_type_id\n", __func__);
|
||||
ret = 1;
|
||||
|
@ -205,7 +215,7 @@ static int do_btf_read(struct bpf_iter_task_btf *skel)
|
|||
goto free_link;
|
||||
}
|
||||
|
||||
if (CHECK(len < 0, "read", "read failed: %s\n", strerror(errno)))
|
||||
if (CHECK(err < 0, "read", "read failed: %s\n", strerror(errno)))
|
||||
goto free_link;
|
||||
|
||||
CHECK(strstr(taskbuf, "(struct task_struct)") == NULL,
|
||||
|
@ -1133,6 +1143,92 @@ static void test_buf_neg_offset(void)
|
|||
bpf_iter_test_kern6__destroy(skel);
|
||||
}
|
||||
|
||||
#define CMP_BUFFER_SIZE 1024
|
||||
static char task_vma_output[CMP_BUFFER_SIZE];
|
||||
static char proc_maps_output[CMP_BUFFER_SIZE];
|
||||
|
||||
/* remove \0 and \t from str, and only keep the first line */
|
||||
static void str_strip_first_line(char *str)
|
||||
{
|
||||
char *dst = str, *src = str;
|
||||
|
||||
do {
|
||||
if (*src == ' ' || *src == '\t')
|
||||
src++;
|
||||
else
|
||||
*(dst++) = *(src++);
|
||||
|
||||
} while (*src != '\0' && *src != '\n');
|
||||
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
static void test_task_vma(void)
|
||||
{
|
||||
int err, iter_fd = -1, proc_maps_fd = -1;
|
||||
struct bpf_iter_task_vma *skel;
|
||||
int len, read_size = 4;
|
||||
char maps_path[64];
|
||||
|
||||
skel = bpf_iter_task_vma__open();
|
||||
if (CHECK(!skel, "bpf_iter_task_vma__open", "skeleton open failed\n"))
|
||||
return;
|
||||
|
||||
skel->bss->pid = getpid();
|
||||
|
||||
err = bpf_iter_task_vma__load(skel);
|
||||
if (CHECK(err, "bpf_iter_task_vma__load", "skeleton load failed\n"))
|
||||
goto out;
|
||||
|
||||
skel->links.proc_maps = bpf_program__attach_iter(
|
||||
skel->progs.proc_maps, NULL);
|
||||
|
||||
if (CHECK(IS_ERR(skel->links.proc_maps), "bpf_program__attach_iter",
|
||||
"attach iterator failed\n")) {
|
||||
skel->links.proc_maps = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
iter_fd = bpf_iter_create(bpf_link__fd(skel->links.proc_maps));
|
||||
if (CHECK(iter_fd < 0, "create_iter", "create_iter failed\n"))
|
||||
goto out;
|
||||
|
||||
/* Read CMP_BUFFER_SIZE (1kB) from bpf_iter. Read in small chunks
|
||||
* to trigger seq_file corner cases. The expected output is much
|
||||
* longer than 1kB, so the while loop will terminate.
|
||||
*/
|
||||
len = 0;
|
||||
while (len < CMP_BUFFER_SIZE) {
|
||||
err = read_fd_into_buffer(iter_fd, task_vma_output + len,
|
||||
min(read_size, CMP_BUFFER_SIZE - len));
|
||||
if (CHECK(err < 0, "read_iter_fd", "read_iter_fd failed\n"))
|
||||
goto out;
|
||||
len += err;
|
||||
}
|
||||
|
||||
/* read CMP_BUFFER_SIZE (1kB) from /proc/pid/maps */
|
||||
snprintf(maps_path, 64, "/proc/%u/maps", skel->bss->pid);
|
||||
proc_maps_fd = open(maps_path, O_RDONLY);
|
||||
if (CHECK(proc_maps_fd < 0, "open_proc_maps", "open_proc_maps failed\n"))
|
||||
goto out;
|
||||
err = read_fd_into_buffer(proc_maps_fd, proc_maps_output, CMP_BUFFER_SIZE);
|
||||
if (CHECK(err < 0, "read_prog_maps_fd", "read_prog_maps_fd failed\n"))
|
||||
goto out;
|
||||
|
||||
/* strip and compare the first line of the two files */
|
||||
str_strip_first_line(task_vma_output);
|
||||
str_strip_first_line(proc_maps_output);
|
||||
|
||||
CHECK(strcmp(task_vma_output, proc_maps_output), "compare_output",
|
||||
"found mismatch\n");
|
||||
out:
|
||||
close(proc_maps_fd);
|
||||
close(iter_fd);
|
||||
bpf_iter_task_vma__destroy(skel);
|
||||
}
|
||||
|
||||
void test_bpf_iter(void)
|
||||
{
|
||||
if (test__start_subtest("btf_id_or_null"))
|
||||
|
@ -1149,6 +1245,8 @@ void test_bpf_iter(void)
|
|||
test_task_stack();
|
||||
if (test__start_subtest("task_file"))
|
||||
test_task_file();
|
||||
if (test__start_subtest("task_vma"))
|
||||
test_task_vma();
|
||||
if (test__start_subtest("task_btf"))
|
||||
test_task_btf();
|
||||
if (test__start_subtest("tcp4"))
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define bpf_iter__netlink bpf_iter__netlink___not_used
|
||||
#define bpf_iter__task bpf_iter__task___not_used
|
||||
#define bpf_iter__task_file bpf_iter__task_file___not_used
|
||||
#define bpf_iter__task_vma bpf_iter__task_vma___not_used
|
||||
#define bpf_iter__tcp bpf_iter__tcp___not_used
|
||||
#define tcp6_sock tcp6_sock___not_used
|
||||
#define bpf_iter__udp bpf_iter__udp___not_used
|
||||
|
@ -26,6 +27,7 @@
|
|||
#undef bpf_iter__netlink
|
||||
#undef bpf_iter__task
|
||||
#undef bpf_iter__task_file
|
||||
#undef bpf_iter__task_vma
|
||||
#undef bpf_iter__tcp
|
||||
#undef tcp6_sock
|
||||
#undef bpf_iter__udp
|
||||
|
@ -67,6 +69,12 @@ struct bpf_iter__task_file {
|
|||
struct file *file;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct bpf_iter__task_vma {
|
||||
struct bpf_iter_meta *meta;
|
||||
struct task_struct *task;
|
||||
struct vm_area_struct *vma;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct bpf_iter__bpf_map {
|
||||
struct bpf_iter_meta *meta;
|
||||
struct bpf_map *map;
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
#include "bpf_iter.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
/* Copied from mm.h */
|
||||
#define VM_READ 0x00000001
|
||||
#define VM_WRITE 0x00000002
|
||||
#define VM_EXEC 0x00000004
|
||||
#define VM_MAYSHARE 0x00000080
|
||||
|
||||
/* Copied from kdev_t.h */
|
||||
#define MINORBITS 20
|
||||
#define MINORMASK ((1U << MINORBITS) - 1)
|
||||
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
|
||||
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
|
||||
|
||||
#define D_PATH_BUF_SIZE 1024
|
||||
char d_path_buf[D_PATH_BUF_SIZE] = {};
|
||||
__u32 pid = 0;
|
||||
|
||||
SEC("iter/task_vma") int proc_maps(struct bpf_iter__task_vma *ctx)
|
||||
{
|
||||
struct vm_area_struct *vma = ctx->vma;
|
||||
struct seq_file *seq = ctx->meta->seq;
|
||||
struct task_struct *task = ctx->task;
|
||||
struct file *file;
|
||||
char perm_str[] = "----";
|
||||
|
||||
if (task == (void *)0 || vma == (void *)0)
|
||||
return 0;
|
||||
|
||||
file = vma->vm_file;
|
||||
if (task->tgid != pid)
|
||||
return 0;
|
||||
perm_str[0] = (vma->vm_flags & VM_READ) ? 'r' : '-';
|
||||
perm_str[1] = (vma->vm_flags & VM_WRITE) ? 'w' : '-';
|
||||
perm_str[2] = (vma->vm_flags & VM_EXEC) ? 'x' : '-';
|
||||
perm_str[3] = (vma->vm_flags & VM_MAYSHARE) ? 's' : 'p';
|
||||
BPF_SEQ_PRINTF(seq, "%08llx-%08llx %s ", vma->vm_start, vma->vm_end, perm_str);
|
||||
|
||||
if (file) {
|
||||
__u32 dev = file->f_inode->i_sb->s_dev;
|
||||
|
||||
bpf_d_path(&file->f_path, d_path_buf, D_PATH_BUF_SIZE);
|
||||
|
||||
BPF_SEQ_PRINTF(seq, "%08llx ", vma->vm_pgoff << 12);
|
||||
BPF_SEQ_PRINTF(seq, "%02x:%02x %u", MAJOR(dev), MINOR(dev),
|
||||
file->f_inode->i_ino);
|
||||
BPF_SEQ_PRINTF(seq, "\t%s\n", d_path_buf);
|
||||
} else {
|
||||
BPF_SEQ_PRINTF(seq, "%08llx 00:00 0\n", 0ULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
Загрузка…
Ссылка в новой задаче