tools/testing/selftests/bpf/prog_tests/uretprobe_stack.c

Source file repositories/reference/linux-study-clean/tools/testing/selftests/bpf/prog_tests/uretprobe_stack.c

File Facts

System
Linux kernel
Corpus path
tools/testing/selftests/bpf/prog_tests/uretprobe_stack.c
Extension
.c
Size
5791 bytes
Lines
187
Domain
Support Tooling And Documentation
Bucket
tools
Inferred role
Support Tooling And Documentation: implementation source
Status
source implementation candidate

Why This File Exists

Repository support layer: documentation, build tooling, samples, user-space helper tools, generated initramfs support, licenses, and validation utilities.

Dependency Surface

Detected Declarations

Annotated Snippet

struct range {
	long start;
	long stop;
};

static struct range targets[] = {
	{}, /* we want target_1 to map to target[1], so need 1-based indexing */
	{ (long)&__start_uprobe__target_1, (long)&__stop_uprobe__target_1 },
	{ (long)&__start_uprobe__target_2, (long)&__stop_uprobe__target_2 },
	{ (long)&__start_uprobe__target_3, (long)&__stop_uprobe__target_3 },
	{ (long)&__start_uprobe__target_4, (long)&__stop_uprobe__target_4 },
};

static struct range caller = {
	(long)&__start_uretprobe_stack_sec,
	(long)&__stop_uretprobe_stack_sec,
};

static void validate_stack(__u64 *ips, int stack_len, int cnt, ...)
{
	int i, j;
	va_list args;

	if (!ASSERT_GT(stack_len, 0, "stack_len"))
		return;

	stack_len /= 8;

	/* check if we have enough entries to satisfy test expectations */
	if (!ASSERT_GE(stack_len, cnt, "stack_len2"))
		return;

	if (env.verbosity >= VERBOSE_NORMAL) {
		printf("caller: %#lx - %#lx\n", caller.start, caller.stop);
		for (i = 1; i < ARRAY_SIZE(targets); i++)
			printf("target_%d: %#lx - %#lx\n", i, targets[i].start, targets[i].stop);
		for (i = 0; i < stack_len; i++) {
			for (j = 1; j < ARRAY_SIZE(targets); j++) {
				if (ips[i] >= targets[j].start && ips[i] < targets[j].stop)
					break;
			}
			if (j < ARRAY_SIZE(targets)) { /* found target match */
				printf("ENTRY #%d: %#lx (in target_%d)\n", i, (long)ips[i], j);
			} else if (ips[i] >= caller.start && ips[i] < caller.stop) {
				printf("ENTRY #%d: %#lx (in caller)\n", i, (long)ips[i]);
			} else {
				printf("ENTRY #%d: %#lx\n", i, (long)ips[i]);
			}
		}
	}

	va_start(args, cnt);

	for (i = cnt - 1; i >= 0; i--) {
		/* most recent entry is the deepest target function */
		const struct range *t = va_arg(args, const struct range *);

		ASSERT_GE(ips[i], t->start, "addr_start");
		ASSERT_LT(ips[i], t->stop, "addr_stop");
	}

	va_end(args);
}

/* __weak prevents inlining */
__attribute__((section("uretprobe_stack_sec")))
__weak void test_uretprobe_stack(void)
{
	LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
	struct uretprobe_stack *skel;
	int err;

	skel = uretprobe_stack__open_and_load();
	if (!ASSERT_OK_PTR(skel, "skel_open"))
		return;

	err = uretprobe_stack__attach(skel);
	if (!ASSERT_OK(err, "skel_attach"))
		goto cleanup;

	/* trigger */
	ASSERT_EQ(target_1(0), 42 + 1, "trigger_return");

	/*
	 * Stacks captured on ENTRY uprobes
	 */

	/* (uprobe 1) target_1 in stack trace*/
	validate_stack(skel->bss->entry_stack1, skel->bss->entry1_len,
		       2, &caller, &targets[1]);

Annotation

Implementation Notes