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@outlook.com>
+ */
+
+#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@outlook.com>
+ */
+
+#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@outlook.com>
+ */
+
+#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 */
