|
11 | 11 | #include <linux/perf_event.h>
|
12 | 12 | #include <linux/slab.h>
|
13 | 13 | #include <linux/sched/task_stack.h>
|
| 14 | +#include <linux/uprobes.h> |
14 | 15 |
|
15 | 16 | #include "internal.h"
|
16 | 17 |
|
@@ -176,13 +177,51 @@ put_callchain_entry(int rctx)
|
176 | 177 | put_recursion_context(this_cpu_ptr(callchain_recursion), rctx);
|
177 | 178 | }
|
178 | 179 |
|
| 180 | +static void fixup_uretprobe_trampoline_entries(struct perf_callchain_entry *entry, |
| 181 | + int start_entry_idx) |
| 182 | +{ |
| 183 | +#ifdef CONFIG_UPROBES |
| 184 | + struct uprobe_task *utask = current->utask; |
| 185 | + struct return_instance *ri; |
| 186 | + __u64 *cur_ip, *last_ip, tramp_addr; |
| 187 | + |
| 188 | + if (likely(!utask || !utask->return_instances)) |
| 189 | + return; |
| 190 | + |
| 191 | + cur_ip = &entry->ip[start_entry_idx]; |
| 192 | + last_ip = &entry->ip[entry->nr - 1]; |
| 193 | + ri = utask->return_instances; |
| 194 | + tramp_addr = uprobe_get_trampoline_vaddr(); |
| 195 | + |
| 196 | + /* |
| 197 | + * If there are pending uretprobes for the current thread, they are |
| 198 | + * recorded in a list inside utask->return_instances; each such |
| 199 | + * pending uretprobe replaces traced user function's return address on |
| 200 | + * the stack, so when stack trace is captured, instead of seeing |
| 201 | + * actual function's return address, we'll have one or many uretprobe |
| 202 | + * trampoline addresses in the stack trace, which are not helpful and |
| 203 | + * misleading to users. |
| 204 | + * So here we go over the pending list of uretprobes, and each |
| 205 | + * encountered trampoline address is replaced with actual return |
| 206 | + * address. |
| 207 | + */ |
| 208 | + while (ri && cur_ip <= last_ip) { |
| 209 | + if (*cur_ip == tramp_addr) { |
| 210 | + *cur_ip = ri->orig_ret_vaddr; |
| 211 | + ri = ri->next; |
| 212 | + } |
| 213 | + cur_ip++; |
| 214 | + } |
| 215 | +#endif |
| 216 | +} |
| 217 | + |
179 | 218 | struct perf_callchain_entry *
|
180 | 219 | get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
|
181 | 220 | u32 max_stack, bool crosstask, bool add_mark)
|
182 | 221 | {
|
183 | 222 | struct perf_callchain_entry *entry;
|
184 | 223 | struct perf_callchain_entry_ctx ctx;
|
185 |
| - int rctx; |
| 224 | + int rctx, start_entry_idx; |
186 | 225 |
|
187 | 226 | entry = get_callchain_entry(&rctx);
|
188 | 227 | if (!entry)
|
@@ -215,7 +254,9 @@ get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
|
215 | 254 | if (add_mark)
|
216 | 255 | perf_callchain_store_context(&ctx, PERF_CONTEXT_USER);
|
217 | 256 |
|
| 257 | + start_entry_idx = entry->nr; |
218 | 258 | perf_callchain_user(&ctx, regs);
|
| 259 | + fixup_uretprobe_trampoline_entries(entry, start_entry_idx); |
219 | 260 | }
|
220 | 261 | }
|
221 | 262 |
|
|
0 commit comments