Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  1// SPDX-License-Identifier: GPL-2.0
  2/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
  3
  4#include <test_progs.h>
  5#include "uretprobe_stack.skel.h"
  6#include "../sdt.h"
  7
  8/* We set up target_1() -> target_2() -> target_3() -> target_4() -> USDT()
  9 * call chain, each being traced by our BPF program. On entry or return from
 10 * each target_*() we are capturing user stack trace and recording it in
 11 * global variable, so that user space part of the test can validate it.
 12 *
 13 * Note, we put each target function into a custom section to get those
 14 * __start_XXX/__stop_XXX symbols, generated by linker for us, which allow us
 15 * to know address range of those functions
 16 */
 17__attribute__((section("uprobe__target_4")))
 18__weak int target_4(void)
 19{
 20	STAP_PROBE1(uretprobe_stack, target, 42);
 21	return 42;
 22}
 23
 24extern const void *__start_uprobe__target_4;
 25extern const void *__stop_uprobe__target_4;
 26
 27__attribute__((section("uprobe__target_3")))
 28__weak int target_3(void)
 29{
 30	return target_4();
 31}
 32
 33extern const void *__start_uprobe__target_3;
 34extern const void *__stop_uprobe__target_3;
 35
 36__attribute__((section("uprobe__target_2")))
 37__weak int target_2(void)
 38{
 39	return target_3();
 40}
 41
 42extern const void *__start_uprobe__target_2;
 43extern const void *__stop_uprobe__target_2;
 44
 45__attribute__((section("uprobe__target_1")))
 46__weak int target_1(int depth)
 47{
 48	if (depth < 1)
 49		return 1 + target_1(depth + 1);
 50	else
 51		return target_2();
 52}
 53
 54extern const void *__start_uprobe__target_1;
 55extern const void *__stop_uprobe__target_1;
 56
 57extern const void *__start_uretprobe_stack_sec;
 58extern const void *__stop_uretprobe_stack_sec;
 59
 60struct range {
 61	long start;
 62	long stop;
 63};
 64
 65static struct range targets[] = {
 66	{}, /* we want target_1 to map to target[1], so need 1-based indexing */
 67	{ (long)&__start_uprobe__target_1, (long)&__stop_uprobe__target_1 },
 68	{ (long)&__start_uprobe__target_2, (long)&__stop_uprobe__target_2 },
 69	{ (long)&__start_uprobe__target_3, (long)&__stop_uprobe__target_3 },
 70	{ (long)&__start_uprobe__target_4, (long)&__stop_uprobe__target_4 },
 71};
 72
 73static struct range caller = {
 74	(long)&__start_uretprobe_stack_sec,
 75	(long)&__stop_uretprobe_stack_sec,
 76};
 77
 78static void validate_stack(__u64 *ips, int stack_len, int cnt, ...)
 79{
 80	int i, j;
 81	va_list args;
 82
 83	if (!ASSERT_GT(stack_len, 0, "stack_len"))
 84		return;
 85
 86	stack_len /= 8;
 87
 88	/* check if we have enough entries to satisfy test expectations */
 89	if (!ASSERT_GE(stack_len, cnt, "stack_len2"))
 90		return;
 91
 92	if (env.verbosity >= VERBOSE_NORMAL) {
 93		printf("caller: %#lx - %#lx\n", caller.start, caller.stop);
 94		for (i = 1; i < ARRAY_SIZE(targets); i++)
 95			printf("target_%d: %#lx - %#lx\n", i, targets[i].start, targets[i].stop);
 96		for (i = 0; i < stack_len; i++) {
 97			for (j = 1; j < ARRAY_SIZE(targets); j++) {
 98				if (ips[i] >= targets[j].start && ips[i] < targets[j].stop)
 99					break;
100			}
101			if (j < ARRAY_SIZE(targets)) { /* found target match */
102				printf("ENTRY #%d: %#lx (in target_%d)\n", i, (long)ips[i], j);
103			} else if (ips[i] >= caller.start && ips[i] < caller.stop) {
104				printf("ENTRY #%d: %#lx (in caller)\n", i, (long)ips[i]);
105			} else {
106				printf("ENTRY #%d: %#lx\n", i, (long)ips[i]);
107			}
108		}
109	}
110
111	va_start(args, cnt);
112
113	for (i = cnt - 1; i >= 0; i--) {
114		/* most recent entry is the deepest target function */
115		const struct range *t = va_arg(args, const struct range *);
116
117		ASSERT_GE(ips[i], t->start, "addr_start");
118		ASSERT_LT(ips[i], t->stop, "addr_stop");
119	}
120
121	va_end(args);
122}
123
124/* __weak prevents inlining */
125__attribute__((section("uretprobe_stack_sec")))
126__weak void test_uretprobe_stack(void)
127{
128	LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
129	struct uretprobe_stack *skel;
130	int err;
131
132	skel = uretprobe_stack__open_and_load();
133	if (!ASSERT_OK_PTR(skel, "skel_open"))
134		return;
135
136	err = uretprobe_stack__attach(skel);
137	if (!ASSERT_OK(err, "skel_attach"))
138		goto cleanup;
139
140	/* trigger */
141	ASSERT_EQ(target_1(0), 42 + 1, "trigger_return");
142
143	/*
144	 * Stacks captured on ENTRY uprobes
145	 */
146
147	/* (uprobe 1) target_1 in stack trace*/
148	validate_stack(skel->bss->entry_stack1, skel->bss->entry1_len,
149		       2, &caller, &targets[1]);
150	/* (uprobe 1, recursed) */
151	validate_stack(skel->bss->entry_stack1_recur, skel->bss->entry1_recur_len,
152		       3, &caller, &targets[1], &targets[1]);
153	/* (uprobe 2) caller -> target_1 -> target_1 -> target_2 */
154	validate_stack(skel->bss->entry_stack2, skel->bss->entry2_len,
155		       4, &caller, &targets[1], &targets[1], &targets[2]);
156	/* (uprobe 3) */
157	validate_stack(skel->bss->entry_stack3, skel->bss->entry3_len,
158		       5, &caller, &targets[1], &targets[1], &targets[2], &targets[3]);
159	/* (uprobe 4) caller -> target_1 -> target_1 -> target_2 -> target_3 -> target_4 */
160	validate_stack(skel->bss->entry_stack4, skel->bss->entry4_len,
161		       6, &caller, &targets[1], &targets[1], &targets[2], &targets[3], &targets[4]);
162
163	/* (USDT): full caller -> target_1 -> target_1 -> target_2 (uretprobed)
164	 *              -> target_3 -> target_4 (uretprobes) chain
165	 */
166	validate_stack(skel->bss->usdt_stack, skel->bss->usdt_len,
167		       6, &caller, &targets[1], &targets[1], &targets[2], &targets[3], &targets[4]);
168
169	/*
170	 * Now stacks captured on the way out in EXIT uprobes
171	 */
172
173	/* (uretprobe 4) everything up to target_4, but excluding it */
174	validate_stack(skel->bss->exit_stack4, skel->bss->exit4_len,
175		       5, &caller, &targets[1], &targets[1], &targets[2], &targets[3]);
176	/* we didn't install uretprobes on target_2 and target_3 */
177	/* (uretprobe 1, recur) first target_1 call only */
178	validate_stack(skel->bss->exit_stack1_recur, skel->bss->exit1_recur_len,
179		       2, &caller, &targets[1]);
180	/* (uretprobe 1) just a caller in the stack trace */
181	validate_stack(skel->bss->exit_stack1, skel->bss->exit1_len,
182		       1, &caller);
183
184cleanup:
185	uretprobe_stack__destroy(skel);
186}