Add a set of tests to validate that stack traces captured from or in the presence of active uprobes and uretprobes are valid and complete. For this we use BPF program that are installed either on entry or exit of user function, plus deep-nested USDT. One of target funtions (target_1) is recursive to generate two different entries in the stack trace for the same uprobe/uretprobe, testing potential edge conditions. If there is no fixes, we get something like this for one of the scenarios: caller: 0x758fff - 0x7595ab target_1: 0x758fd5 - 0x758fff target_2: 0x758fca - 0x758fd5 target_3: 0x758fbf - 0x758fca target_4: 0x758fb3 - 0x758fbf ENTRY #0: 0x758fb3 (in target_4) ENTRY #1: 0x758fd3 (in target_2) ENTRY #2: 0x758ffd (in target_1) ENTRY #3: 0x7fffffffe000 ENTRY #4: 0x7fffffffe000 ENTRY #5: 0x6f8f39 ENTRY #6: 0x6fa6f0 ENTRY #7: 0x7f403f229590 Entry #3 and #4 (0x7fffffffe000) are uretprobe trampoline addresses which obscure actual target_1 and another target_1 invocations. Also note that between entry #0 and entry #1 we are missing an entry for target_3. With fixes, we get desired full stack traces: caller: 0x758fff - 0x7595ab target_1: 0x758fd5 - 0x758fff target_2: 0x758fca - 0x758fd5 target_3: 0x758fbf - 0x758fca target_4: 0x758fb3 - 0x758fbf ENTRY #0: 0x758fb7 (in target_4) ENTRY #1: 0x758fc8 (in target_3) ENTRY #2: 0x758fd3 (in target_2) ENTRY #3: 0x758ffd (in target_1) ENTRY #4: 0x758ff3 (in target_1) ENTRY #5: 0x75922c (in caller) ENTRY #6: 0x6f8f39 ENTRY #7: 0x6fa6f0 ENTRY #8: 0x7f986adc4cd0 Now there is a logical and complete sequence of function calls. Link: https://lore.kernel.org/all/20240522013845.1631305-5-andrii@kernel.org/ Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Acked-by: Jiri Olsa <jolsa@kernel.org> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
96 lines
2.3 KiB
C
96 lines
2.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
|
#include <vmlinux.h>
|
|
#include <bpf/bpf_helpers.h>
|
|
#include <bpf/bpf_tracing.h>
|
|
#include <bpf/usdt.bpf.h>
|
|
|
|
char _license[] SEC("license") = "GPL";
|
|
|
|
__u64 entry_stack1[32], exit_stack1[32];
|
|
__u64 entry_stack1_recur[32], exit_stack1_recur[32];
|
|
__u64 entry_stack2[32];
|
|
__u64 entry_stack3[32];
|
|
__u64 entry_stack4[32], exit_stack4[32];
|
|
__u64 usdt_stack[32];
|
|
|
|
int entry1_len, exit1_len;
|
|
int entry1_recur_len, exit1_recur_len;
|
|
int entry2_len, exit2_len;
|
|
int entry3_len, exit3_len;
|
|
int entry4_len, exit4_len;
|
|
int usdt_len;
|
|
|
|
#define SZ sizeof(usdt_stack)
|
|
|
|
SEC("uprobe//proc/self/exe:target_1")
|
|
int BPF_UPROBE(uprobe_1)
|
|
{
|
|
/* target_1 is recursive wit depth of 2, so we capture two separate
|
|
* stack traces, depending on which occurence it is
|
|
*/
|
|
static bool recur = false;
|
|
|
|
if (!recur)
|
|
entry1_len = bpf_get_stack(ctx, &entry_stack1, SZ, BPF_F_USER_STACK);
|
|
else
|
|
entry1_recur_len = bpf_get_stack(ctx, &entry_stack1_recur, SZ, BPF_F_USER_STACK);
|
|
|
|
recur = true;
|
|
return 0;
|
|
}
|
|
|
|
SEC("uretprobe//proc/self/exe:target_1")
|
|
int BPF_URETPROBE(uretprobe_1)
|
|
{
|
|
/* see above, target_1 is recursive */
|
|
static bool recur = false;
|
|
|
|
/* NOTE: order of returns is reversed to order of entries */
|
|
if (!recur)
|
|
exit1_recur_len = bpf_get_stack(ctx, &exit_stack1_recur, SZ, BPF_F_USER_STACK);
|
|
else
|
|
exit1_len = bpf_get_stack(ctx, &exit_stack1, SZ, BPF_F_USER_STACK);
|
|
|
|
recur = true;
|
|
return 0;
|
|
}
|
|
|
|
SEC("uprobe//proc/self/exe:target_2")
|
|
int BPF_UPROBE(uprobe_2)
|
|
{
|
|
entry2_len = bpf_get_stack(ctx, &entry_stack2, SZ, BPF_F_USER_STACK);
|
|
return 0;
|
|
}
|
|
|
|
/* no uretprobe for target_2 */
|
|
|
|
SEC("uprobe//proc/self/exe:target_3")
|
|
int BPF_UPROBE(uprobe_3)
|
|
{
|
|
entry3_len = bpf_get_stack(ctx, &entry_stack3, SZ, BPF_F_USER_STACK);
|
|
return 0;
|
|
}
|
|
|
|
/* no uretprobe for target_3 */
|
|
|
|
SEC("uprobe//proc/self/exe:target_4")
|
|
int BPF_UPROBE(uprobe_4)
|
|
{
|
|
entry4_len = bpf_get_stack(ctx, &entry_stack4, SZ, BPF_F_USER_STACK);
|
|
return 0;
|
|
}
|
|
|
|
SEC("uretprobe//proc/self/exe:target_4")
|
|
int BPF_URETPROBE(uretprobe_4)
|
|
{
|
|
exit4_len = bpf_get_stack(ctx, &exit_stack4, SZ, BPF_F_USER_STACK);
|
|
return 0;
|
|
}
|
|
|
|
SEC("usdt//proc/self/exe:uretprobe_stack:target")
|
|
int BPF_USDT(usdt_probe)
|
|
{
|
|
usdt_len = bpf_get_stack(ctx, &usdt_stack, SZ, BPF_F_USER_STACK);
|
|
return 0;
|
|
}
|