[RFC PATCH 14/16] selftests/crib: Add test for getting basic information of the process

From: Juntong Deng
Date: Wed Jul 10 2024 - 14:48:22 EST


In this test, the basic information of the process is obtained through
CRIB, including priority, comm, address space, all VMAs, etc.

The process information obtained through CRIB will be compared with the
process information obtained through procfs to verify the correctness of
the information.

In addition, the performance of CRIB and procfs will be compared in this
test, usually CRIB takes only 20-30% of the time of procfs.

The following is an example of test results:
CRIB dump took 0.001349 seconds
PROC dump took 0.005516 seconds

Signed-off-by: Juntong Deng <juntong.deng@xxxxxxxxxxx>
---
tools/testing/selftests/crib/.gitignore | 1 +
tools/testing/selftests/crib/Makefile | 136 +++++++
tools/testing/selftests/crib/config | 7 +
.../selftests/crib/test_dump_task.bpf.c | 125 +++++++
tools/testing/selftests/crib/test_dump_task.c | 337 ++++++++++++++++++
tools/testing/selftests/crib/test_dump_task.h | 90 +++++
6 files changed, 696 insertions(+)
create mode 100644 tools/testing/selftests/crib/.gitignore
create mode 100644 tools/testing/selftests/crib/Makefile
create mode 100644 tools/testing/selftests/crib/config
create mode 100644 tools/testing/selftests/crib/test_dump_task.bpf.c
create mode 100644 tools/testing/selftests/crib/test_dump_task.c
create mode 100644 tools/testing/selftests/crib/test_dump_task.h

diff --git a/tools/testing/selftests/crib/.gitignore b/tools/testing/selftests/crib/.gitignore
new file mode 100644
index 000000000000..378eac25d311
--- /dev/null
+++ b/tools/testing/selftests/crib/.gitignore
@@ -0,0 +1 @@
+build
diff --git a/tools/testing/selftests/crib/Makefile b/tools/testing/selftests/crib/Makefile
new file mode 100644
index 000000000000..9d0553f1ff5c
--- /dev/null
+++ b/tools/testing/selftests/crib/Makefile
@@ -0,0 +1,136 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../../../build/Build.include
+include ../../../scripts/Makefile.arch
+include ../../../scripts/Makefile.include
+include ../lib.mk
+
+CUR_DIR := $(abspath .)
+REPO_ROOT := $(abspath ../../../..)
+TOOLS_DIR := $(REPO_ROOT)/tools
+TOOLSINC_DIR := $(TOOLS_DIR)/include
+BPFTOOL_DIR := $(TOOLS_DIR)/bpf/bpftool
+UAPI_DIR := $(TOOLSINC_DIR)/uapi
+LIB_DIR := $(TOOLS_DIR)/lib
+LIBBPF_DIR := $(LIB_DIR)/bpf
+GEN_DIR := $(REPO_ROOT)/include/generated
+GEN_HDR := $(GEN_DIR)/autoconf.h
+
+OUTPUT_DIR := $(CUR_DIR)/build
+INCLUDE_DIR := $(OUTPUT_DIR)/include
+SBIN_DIR:= $(OUTPUT_DIR)/sbin
+OBJ_DIR := $(OUTPUT_DIR)/obj
+CRIBOBJ_DIR := $(OBJ_DIR)/crib
+LIBBPF_OUTPUT := $(OBJ_DIR)/libbpf/libbpf.a
+LIBBPF_OBJ_DIR := $(OBJ_DIR)/libbpf
+LIBBPF_OBJ := $(LIBBPF_OBJ_DIR)/libbpf.a
+
+DEFAULT_BPFTOOL := $(SBIN_DIR)/bpftool
+BPFTOOL ?= $(DEFAULT_BPFTOOL)
+
+VMLINUX_BTF_PATHS ?= ../../../../vmlinux \
+ /sys/kernel/btf/vmlinux \
+ /boot/vmlinux-$(shell uname -r)
+VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
+ifeq ($(VMLINUX_BTF),)
+$(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)")
+endif
+
+ifneq ($(wildcard $(GEN_HDR)),)
+ GENFLAGS := -DHAVE_GENHDR
+endif
+
+CFLAGS += -g -O2 -rdynamic -pthread -Wall -Werror $(GENFLAGS) \
+ -I$(INCLUDE_DIR) -I$(GEN_DIR) -I$(LIB_DIR) \
+ -I$(TOOLSINC_DIR) -I$(UAPI_DIR) -I$(CUR_DIR)/include \
+ -Wno-unused-command-line-argument
+
+LDFLAGS = -lelf -lz
+
+IS_LITTLE_ENDIAN = $(shell $(CC) -dM -E - </dev/null | \
+ grep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')
+
+define get_sys_includes
+$(shell $(1) -v -E - </dev/null 2>&1 \
+ | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') \
+$(shell $(1) -dM -E - </dev/null | grep '__riscv_xlen ' | awk '{printf("-D__riscv_xlen=%d -D__BITS_PER_LONG=%d", $$3, $$3)}')
+endef
+
+BPF_CFLAGS = -g -D__TARGET_ARCH_$(SRCARCH) \
+ $(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian) \
+ -I$(CUR_DIR)/include -I$(CUR_DIR)/include/bpf-compat \
+ -I$(INCLUDE_DIR) -I$(UAPI_DIR) \
+ -I$(REPO_ROOT)/include \
+ $(call get_sys_includes,$(CLANG)) \
+ -Wall -Wno-compare-distinct-pointer-types \
+ -Wno-incompatible-function-pointer-types \
+ -O2 -mcpu=v3
+
+MAKE_DIRS := $(sort $(OBJ_DIR)/libbpf $(OBJ_DIR)/libbpf \
+ $(OBJ_DIR)/bpftool $(OBJ_DIR)/resolve_btfids \
+ $(INCLUDE_DIR) $(CRIBOBJ_DIR) $(SBIN_DIR))
+
+TEST_GEN_BPF_PROGS_SKEL := $(foreach prog,$(wildcard *.bpf.c),$(INCLUDE_DIR)/$(patsubst %.c,%.skel.h,$(prog)))
+
+TEST_GEN_PROGS := $(addprefix $(SBIN_DIR)/, $(basename $(filter-out $(wildcard *.bpf.c), $(wildcard *.c))))
+
+TEST_GEN_PROGS_OBJ := $(addsuffix .o,$(addprefix $(CRIBOBJ_DIR)/, $(notdir $(TEST_GEN_PROGS))))
+
+$(MAKE_DIRS):
+ $(call msg,MKDIR,,$@)
+ $(Q)mkdir -p $@
+
+$(LIBBPF_OBJ): $(wildcard $(LIBBPF_DIR)/*.[ch] $(LIBBPF_DIR)/Makefile) \
+ $(UAPI_DIR)/linux/bpf.h \
+ | $(OBJ_DIR)/libbpf
+ $(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_DIR) OUTPUT=$(OBJ_DIR)/libbpf/ \
+ EXTRA_CFLAGS='-g -O0 -fPIC' \
+ DESTDIR=$(OUTPUT_DIR) prefix= all install_headers
+
+$(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOL_DIR)/*.[ch] $(BPFTOOL_DIR)/Makefile) \
+ $(LIBBPF_OUTPUT) | $(OBJ_DIR)/bpftool
+ $(Q)$(MAKE) $(submake_extras) -C $(BPFTOOL_DIR) \
+ ARCH= CROSS_COMPILE= CC=$(HOSTCC) LD=$(HOSTLD) \
+ EXTRA_CFLAGS='-g -O0' \
+ OUTPUT=$(OBJ_DIR)/bpftool/ \
+ LIBBPF_OUTPUT=$(OBJ_DIR)/libbpf/ \
+ LIBBPF_DESTDIR=$(OUTPUT_DIR)/ \
+ prefix= DESTDIR=$(OUTPUT_DIR)/ install-bin
+
+$(INCLUDE_DIR)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR)
+ifeq ($(VMLINUX_H),)
+ $(call msg,GEN,,$@)
+ $(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@
+else
+ $(call msg,CP,,$@)
+ $(Q)cp "$(VMLINUX_H)" $@
+endif
+
+$(CRIBOBJ_DIR)/%.bpf.o: %.bpf.c %.h $(INCLUDE_DIR)/vmlinux.h | $(LIBBPF_OBJ) $(CRIBOBJ_DIR)
+ $(call msg,CLANG-BPF,,$(notdir $@))
+ $(Q)$(CLANG) $(BPF_CFLAGS) -target bpf -c $< -o $@
+
+$(INCLUDE_DIR)/%.bpf.skel.h: $(CRIBOBJ_DIR)/%.bpf.o $(INCLUDE_DIR)/vmlinux.h $(BPFTOOL) | $(INCLUDE_DIR)
+ $(call msg,GEN-SKEL,,$(notdir $@))
+ $(Q)$(BPFTOOL) gen skeleton $< > $@
+
+$(TEST_GEN_PROGS_OBJ): $(CRIBOBJ_DIR)/%.o: %.c %.h $(INCLUDE_DIR)/%.bpf.skel.h | $(CRIBOBJ_DIR)
+ $(call msg,CLANG,,$(notdir $@))
+ $(Q)$(CLANG) $(CFLAGS) -c $< -o $@
+
+$(TEST_GEN_PROGS): $(SBIN_DIR)/%: $(CRIBOBJ_DIR)/%.o $(LIBBPF_OBJ) | $(SBIN_DIR)
+ $(call msg,CLANG-LINK,,$(notdir $@))
+ $(Q)$(CLANG) $(CFLAGS) $(LDFLAGS) $^ -o $@
+
+override define CLEAN
+ rm -rf $(OUTPUT_DIR)
+endef
+
+all: $(TEST_GEN_PROGS)
+
+.PHONY: all clean help
+
+.DEFAULT_GOAL := all
+
+.DELETE_ON_ERROR:
+
+.SECONDARY:
diff --git a/tools/testing/selftests/crib/config b/tools/testing/selftests/crib/config
new file mode 100644
index 000000000000..61684f763df0
--- /dev/null
+++ b/tools/testing/selftests/crib/config
@@ -0,0 +1,7 @@
+CONFIG_BPF=y
+CONFIG_BPF_EVENTS=y
+CONFIG_BPF_JIT=y
+CONFIG_BPF_CRIB=y
+CONFIG_DEBUG_INFO_BTF=y
+CONFIG_IPV6=y
+CONFIG_NET=y
diff --git a/tools/testing/selftests/crib/test_dump_task.bpf.c b/tools/testing/selftests/crib/test_dump_task.bpf.c
new file mode 100644
index 000000000000..ee9843a79dac
--- /dev/null
+++ b/tools/testing/selftests/crib/test_dump_task.bpf.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author:
+ * Juntong Deng <juntong.deng@xxxxxxxxxxx>
+ */
+
+#include "vmlinux.h"
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+
+#include "test_dump_task.h"
+
+char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+struct {
+ __uint(type, BPF_MAP_TYPE_RINGBUF);
+ __uint(max_entries, 100000);
+} rb SEC(".maps");
+
+extern struct task_struct *bpf_task_from_vpid(pid_t vpid) __ksym;
+extern void bpf_task_release(struct task_struct *p) __ksym;
+
+extern int bpf_iter_task_vma_new(struct bpf_iter_task_vma *it,
+ struct task_struct *task,
+ unsigned long addr) __ksym;
+extern struct vm_area_struct *bpf_iter_task_vma_next(struct bpf_iter_task_vma *it) __ksym;
+extern void bpf_iter_task_vma_destroy(struct bpf_iter_task_vma *it) __ksym;
+
+SEC("crib")
+int dump_all_vma(struct prog_args *arg)
+{
+ int err = 0;
+
+ struct task_struct *task = bpf_task_from_vpid(arg->pid);
+ if (!task) {
+ err = -1;
+ goto error;
+ }
+
+ struct vm_area_struct *cur_vma;
+ struct bpf_iter_task_vma vma_it;
+
+ bpf_iter_task_vma_new(&vma_it, task, 0);
+ while ((cur_vma = bpf_iter_task_vma_next(&vma_it))) {
+ struct event_vma *e_vma = bpf_ringbuf_reserve(&rb, sizeof(struct event_vma), 0);
+ if (!e_vma) {
+ err = -2;
+ goto error_buf;
+ }
+
+ e_vma->hdr.type = EVENT_TYPE_VMA;
+ e_vma->vm_start = BPF_CORE_READ(cur_vma, vm_start);
+ e_vma->vm_end = BPF_CORE_READ(cur_vma, vm_end);
+ e_vma->vm_flags = BPF_CORE_READ(cur_vma, vm_flags);
+
+ if (cur_vma->vm_file)
+ e_vma->vm_pgoff = BPF_CORE_READ(cur_vma, vm_pgoff);
+
+ bpf_ringbuf_submit(e_vma, 0);
+ }
+
+error_buf:
+ bpf_iter_task_vma_destroy(&vma_it);
+ bpf_task_release(task);
+error:
+ return err;
+}
+
+SEC("crib")
+int dump_task_stat(struct prog_args *arg)
+{
+ int err = 0;
+
+ struct task_struct *task = bpf_task_from_vpid(arg->pid);
+ if (!task) {
+ err = -1;
+ goto error;
+ }
+
+ struct event_task *e_task = bpf_ringbuf_reserve(&rb, sizeof(struct event_task), 0);
+ if (!e_task) {
+ err = -2;
+ goto error_buf;
+ }
+
+ e_task->hdr.type = EVENT_TYPE_TASK;
+ e_task->pid = BPF_CORE_READ(task, pid);
+ e_task->prio = BPF_CORE_READ(task, prio);
+ e_task->policy = BPF_CORE_READ(task, policy);
+ e_task->flags = BPF_CORE_READ(task, flags);
+ e_task->exit_code = BPF_CORE_READ(task, exit_code);
+ BPF_CORE_READ_STR_INTO(&e_task->comm, task, comm);
+
+ bpf_ringbuf_submit(e_task, 0);
+
+ struct event_mm *e_mm = bpf_ringbuf_reserve(&rb, sizeof(struct event_mm), 0);
+ if (!e_mm) {
+ err = -2;
+ goto error_buf;
+ }
+
+ struct mm_struct *mm = BPF_CORE_READ(task, mm);
+ e_mm->hdr.type = EVENT_TYPE_MM;
+ e_mm->start_code = BPF_CORE_READ(mm, start_code);
+ e_mm->end_code = BPF_CORE_READ(mm, end_code);
+ e_mm->start_data = BPF_CORE_READ(mm, start_data);
+ e_mm->end_data = BPF_CORE_READ(mm, end_data);
+ e_mm->start_brk = BPF_CORE_READ(mm, start_brk);
+ e_mm->brk = BPF_CORE_READ(mm, brk);
+ e_mm->start_stack = BPF_CORE_READ(mm, start_stack);
+ e_mm->arg_start = BPF_CORE_READ(mm, arg_start);
+ e_mm->arg_end = BPF_CORE_READ(mm, arg_end);
+ e_mm->env_start = BPF_CORE_READ(mm, env_start);
+ e_mm->env_end = BPF_CORE_READ(mm, env_end);
+ e_mm->map_count = BPF_CORE_READ(mm, map_count);
+
+ bpf_ringbuf_submit(e_mm, 0);
+
+error_buf:
+ bpf_task_release(task);
+error:
+ return err;
+}
diff --git a/tools/testing/selftests/crib/test_dump_task.c b/tools/testing/selftests/crib/test_dump_task.c
new file mode 100644
index 000000000000..1b9f234ac292
--- /dev/null
+++ b/tools/testing/selftests/crib/test_dump_task.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author:
+ * Juntong Deng <juntong.deng@xxxxxxxxxxx>
+ */
+
+#include <argp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <bpf/libbpf.h>
+#include <bpf/bpf.h>
+#include <autoconf.h>
+
+#include "../kselftest_harness.h"
+
+#include "test_dump_task.h"
+#include "test_dump_task.bpf.skel.h"
+
+struct task {
+ int pid;
+ unsigned int flags;
+ int prio;
+ unsigned int policy;
+ int exit_code;
+ char comm[16];
+};
+
+struct mm {
+ unsigned long start_code;
+ unsigned long end_code;
+ unsigned long start_data;
+ unsigned long end_data;
+ unsigned long start_brk;
+ unsigned long brk;
+ unsigned long start_stack;
+ unsigned long arg_start;
+ unsigned long arg_end;
+ unsigned long env_start;
+ unsigned long env_end;
+ int map_count;
+};
+
+struct vma {
+ unsigned long vm_start;
+ unsigned long vm_end;
+ unsigned long vm_flags;
+ unsigned long vm_pgoff;
+};
+
+struct dump_info {
+ struct task task;
+ struct mm mm;
+ struct vma *vma;
+ unsigned int vma_count;
+};
+
+static int handle_vma_event(struct dump_info *info, struct event_vma *e_vma)
+{
+ struct vma *vma = &info->vma[info->vma_count];
+ vma->vm_start = e_vma->vm_start;
+ vma->vm_end = e_vma->vm_end;
+ vma->vm_flags = e_vma->vm_flags;
+ vma->vm_pgoff = e_vma->vm_pgoff;
+ info->vma_count++;
+ return 0;
+}
+
+static int handle_mm_event(struct dump_info *info, struct event_mm *e_mm)
+{
+ info->mm.start_code = e_mm->start_code;
+ info->mm.end_code = e_mm->end_code;
+ info->mm.start_data = e_mm->start_data;
+ info->mm.end_data = e_mm->end_data;
+ info->mm.start_brk = e_mm->start_brk;
+ info->mm.brk = e_mm->brk;
+ info->mm.start_stack = e_mm->start_stack;
+ info->mm.arg_start = e_mm->arg_start;
+ info->mm.arg_end = e_mm->arg_end;
+ info->mm.env_start = e_mm->env_start;
+ info->mm.env_end = e_mm->env_end;
+ info->mm.map_count = e_mm->map_count;
+ info->vma = (struct vma *)malloc(sizeof(struct vma) * e_mm->map_count);
+ info->vma_count = 0;
+ return 0;
+}
+
+static int handle_task_event(struct dump_info *info, struct event_task *e_task)
+{
+ info->task.pid = e_task->pid;
+ info->task.flags = e_task->flags;
+ info->task.prio = e_task->prio;
+ info->task.policy = e_task->policy;
+ info->task.exit_code = e_task->exit_code;
+ memcpy(info->task.comm, e_task->comm, sizeof(info->task.comm));
+ return 0;
+}
+
+static int handle_event(void *ctx, void *data, size_t data_sz)
+{
+ struct dump_info *info = (struct dump_info *)ctx;
+ const struct event_hdr *e_hdr = data;
+ int err = 0;
+
+ switch (e_hdr->type) {
+ case EVENT_TYPE_TASK:
+ handle_task_event(info, (struct event_task *)data);
+ break;
+ case EVENT_TYPE_VMA:
+ handle_vma_event(info, (struct event_vma *)data);
+ break;
+ case EVENT_TYPE_MM:
+ handle_mm_event(info, (struct event_mm *)data);
+ break;
+ default:
+ err = -1;
+ printf("Unknown event type!\n");
+ break;
+ }
+ return err;
+}
+
+static int dump_task_and_mm_struct_from_proc(struct dump_info *info)
+{
+ FILE *file = fopen("/proc/self/stat", "r");
+ if (!file)
+ return -1;
+
+ fscanf(file, "%d %s %*c %*d %*d %*d %*d %*d %u %*lu %*lu %*lu %*lu "
+ "%*lu %*lu %*ld %*ld %d %*ld %*d %*d %*llu %*lu %*ld %*lu %lu %lu %lu "
+ "%*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*d %*d %*u %u %*llu %*lu %*ld "
+ "%lu %lu %lu %lu %lu %lu %lu %d",
+ &info->task.pid, info->task.comm, &info->task.flags, &info->task.prio,
+ &info->mm.start_code, &info->mm.end_code, &info->mm.start_stack,
+ &info->task.policy, &info->mm.start_data, &info->mm.end_data,
+ &info->mm.start_brk, &info->mm.arg_start, &info->mm.arg_end,
+ &info->mm.env_start, &info->mm.env_end, &info->task.exit_code);
+
+ fclose(file);
+ return 0;
+}
+
+static void parse_vma_vmflags(char *buf, struct vma *vma)
+{
+ vma->vm_flags = 0;
+ char *token = strtok(buf, " ");
+ do {
+ if (!strncmp(token, "rd", 2))
+ vma->vm_flags |= VM_READ;
+ else if (!strncmp(token, "wr", 2))
+ vma->vm_flags |= VM_WRITE;
+ else if (!strncmp(token, "ex", 2))
+ vma->vm_flags |= VM_EXEC;
+ else if (!strncmp(token, "sh", 2))
+ vma->vm_flags |= VM_SHARED;
+ else if (!strncmp(token, "mr", 2))
+ vma->vm_flags |= VM_MAYREAD;
+ else if (!strncmp(token, "mw", 2))
+ vma->vm_flags |= VM_MAYWRITE;
+ else if (!strncmp(token, "me", 2))
+ vma->vm_flags |= VM_MAYEXEC;
+ else if (!strncmp(token, "ms", 2))
+ vma->vm_flags |= VM_MAYSHARE;
+ else if (!strncmp(token, "gd", 2))
+ vma->vm_flags |= VM_GROWSDOWN;
+ else if (!strncmp(token, "pf", 2))
+ vma->vm_flags |= VM_PFNMAP;
+ else if (!strncmp(token, "lo", 2))
+ vma->vm_flags |= VM_LOCKED;
+ else if (!strncmp(token, "io", 2))
+ vma->vm_flags |= VM_IO;
+ else if (!strncmp(token, "sr", 2))
+ vma->vm_flags |= VM_SEQ_READ;
+ else if (!strncmp(token, "rr", 2))
+ vma->vm_flags |= VM_RAND_READ;
+ else if (!strncmp(token, "dc", 2))
+ vma->vm_flags |= VM_DONTCOPY;
+ else if (!strncmp(token, "de", 2))
+ vma->vm_flags |= VM_DONTEXPAND;
+ else if (!strncmp(token, "lf", 2))
+ vma->vm_flags |= VM_LOCKONFAULT;
+ else if (!strncmp(token, "ac", 2))
+ vma->vm_flags |= VM_ACCOUNT;
+ else if (!strncmp(token, "nr", 2))
+ vma->vm_flags |= VM_NORESERVE;
+ else if (!strncmp(token, "ht", 2))
+ vma->vm_flags |= VM_HUGETLB;
+ else if (!strncmp(token, "sf", 2))
+ vma->vm_flags |= VM_SYNC;
+ else if (!strncmp(token, "ar", 2))
+ vma->vm_flags |= VM_ARCH_1;
+ else if (!strncmp(token, "wf", 2))
+ vma->vm_flags |= VM_WIPEONFORK;
+ else if (!strncmp(token, "dd", 2))
+ vma->vm_flags |= VM_DONTDUMP;
+ else if (!strncmp(token, "sd", 2))
+ vma->vm_flags |= VM_SOFTDIRTY;
+ else if (!strncmp(token, "mm", 2))
+ vma->vm_flags |= VM_MIXEDMAP;
+ else if (!strncmp(token, "hg", 2))
+ vma->vm_flags |= VM_HUGEPAGE;
+ else if (!strncmp(token, "nh", 2))
+ vma->vm_flags |= VM_NOHUGEPAGE;
+ else if (!strncmp(token, "mg", 2))
+ vma->vm_flags |= VM_MERGEABLE;
+ else if (!strncmp(token, "um", 2))
+ vma->vm_flags |= VM_UFFD_MISSING;
+ else if (!strncmp(token, "uw", 2))
+ vma->vm_flags |= VM_UFFD_WP;
+ }
+ while ((token = strtok(NULL, " ")) != NULL);
+}
+
+static int dump_vma_from_proc(struct dump_info *info)
+{
+ FILE *file = fopen("/proc/self/smaps", "r");
+ if (!file)
+ return -1;
+
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t nread;
+ while ((nread = getline(&line, &len, file)) != -1) {
+ struct vma *vma = &info->vma[info->vma_count];
+ if (isupper(*line)) {
+ if (!strncmp(line, "VmFlags: ", 9)) {
+ parse_vma_vmflags(&line[9], vma);
+ info->vma_count++;
+ }
+ } else {
+ sscanf(line, "%lx-%lx %*c%*c%*c%*c %lx",
+ &vma->vm_start, &vma->vm_end, &vma->vm_pgoff);
+ }
+ }
+
+ fclose(file);
+ return 0;
+}
+
+static int check_dump_info_correctness(struct dump_info *crib_info, struct dump_info *proc_info)
+{
+ if (crib_info->task.pid != proc_info->task.pid ||
+ crib_info->task.flags != proc_info->task.flags ||
+ crib_info->task.prio - 100 != proc_info->task.prio ||
+ crib_info->task.policy != proc_info->task.policy ||
+ crib_info->task.exit_code != proc_info->task.exit_code ||
+ strncmp(crib_info->task.comm, proc_info->task.comm + 1,
+ strlen(crib_info->task.comm)))
+ return -1;
+
+ if (crib_info->mm.start_code != proc_info->mm.start_code ||
+ crib_info->mm.end_code != proc_info->mm.end_code ||
+ crib_info->mm.start_data != proc_info->mm.start_data ||
+ crib_info->mm.end_data != proc_info->mm.end_data ||
+ crib_info->mm.start_brk != proc_info->mm.start_brk ||
+ crib_info->mm.arg_start != proc_info->mm.arg_start ||
+ crib_info->mm.arg_end != proc_info->mm.arg_end ||
+ crib_info->mm.env_start != proc_info->mm.env_start ||
+ crib_info->mm.env_end != proc_info->mm.env_end ||
+ crib_info->mm.start_stack != proc_info->mm.start_stack)
+ return -1;
+
+ struct vma *crib_vma, *proc_vma;
+ for (int i = 0; i < crib_info->mm.map_count; i++) {
+ crib_vma = &crib_info->vma[i];
+ proc_vma = &proc_info->vma[i];
+ if (crib_vma->vm_start != proc_vma->vm_start ||
+ crib_vma->vm_end != proc_vma->vm_end ||
+ crib_vma->vm_flags != proc_vma->vm_flags ||
+ crib_vma->vm_pgoff << CONFIG_PAGE_SHIFT != proc_vma->vm_pgoff)
+ return -1;
+ }
+ return 0;
+}
+
+TEST(dump_task)
+{
+ struct prog_args args = {
+ .pid = getpid(),
+ };
+ ASSERT_GT(args.pid, 0);
+
+ DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
+ .ctx_in = &args,
+ .ctx_size_in = sizeof(args),
+ );
+
+ struct test_dump_task_bpf *skel = test_dump_task_bpf__open_and_load();
+ ASSERT_NE(skel, NULL);
+
+ int dump_task_stat_fd = bpf_program__fd(skel->progs.dump_task_stat);
+ ASSERT_GT(dump_task_stat_fd, 0);
+
+ int dump_all_vma_fd = bpf_program__fd(skel->progs.dump_all_vma);
+ ASSERT_GT(dump_all_vma_fd, 0);
+
+ struct dump_info crib_info, proc_info;
+ memset(&crib_info, 0, sizeof(struct dump_info));
+ memset(&proc_info, 0, sizeof(struct dump_info));
+
+ struct ring_buffer *rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event,
+ &crib_info, NULL);
+ ASSERT_NE(rb, NULL);
+
+ clock_t crib_begin = clock();
+
+ ASSERT_EQ(bpf_prog_test_run_opts(dump_task_stat_fd, &opts), 0);
+ ASSERT_EQ(bpf_prog_test_run_opts(dump_all_vma_fd, &opts), 0);
+
+ ASSERT_GT(ring_buffer__poll(rb, 100), 0);
+
+ clock_t crib_end = clock();
+
+ printf("CRIB dump took %f seconds\n", (double)(crib_end - crib_begin) / CLOCKS_PER_SEC);
+
+ clock_t proc_begin = clock();
+
+ proc_info.vma = (struct vma *)malloc(sizeof(struct vma) * (crib_info.mm.map_count + 1));
+ ASSERT_EQ(dump_task_and_mm_struct_from_proc(&proc_info), 0);
+ ASSERT_EQ(dump_vma_from_proc(&proc_info), 0);
+
+ clock_t proc_end = clock();
+
+ printf("PROC dump took %f seconds\n", (double)(proc_end - proc_begin) / CLOCKS_PER_SEC);
+
+ ASSERT_EQ(check_dump_info_correctness(&crib_info, &proc_info), 0);
+
+ free(crib_info.vma);
+ free(proc_info.vma);
+ ring_buffer__free(rb);
+ test_dump_task_bpf__destroy(skel);
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/crib/test_dump_task.h b/tools/testing/selftests/crib/test_dump_task.h
new file mode 100644
index 000000000000..b3cf3df852bf
--- /dev/null
+++ b/tools/testing/selftests/crib/test_dump_task.h
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author:
+ * Juntong Deng <juntong.deng@xxxxxxxxxxx>
+ */
+
+#ifndef __TEST_DUMP_TASK_H
+#define __TEST_DUMP_TASK_H
+
+#define EVENT_TYPE_VMA 0
+#define EVENT_TYPE_TASK 1
+#define EVENT_TYPE_MM 2
+
+#define VM_READ 0x00000001
+#define VM_WRITE 0x00000002
+#define VM_EXEC 0x00000004
+#define VM_SHARED 0x00000008
+#define VM_MAYREAD 0x00000010
+#define VM_MAYWRITE 0x00000020
+#define VM_MAYEXEC 0x00000040
+#define VM_MAYSHARE 0x00000080
+#define VM_GROWSDOWN 0x00000100
+#define VM_UFFD_MISSING 0x00000200
+#define VM_MAYOVERLAY 0x00000200
+#define VM_PFNMAP 0x00000400
+#define VM_UFFD_WP 0x00001000
+#define VM_LOCKED 0x00002000
+#define VM_IO 0x00004000
+#define VM_SEQ_READ 0x00008000
+#define VM_RAND_READ 0x00010000
+#define VM_DONTCOPY 0x00020000
+#define VM_DONTEXPAND 0x00040000
+#define VM_LOCKONFAULT 0x00080000
+#define VM_ACCOUNT 0x00100000
+#define VM_NORESERVE 0x00200000
+#define VM_HUGETLB 0x00400000
+#define VM_SYNC 0x00800000
+#define VM_ARCH_1 0x01000000
+#define VM_WIPEONFORK 0x02000000
+#define VM_DONTDUMP 0x04000000
+#define VM_SOFTDIRTY 0x08000000
+#define VM_MIXEDMAP 0x10000000
+#define VM_HUGEPAGE 0x20000000
+#define VM_NOHUGEPAGE 0x40000000
+#define VM_MERGEABLE 0x80000000
+
+struct prog_args {
+ int pid;
+};
+
+struct event_hdr {
+ int type;
+ int subtype;
+};
+
+struct event_task {
+ struct event_hdr hdr;
+ int pid;
+ unsigned int flags;
+ int prio;
+ unsigned int policy;
+ int exit_code;
+ char comm[16];
+};
+
+struct event_vma {
+ struct event_hdr hdr;
+ unsigned long vm_start;
+ unsigned long vm_end;
+ unsigned long vm_flags;
+ unsigned long vm_pgoff;
+};
+
+struct event_mm {
+ struct event_hdr hdr;
+ unsigned long start_code;
+ unsigned long end_code;
+ unsigned long start_data;
+ unsigned long end_data;
+ unsigned long start_brk;
+ unsigned long brk;
+ unsigned long start_stack;
+ unsigned long arg_start;
+ unsigned long arg_end;
+ unsigned long env_start;
+ unsigned long env_end;
+ int map_count;
+};
+
+#endif /* __TEST_DUMP_TASK_H */
--
2.39.2