18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * thread-stack.c: Synthesize a thread's stack using call / return events 48c2ecf20Sopenharmony_ci * Copyright (c) 2014, Intel Corporation. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/rbtree.h> 88c2ecf20Sopenharmony_ci#include <linux/list.h> 98c2ecf20Sopenharmony_ci#include <linux/log2.h> 108c2ecf20Sopenharmony_ci#include <linux/zalloc.h> 118c2ecf20Sopenharmony_ci#include <errno.h> 128c2ecf20Sopenharmony_ci#include <stdlib.h> 138c2ecf20Sopenharmony_ci#include <string.h> 148c2ecf20Sopenharmony_ci#include "thread.h" 158c2ecf20Sopenharmony_ci#include "event.h" 168c2ecf20Sopenharmony_ci#include "machine.h" 178c2ecf20Sopenharmony_ci#include "env.h" 188c2ecf20Sopenharmony_ci#include "debug.h" 198c2ecf20Sopenharmony_ci#include "symbol.h" 208c2ecf20Sopenharmony_ci#include "comm.h" 218c2ecf20Sopenharmony_ci#include "call-path.h" 228c2ecf20Sopenharmony_ci#include "thread-stack.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define STACK_GROWTH 2048 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * State of retpoline detection. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * RETPOLINE_NONE: no retpoline detection 308c2ecf20Sopenharmony_ci * X86_RETPOLINE_POSSIBLE: x86 retpoline possible 318c2ecf20Sopenharmony_ci * X86_RETPOLINE_DETECTED: x86 retpoline detected 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_cienum retpoline_state_t { 348c2ecf20Sopenharmony_ci RETPOLINE_NONE, 358c2ecf20Sopenharmony_ci X86_RETPOLINE_POSSIBLE, 368c2ecf20Sopenharmony_ci X86_RETPOLINE_DETECTED, 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/** 408c2ecf20Sopenharmony_ci * struct thread_stack_entry - thread stack entry. 418c2ecf20Sopenharmony_ci * @ret_addr: return address 428c2ecf20Sopenharmony_ci * @timestamp: timestamp (if known) 438c2ecf20Sopenharmony_ci * @ref: external reference (e.g. db_id of sample) 448c2ecf20Sopenharmony_ci * @branch_count: the branch count when the entry was created 458c2ecf20Sopenharmony_ci * @insn_count: the instruction count when the entry was created 468c2ecf20Sopenharmony_ci * @cyc_count the cycle count when the entry was created 478c2ecf20Sopenharmony_ci * @db_id: id used for db-export 488c2ecf20Sopenharmony_ci * @cp: call path 498c2ecf20Sopenharmony_ci * @no_call: a 'call' was not seen 508c2ecf20Sopenharmony_ci * @trace_end: a 'call' but trace ended 518c2ecf20Sopenharmony_ci * @non_call: a branch but not a 'call' to the start of a different symbol 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistruct thread_stack_entry { 548c2ecf20Sopenharmony_ci u64 ret_addr; 558c2ecf20Sopenharmony_ci u64 timestamp; 568c2ecf20Sopenharmony_ci u64 ref; 578c2ecf20Sopenharmony_ci u64 branch_count; 588c2ecf20Sopenharmony_ci u64 insn_count; 598c2ecf20Sopenharmony_ci u64 cyc_count; 608c2ecf20Sopenharmony_ci u64 db_id; 618c2ecf20Sopenharmony_ci struct call_path *cp; 628c2ecf20Sopenharmony_ci bool no_call; 638c2ecf20Sopenharmony_ci bool trace_end; 648c2ecf20Sopenharmony_ci bool non_call; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/** 688c2ecf20Sopenharmony_ci * struct thread_stack - thread stack constructed from 'call' and 'return' 698c2ecf20Sopenharmony_ci * branch samples. 708c2ecf20Sopenharmony_ci * @stack: array that holds the stack 718c2ecf20Sopenharmony_ci * @cnt: number of entries in the stack 728c2ecf20Sopenharmony_ci * @sz: current maximum stack size 738c2ecf20Sopenharmony_ci * @trace_nr: current trace number 748c2ecf20Sopenharmony_ci * @branch_count: running branch count 758c2ecf20Sopenharmony_ci * @insn_count: running instruction count 768c2ecf20Sopenharmony_ci * @cyc_count running cycle count 778c2ecf20Sopenharmony_ci * @kernel_start: kernel start address 788c2ecf20Sopenharmony_ci * @last_time: last timestamp 798c2ecf20Sopenharmony_ci * @crp: call/return processor 808c2ecf20Sopenharmony_ci * @comm: current comm 818c2ecf20Sopenharmony_ci * @arr_sz: size of array if this is the first element of an array 828c2ecf20Sopenharmony_ci * @rstate: used to detect retpolines 838c2ecf20Sopenharmony_ci * @br_stack_rb: branch stack (ring buffer) 848c2ecf20Sopenharmony_ci * @br_stack_sz: maximum branch stack size 858c2ecf20Sopenharmony_ci * @br_stack_pos: current position in @br_stack_rb 868c2ecf20Sopenharmony_ci * @mispred_all: mark all branches as mispredicted 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_cistruct thread_stack { 898c2ecf20Sopenharmony_ci struct thread_stack_entry *stack; 908c2ecf20Sopenharmony_ci size_t cnt; 918c2ecf20Sopenharmony_ci size_t sz; 928c2ecf20Sopenharmony_ci u64 trace_nr; 938c2ecf20Sopenharmony_ci u64 branch_count; 948c2ecf20Sopenharmony_ci u64 insn_count; 958c2ecf20Sopenharmony_ci u64 cyc_count; 968c2ecf20Sopenharmony_ci u64 kernel_start; 978c2ecf20Sopenharmony_ci u64 last_time; 988c2ecf20Sopenharmony_ci struct call_return_processor *crp; 998c2ecf20Sopenharmony_ci struct comm *comm; 1008c2ecf20Sopenharmony_ci unsigned int arr_sz; 1018c2ecf20Sopenharmony_ci enum retpoline_state_t rstate; 1028c2ecf20Sopenharmony_ci struct branch_stack *br_stack_rb; 1038c2ecf20Sopenharmony_ci unsigned int br_stack_sz; 1048c2ecf20Sopenharmony_ci unsigned int br_stack_pos; 1058c2ecf20Sopenharmony_ci bool mispred_all; 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* 1098c2ecf20Sopenharmony_ci * Assume pid == tid == 0 identifies the idle task as defined by 1108c2ecf20Sopenharmony_ci * perf_session__register_idle_thread(). The idle task is really 1 task per cpu, 1118c2ecf20Sopenharmony_ci * and therefore requires a stack for each cpu. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistatic inline bool thread_stack__per_cpu(struct thread *thread) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci return !(thread->tid || thread->pid_); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int thread_stack__grow(struct thread_stack *ts) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct thread_stack_entry *new_stack; 1218c2ecf20Sopenharmony_ci size_t sz, new_sz; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci new_sz = ts->sz + STACK_GROWTH; 1248c2ecf20Sopenharmony_ci sz = new_sz * sizeof(struct thread_stack_entry); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci new_stack = realloc(ts->stack, sz); 1278c2ecf20Sopenharmony_ci if (!new_stack) 1288c2ecf20Sopenharmony_ci return -ENOMEM; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci ts->stack = new_stack; 1318c2ecf20Sopenharmony_ci ts->sz = new_sz; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int thread_stack__init(struct thread_stack *ts, struct thread *thread, 1378c2ecf20Sopenharmony_ci struct call_return_processor *crp, 1388c2ecf20Sopenharmony_ci bool callstack, unsigned int br_stack_sz) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int err; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (callstack) { 1438c2ecf20Sopenharmony_ci err = thread_stack__grow(ts); 1448c2ecf20Sopenharmony_ci if (err) 1458c2ecf20Sopenharmony_ci return err; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (br_stack_sz) { 1498c2ecf20Sopenharmony_ci size_t sz = sizeof(struct branch_stack); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci sz += br_stack_sz * sizeof(struct branch_entry); 1528c2ecf20Sopenharmony_ci ts->br_stack_rb = zalloc(sz); 1538c2ecf20Sopenharmony_ci if (!ts->br_stack_rb) 1548c2ecf20Sopenharmony_ci return -ENOMEM; 1558c2ecf20Sopenharmony_ci ts->br_stack_sz = br_stack_sz; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (thread->maps && thread->maps->machine) { 1598c2ecf20Sopenharmony_ci struct machine *machine = thread->maps->machine; 1608c2ecf20Sopenharmony_ci const char *arch = perf_env__arch(machine->env); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci ts->kernel_start = machine__kernel_start(machine); 1638c2ecf20Sopenharmony_ci if (!strcmp(arch, "x86")) 1648c2ecf20Sopenharmony_ci ts->rstate = X86_RETPOLINE_POSSIBLE; 1658c2ecf20Sopenharmony_ci } else { 1668c2ecf20Sopenharmony_ci ts->kernel_start = 1ULL << 63; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci ts->crp = crp; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic struct thread_stack *thread_stack__new(struct thread *thread, int cpu, 1748c2ecf20Sopenharmony_ci struct call_return_processor *crp, 1758c2ecf20Sopenharmony_ci bool callstack, 1768c2ecf20Sopenharmony_ci unsigned int br_stack_sz) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct thread_stack *ts = thread->ts, *new_ts; 1798c2ecf20Sopenharmony_ci unsigned int old_sz = ts ? ts->arr_sz : 0; 1808c2ecf20Sopenharmony_ci unsigned int new_sz = 1; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (thread_stack__per_cpu(thread) && cpu > 0) 1838c2ecf20Sopenharmony_ci new_sz = roundup_pow_of_two(cpu + 1); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (!ts || new_sz > old_sz) { 1868c2ecf20Sopenharmony_ci new_ts = calloc(new_sz, sizeof(*ts)); 1878c2ecf20Sopenharmony_ci if (!new_ts) 1888c2ecf20Sopenharmony_ci return NULL; 1898c2ecf20Sopenharmony_ci if (ts) 1908c2ecf20Sopenharmony_ci memcpy(new_ts, ts, old_sz * sizeof(*ts)); 1918c2ecf20Sopenharmony_ci new_ts->arr_sz = new_sz; 1928c2ecf20Sopenharmony_ci zfree(&thread->ts); 1938c2ecf20Sopenharmony_ci thread->ts = new_ts; 1948c2ecf20Sopenharmony_ci ts = new_ts; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (thread_stack__per_cpu(thread) && cpu > 0 && 1988c2ecf20Sopenharmony_ci (unsigned int)cpu < ts->arr_sz) 1998c2ecf20Sopenharmony_ci ts += cpu; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (!ts->stack && 2028c2ecf20Sopenharmony_ci thread_stack__init(ts, thread, crp, callstack, br_stack_sz)) 2038c2ecf20Sopenharmony_ci return NULL; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return ts; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic struct thread_stack *thread__cpu_stack(struct thread *thread, int cpu) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct thread_stack *ts = thread->ts; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (cpu < 0) 2138c2ecf20Sopenharmony_ci cpu = 0; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (!ts || (unsigned int)cpu >= ts->arr_sz) 2168c2ecf20Sopenharmony_ci return NULL; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ts += cpu; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (!ts->stack) 2218c2ecf20Sopenharmony_ci return NULL; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return ts; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic inline struct thread_stack *thread__stack(struct thread *thread, 2278c2ecf20Sopenharmony_ci int cpu) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci if (!thread) 2308c2ecf20Sopenharmony_ci return NULL; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (thread_stack__per_cpu(thread)) 2338c2ecf20Sopenharmony_ci return thread__cpu_stack(thread, cpu); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return thread->ts; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int thread_stack__push(struct thread_stack *ts, u64 ret_addr, 2398c2ecf20Sopenharmony_ci bool trace_end) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci int err = 0; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (ts->cnt == ts->sz) { 2448c2ecf20Sopenharmony_ci err = thread_stack__grow(ts); 2458c2ecf20Sopenharmony_ci if (err) { 2468c2ecf20Sopenharmony_ci pr_warning("Out of memory: discarding thread stack\n"); 2478c2ecf20Sopenharmony_ci ts->cnt = 0; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci ts->stack[ts->cnt].trace_end = trace_end; 2528c2ecf20Sopenharmony_ci ts->stack[ts->cnt++].ret_addr = ret_addr; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return err; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void thread_stack__pop(struct thread_stack *ts, u64 ret_addr) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci size_t i; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* 2628c2ecf20Sopenharmony_ci * In some cases there may be functions which are not seen to return. 2638c2ecf20Sopenharmony_ci * For example when setjmp / longjmp has been used. Or the perf context 2648c2ecf20Sopenharmony_ci * switch in the kernel which doesn't stop and start tracing in exactly 2658c2ecf20Sopenharmony_ci * the same code path. When that happens the return address will be 2668c2ecf20Sopenharmony_ci * further down the stack. If the return address is not found at all, 2678c2ecf20Sopenharmony_ci * we assume the opposite (i.e. this is a return for a call that wasn't 2688c2ecf20Sopenharmony_ci * seen for some reason) and leave the stack alone. 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_ci for (i = ts->cnt; i; ) { 2718c2ecf20Sopenharmony_ci if (ts->stack[--i].ret_addr == ret_addr) { 2728c2ecf20Sopenharmony_ci ts->cnt = i; 2738c2ecf20Sopenharmony_ci return; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic void thread_stack__pop_trace_end(struct thread_stack *ts) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci size_t i; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci for (i = ts->cnt; i; ) { 2838c2ecf20Sopenharmony_ci if (ts->stack[--i].trace_end) 2848c2ecf20Sopenharmony_ci ts->cnt = i; 2858c2ecf20Sopenharmony_ci else 2868c2ecf20Sopenharmony_ci return; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic bool thread_stack__in_kernel(struct thread_stack *ts) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci if (!ts->cnt) 2938c2ecf20Sopenharmony_ci return false; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return ts->stack[ts->cnt - 1].cp->in_kernel; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int thread_stack__call_return(struct thread *thread, 2998c2ecf20Sopenharmony_ci struct thread_stack *ts, size_t idx, 3008c2ecf20Sopenharmony_ci u64 timestamp, u64 ref, bool no_return) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct call_return_processor *crp = ts->crp; 3038c2ecf20Sopenharmony_ci struct thread_stack_entry *tse; 3048c2ecf20Sopenharmony_ci struct call_return cr = { 3058c2ecf20Sopenharmony_ci .thread = thread, 3068c2ecf20Sopenharmony_ci .comm = ts->comm, 3078c2ecf20Sopenharmony_ci .db_id = 0, 3088c2ecf20Sopenharmony_ci }; 3098c2ecf20Sopenharmony_ci u64 *parent_db_id; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci tse = &ts->stack[idx]; 3128c2ecf20Sopenharmony_ci cr.cp = tse->cp; 3138c2ecf20Sopenharmony_ci cr.call_time = tse->timestamp; 3148c2ecf20Sopenharmony_ci cr.return_time = timestamp; 3158c2ecf20Sopenharmony_ci cr.branch_count = ts->branch_count - tse->branch_count; 3168c2ecf20Sopenharmony_ci cr.insn_count = ts->insn_count - tse->insn_count; 3178c2ecf20Sopenharmony_ci cr.cyc_count = ts->cyc_count - tse->cyc_count; 3188c2ecf20Sopenharmony_ci cr.db_id = tse->db_id; 3198c2ecf20Sopenharmony_ci cr.call_ref = tse->ref; 3208c2ecf20Sopenharmony_ci cr.return_ref = ref; 3218c2ecf20Sopenharmony_ci if (tse->no_call) 3228c2ecf20Sopenharmony_ci cr.flags |= CALL_RETURN_NO_CALL; 3238c2ecf20Sopenharmony_ci if (no_return) 3248c2ecf20Sopenharmony_ci cr.flags |= CALL_RETURN_NO_RETURN; 3258c2ecf20Sopenharmony_ci if (tse->non_call) 3268c2ecf20Sopenharmony_ci cr.flags |= CALL_RETURN_NON_CALL; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* 3298c2ecf20Sopenharmony_ci * The parent db_id must be assigned before exporting the child. Note 3308c2ecf20Sopenharmony_ci * it is not possible to export the parent first because its information 3318c2ecf20Sopenharmony_ci * is not yet complete because its 'return' has not yet been processed. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci parent_db_id = idx ? &(tse - 1)->db_id : NULL; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return crp->process(&cr, parent_db_id, crp->data); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int __thread_stack__flush(struct thread *thread, struct thread_stack *ts) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct call_return_processor *crp = ts->crp; 3418c2ecf20Sopenharmony_ci int err; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (!crp) { 3448c2ecf20Sopenharmony_ci ts->cnt = 0; 3458c2ecf20Sopenharmony_ci ts->br_stack_pos = 0; 3468c2ecf20Sopenharmony_ci if (ts->br_stack_rb) 3478c2ecf20Sopenharmony_ci ts->br_stack_rb->nr = 0; 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci while (ts->cnt) { 3528c2ecf20Sopenharmony_ci err = thread_stack__call_return(thread, ts, --ts->cnt, 3538c2ecf20Sopenharmony_ci ts->last_time, 0, true); 3548c2ecf20Sopenharmony_ci if (err) { 3558c2ecf20Sopenharmony_ci pr_err("Error flushing thread stack!\n"); 3568c2ecf20Sopenharmony_ci ts->cnt = 0; 3578c2ecf20Sopenharmony_ci return err; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ciint thread_stack__flush(struct thread *thread) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct thread_stack *ts = thread->ts; 3678c2ecf20Sopenharmony_ci unsigned int pos; 3688c2ecf20Sopenharmony_ci int err = 0; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (ts) { 3718c2ecf20Sopenharmony_ci for (pos = 0; pos < ts->arr_sz; pos++) { 3728c2ecf20Sopenharmony_ci int ret = __thread_stack__flush(thread, ts + pos); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (ret) 3758c2ecf20Sopenharmony_ci err = ret; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return err; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic void thread_stack__update_br_stack(struct thread_stack *ts, u32 flags, 3838c2ecf20Sopenharmony_ci u64 from_ip, u64 to_ip) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct branch_stack *bs = ts->br_stack_rb; 3868c2ecf20Sopenharmony_ci struct branch_entry *be; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (!ts->br_stack_pos) 3898c2ecf20Sopenharmony_ci ts->br_stack_pos = ts->br_stack_sz; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci ts->br_stack_pos -= 1; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci be = &bs->entries[ts->br_stack_pos]; 3948c2ecf20Sopenharmony_ci be->from = from_ip; 3958c2ecf20Sopenharmony_ci be->to = to_ip; 3968c2ecf20Sopenharmony_ci be->flags.value = 0; 3978c2ecf20Sopenharmony_ci be->flags.abort = !!(flags & PERF_IP_FLAG_TX_ABORT); 3988c2ecf20Sopenharmony_ci be->flags.in_tx = !!(flags & PERF_IP_FLAG_IN_TX); 3998c2ecf20Sopenharmony_ci /* No support for mispredict */ 4008c2ecf20Sopenharmony_ci be->flags.mispred = ts->mispred_all; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (bs->nr < ts->br_stack_sz) 4038c2ecf20Sopenharmony_ci bs->nr += 1; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ciint thread_stack__event(struct thread *thread, int cpu, u32 flags, u64 from_ip, 4078c2ecf20Sopenharmony_ci u64 to_ip, u16 insn_len, u64 trace_nr, bool callstack, 4088c2ecf20Sopenharmony_ci unsigned int br_stack_sz, bool mispred_all) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct thread_stack *ts = thread__stack(thread, cpu); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (!thread) 4138c2ecf20Sopenharmony_ci return -EINVAL; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (!ts) { 4168c2ecf20Sopenharmony_ci ts = thread_stack__new(thread, cpu, NULL, callstack, br_stack_sz); 4178c2ecf20Sopenharmony_ci if (!ts) { 4188c2ecf20Sopenharmony_ci pr_warning("Out of memory: no thread stack\n"); 4198c2ecf20Sopenharmony_ci return -ENOMEM; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci ts->trace_nr = trace_nr; 4228c2ecf20Sopenharmony_ci ts->mispred_all = mispred_all; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* 4268c2ecf20Sopenharmony_ci * When the trace is discontinuous, the trace_nr changes. In that case 4278c2ecf20Sopenharmony_ci * the stack might be completely invalid. Better to report nothing than 4288c2ecf20Sopenharmony_ci * to report something misleading, so flush the stack. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci if (trace_nr != ts->trace_nr) { 4318c2ecf20Sopenharmony_ci if (ts->trace_nr) 4328c2ecf20Sopenharmony_ci __thread_stack__flush(thread, ts); 4338c2ecf20Sopenharmony_ci ts->trace_nr = trace_nr; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (br_stack_sz) 4378c2ecf20Sopenharmony_ci thread_stack__update_br_stack(ts, flags, from_ip, to_ip); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* 4408c2ecf20Sopenharmony_ci * Stop here if thread_stack__process() is in use, or not recording call 4418c2ecf20Sopenharmony_ci * stack. 4428c2ecf20Sopenharmony_ci */ 4438c2ecf20Sopenharmony_ci if (ts->crp || !callstack) 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (flags & PERF_IP_FLAG_CALL) { 4478c2ecf20Sopenharmony_ci u64 ret_addr; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (!to_ip) 4508c2ecf20Sopenharmony_ci return 0; 4518c2ecf20Sopenharmony_ci ret_addr = from_ip + insn_len; 4528c2ecf20Sopenharmony_ci if (ret_addr == to_ip) 4538c2ecf20Sopenharmony_ci return 0; /* Zero-length calls are excluded */ 4548c2ecf20Sopenharmony_ci return thread_stack__push(ts, ret_addr, 4558c2ecf20Sopenharmony_ci flags & PERF_IP_FLAG_TRACE_END); 4568c2ecf20Sopenharmony_ci } else if (flags & PERF_IP_FLAG_TRACE_BEGIN) { 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * If the caller did not change the trace number (which would 4598c2ecf20Sopenharmony_ci * have flushed the stack) then try to make sense of the stack. 4608c2ecf20Sopenharmony_ci * Possibly, tracing began after returning to the current 4618c2ecf20Sopenharmony_ci * address, so try to pop that. Also, do not expect a call made 4628c2ecf20Sopenharmony_ci * when the trace ended, to return, so pop that. 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_ci thread_stack__pop(ts, to_ip); 4658c2ecf20Sopenharmony_ci thread_stack__pop_trace_end(ts); 4668c2ecf20Sopenharmony_ci } else if ((flags & PERF_IP_FLAG_RETURN) && from_ip) { 4678c2ecf20Sopenharmony_ci thread_stack__pop(ts, to_ip); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_civoid thread_stack__set_trace_nr(struct thread *thread, int cpu, u64 trace_nr) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct thread_stack *ts = thread__stack(thread, cpu); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (!ts) 4788c2ecf20Sopenharmony_ci return; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (trace_nr != ts->trace_nr) { 4818c2ecf20Sopenharmony_ci if (ts->trace_nr) 4828c2ecf20Sopenharmony_ci __thread_stack__flush(thread, ts); 4838c2ecf20Sopenharmony_ci ts->trace_nr = trace_nr; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic void __thread_stack__free(struct thread *thread, struct thread_stack *ts) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci __thread_stack__flush(thread, ts); 4908c2ecf20Sopenharmony_ci zfree(&ts->stack); 4918c2ecf20Sopenharmony_ci zfree(&ts->br_stack_rb); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void thread_stack__reset(struct thread *thread, struct thread_stack *ts) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci unsigned int arr_sz = ts->arr_sz; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci __thread_stack__free(thread, ts); 4998c2ecf20Sopenharmony_ci memset(ts, 0, sizeof(*ts)); 5008c2ecf20Sopenharmony_ci ts->arr_sz = arr_sz; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_civoid thread_stack__free(struct thread *thread) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci struct thread_stack *ts = thread->ts; 5068c2ecf20Sopenharmony_ci unsigned int pos; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (ts) { 5098c2ecf20Sopenharmony_ci for (pos = 0; pos < ts->arr_sz; pos++) 5108c2ecf20Sopenharmony_ci __thread_stack__free(thread, ts + pos); 5118c2ecf20Sopenharmony_ci zfree(&thread->ts); 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic inline u64 callchain_context(u64 ip, u64 kernel_start) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci return ip < kernel_start ? PERF_CONTEXT_USER : PERF_CONTEXT_KERNEL; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_civoid thread_stack__sample(struct thread *thread, int cpu, 5218c2ecf20Sopenharmony_ci struct ip_callchain *chain, 5228c2ecf20Sopenharmony_ci size_t sz, u64 ip, u64 kernel_start) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct thread_stack *ts = thread__stack(thread, cpu); 5258c2ecf20Sopenharmony_ci u64 context = callchain_context(ip, kernel_start); 5268c2ecf20Sopenharmony_ci u64 last_context; 5278c2ecf20Sopenharmony_ci size_t i, j; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (sz < 2) { 5308c2ecf20Sopenharmony_ci chain->nr = 0; 5318c2ecf20Sopenharmony_ci return; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci chain->ips[0] = context; 5358c2ecf20Sopenharmony_ci chain->ips[1] = ip; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (!ts) { 5388c2ecf20Sopenharmony_ci chain->nr = 2; 5398c2ecf20Sopenharmony_ci return; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci last_context = context; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci for (i = 2, j = 1; i < sz && j <= ts->cnt; i++, j++) { 5458c2ecf20Sopenharmony_ci ip = ts->stack[ts->cnt - j].ret_addr; 5468c2ecf20Sopenharmony_ci context = callchain_context(ip, kernel_start); 5478c2ecf20Sopenharmony_ci if (context != last_context) { 5488c2ecf20Sopenharmony_ci if (i >= sz - 1) 5498c2ecf20Sopenharmony_ci break; 5508c2ecf20Sopenharmony_ci chain->ips[i++] = context; 5518c2ecf20Sopenharmony_ci last_context = context; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci chain->ips[i] = ip; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci chain->nr = i; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci/* 5608c2ecf20Sopenharmony_ci * Hardware sample records, created some time after the event occurred, need to 5618c2ecf20Sopenharmony_ci * have subsequent addresses removed from the call chain. 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_civoid thread_stack__sample_late(struct thread *thread, int cpu, 5648c2ecf20Sopenharmony_ci struct ip_callchain *chain, size_t sz, 5658c2ecf20Sopenharmony_ci u64 sample_ip, u64 kernel_start) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct thread_stack *ts = thread__stack(thread, cpu); 5688c2ecf20Sopenharmony_ci u64 sample_context = callchain_context(sample_ip, kernel_start); 5698c2ecf20Sopenharmony_ci u64 last_context, context, ip; 5708c2ecf20Sopenharmony_ci size_t nr = 0, j; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (sz < 2) { 5738c2ecf20Sopenharmony_ci chain->nr = 0; 5748c2ecf20Sopenharmony_ci return; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (!ts) 5788c2ecf20Sopenharmony_ci goto out; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* 5818c2ecf20Sopenharmony_ci * When tracing kernel space, kernel addresses occur at the top of the 5828c2ecf20Sopenharmony_ci * call chain after the event occurred but before tracing stopped. 5838c2ecf20Sopenharmony_ci * Skip them. 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_ci for (j = 1; j <= ts->cnt; j++) { 5868c2ecf20Sopenharmony_ci ip = ts->stack[ts->cnt - j].ret_addr; 5878c2ecf20Sopenharmony_ci context = callchain_context(ip, kernel_start); 5888c2ecf20Sopenharmony_ci if (context == PERF_CONTEXT_USER || 5898c2ecf20Sopenharmony_ci (context == sample_context && ip == sample_ip)) 5908c2ecf20Sopenharmony_ci break; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci last_context = sample_ip; /* Use sample_ip as an invalid context */ 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci for (; nr < sz && j <= ts->cnt; nr++, j++) { 5968c2ecf20Sopenharmony_ci ip = ts->stack[ts->cnt - j].ret_addr; 5978c2ecf20Sopenharmony_ci context = callchain_context(ip, kernel_start); 5988c2ecf20Sopenharmony_ci if (context != last_context) { 5998c2ecf20Sopenharmony_ci if (nr >= sz - 1) 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci chain->ips[nr++] = context; 6028c2ecf20Sopenharmony_ci last_context = context; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci chain->ips[nr] = ip; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ciout: 6078c2ecf20Sopenharmony_ci if (nr) { 6088c2ecf20Sopenharmony_ci chain->nr = nr; 6098c2ecf20Sopenharmony_ci } else { 6108c2ecf20Sopenharmony_ci chain->ips[0] = sample_context; 6118c2ecf20Sopenharmony_ci chain->ips[1] = sample_ip; 6128c2ecf20Sopenharmony_ci chain->nr = 2; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_civoid thread_stack__br_sample(struct thread *thread, int cpu, 6178c2ecf20Sopenharmony_ci struct branch_stack *dst, unsigned int sz) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct thread_stack *ts = thread__stack(thread, cpu); 6208c2ecf20Sopenharmony_ci const size_t bsz = sizeof(struct branch_entry); 6218c2ecf20Sopenharmony_ci struct branch_stack *src; 6228c2ecf20Sopenharmony_ci struct branch_entry *be; 6238c2ecf20Sopenharmony_ci unsigned int nr; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci dst->nr = 0; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (!ts) 6288c2ecf20Sopenharmony_ci return; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci src = ts->br_stack_rb; 6318c2ecf20Sopenharmony_ci if (!src->nr) 6328c2ecf20Sopenharmony_ci return; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci dst->nr = min((unsigned int)src->nr, sz); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci be = &dst->entries[0]; 6378c2ecf20Sopenharmony_ci nr = min(ts->br_stack_sz - ts->br_stack_pos, (unsigned int)dst->nr); 6388c2ecf20Sopenharmony_ci memcpy(be, &src->entries[ts->br_stack_pos], bsz * nr); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (src->nr >= ts->br_stack_sz) { 6418c2ecf20Sopenharmony_ci sz -= nr; 6428c2ecf20Sopenharmony_ci be = &dst->entries[nr]; 6438c2ecf20Sopenharmony_ci nr = min(ts->br_stack_pos, sz); 6448c2ecf20Sopenharmony_ci memcpy(be, &src->entries[0], bsz * ts->br_stack_pos); 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci/* Start of user space branch entries */ 6498c2ecf20Sopenharmony_cistatic bool us_start(struct branch_entry *be, u64 kernel_start, bool *start) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci if (!*start) 6528c2ecf20Sopenharmony_ci *start = be->to && be->to < kernel_start; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci return *start; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci/* 6588c2ecf20Sopenharmony_ci * Start of branch entries after the ip fell in between 2 branches, or user 6598c2ecf20Sopenharmony_ci * space branch entries. 6608c2ecf20Sopenharmony_ci */ 6618c2ecf20Sopenharmony_cistatic bool ks_start(struct branch_entry *be, u64 sample_ip, u64 kernel_start, 6628c2ecf20Sopenharmony_ci bool *start, struct branch_entry *nb) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci if (!*start) { 6658c2ecf20Sopenharmony_ci *start = (nb && sample_ip >= be->to && sample_ip <= nb->from) || 6668c2ecf20Sopenharmony_ci be->from < kernel_start || 6678c2ecf20Sopenharmony_ci (be->to && be->to < kernel_start); 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci return *start; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci/* 6748c2ecf20Sopenharmony_ci * Hardware sample records, created some time after the event occurred, need to 6758c2ecf20Sopenharmony_ci * have subsequent addresses removed from the branch stack. 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_civoid thread_stack__br_sample_late(struct thread *thread, int cpu, 6788c2ecf20Sopenharmony_ci struct branch_stack *dst, unsigned int sz, 6798c2ecf20Sopenharmony_ci u64 ip, u64 kernel_start) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct thread_stack *ts = thread__stack(thread, cpu); 6828c2ecf20Sopenharmony_ci struct branch_entry *d, *s, *spos, *ssz; 6838c2ecf20Sopenharmony_ci struct branch_stack *src; 6848c2ecf20Sopenharmony_ci unsigned int nr = 0; 6858c2ecf20Sopenharmony_ci bool start = false; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci dst->nr = 0; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (!ts) 6908c2ecf20Sopenharmony_ci return; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci src = ts->br_stack_rb; 6938c2ecf20Sopenharmony_ci if (!src->nr) 6948c2ecf20Sopenharmony_ci return; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci spos = &src->entries[ts->br_stack_pos]; 6978c2ecf20Sopenharmony_ci ssz = &src->entries[ts->br_stack_sz]; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci d = &dst->entries[0]; 7008c2ecf20Sopenharmony_ci s = spos; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (ip < kernel_start) { 7038c2ecf20Sopenharmony_ci /* 7048c2ecf20Sopenharmony_ci * User space sample: start copying branch entries when the 7058c2ecf20Sopenharmony_ci * branch is in user space. 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ci for (s = spos; s < ssz && nr < sz; s++) { 7088c2ecf20Sopenharmony_ci if (us_start(s, kernel_start, &start)) { 7098c2ecf20Sopenharmony_ci *d++ = *s; 7108c2ecf20Sopenharmony_ci nr += 1; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (src->nr >= ts->br_stack_sz) { 7158c2ecf20Sopenharmony_ci for (s = &src->entries[0]; s < spos && nr < sz; s++) { 7168c2ecf20Sopenharmony_ci if (us_start(s, kernel_start, &start)) { 7178c2ecf20Sopenharmony_ci *d++ = *s; 7188c2ecf20Sopenharmony_ci nr += 1; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci } else { 7238c2ecf20Sopenharmony_ci struct branch_entry *nb = NULL; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* 7268c2ecf20Sopenharmony_ci * Kernel space sample: start copying branch entries when the ip 7278c2ecf20Sopenharmony_ci * falls in between 2 branches (or the branch is in user space 7288c2ecf20Sopenharmony_ci * because then the start must have been missed). 7298c2ecf20Sopenharmony_ci */ 7308c2ecf20Sopenharmony_ci for (s = spos; s < ssz && nr < sz; s++) { 7318c2ecf20Sopenharmony_ci if (ks_start(s, ip, kernel_start, &start, nb)) { 7328c2ecf20Sopenharmony_ci *d++ = *s; 7338c2ecf20Sopenharmony_ci nr += 1; 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci nb = s; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (src->nr >= ts->br_stack_sz) { 7398c2ecf20Sopenharmony_ci for (s = &src->entries[0]; s < spos && nr < sz; s++) { 7408c2ecf20Sopenharmony_ci if (ks_start(s, ip, kernel_start, &start, nb)) { 7418c2ecf20Sopenharmony_ci *d++ = *s; 7428c2ecf20Sopenharmony_ci nr += 1; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci nb = s; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci dst->nr = nr; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistruct call_return_processor * 7538c2ecf20Sopenharmony_cicall_return_processor__new(int (*process)(struct call_return *cr, u64 *parent_db_id, void *data), 7548c2ecf20Sopenharmony_ci void *data) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci struct call_return_processor *crp; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci crp = zalloc(sizeof(struct call_return_processor)); 7598c2ecf20Sopenharmony_ci if (!crp) 7608c2ecf20Sopenharmony_ci return NULL; 7618c2ecf20Sopenharmony_ci crp->cpr = call_path_root__new(); 7628c2ecf20Sopenharmony_ci if (!crp->cpr) 7638c2ecf20Sopenharmony_ci goto out_free; 7648c2ecf20Sopenharmony_ci crp->process = process; 7658c2ecf20Sopenharmony_ci crp->data = data; 7668c2ecf20Sopenharmony_ci return crp; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ciout_free: 7698c2ecf20Sopenharmony_ci free(crp); 7708c2ecf20Sopenharmony_ci return NULL; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_civoid call_return_processor__free(struct call_return_processor *crp) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci if (crp) { 7768c2ecf20Sopenharmony_ci call_path_root__free(crp->cpr); 7778c2ecf20Sopenharmony_ci free(crp); 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr, 7828c2ecf20Sopenharmony_ci u64 timestamp, u64 ref, struct call_path *cp, 7838c2ecf20Sopenharmony_ci bool no_call, bool trace_end) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct thread_stack_entry *tse; 7868c2ecf20Sopenharmony_ci int err; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (!cp) 7898c2ecf20Sopenharmony_ci return -ENOMEM; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (ts->cnt == ts->sz) { 7928c2ecf20Sopenharmony_ci err = thread_stack__grow(ts); 7938c2ecf20Sopenharmony_ci if (err) 7948c2ecf20Sopenharmony_ci return err; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci tse = &ts->stack[ts->cnt++]; 7988c2ecf20Sopenharmony_ci tse->ret_addr = ret_addr; 7998c2ecf20Sopenharmony_ci tse->timestamp = timestamp; 8008c2ecf20Sopenharmony_ci tse->ref = ref; 8018c2ecf20Sopenharmony_ci tse->branch_count = ts->branch_count; 8028c2ecf20Sopenharmony_ci tse->insn_count = ts->insn_count; 8038c2ecf20Sopenharmony_ci tse->cyc_count = ts->cyc_count; 8048c2ecf20Sopenharmony_ci tse->cp = cp; 8058c2ecf20Sopenharmony_ci tse->no_call = no_call; 8068c2ecf20Sopenharmony_ci tse->trace_end = trace_end; 8078c2ecf20Sopenharmony_ci tse->non_call = false; 8088c2ecf20Sopenharmony_ci tse->db_id = 0; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci return 0; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts, 8148c2ecf20Sopenharmony_ci u64 ret_addr, u64 timestamp, u64 ref, 8158c2ecf20Sopenharmony_ci struct symbol *sym) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci int err; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if (!ts->cnt) 8208c2ecf20Sopenharmony_ci return 1; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (ts->cnt == 1) { 8238c2ecf20Sopenharmony_ci struct thread_stack_entry *tse = &ts->stack[0]; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if (tse->cp->sym == sym) 8268c2ecf20Sopenharmony_ci return thread_stack__call_return(thread, ts, --ts->cnt, 8278c2ecf20Sopenharmony_ci timestamp, ref, false); 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (ts->stack[ts->cnt - 1].ret_addr == ret_addr && 8318c2ecf20Sopenharmony_ci !ts->stack[ts->cnt - 1].non_call) { 8328c2ecf20Sopenharmony_ci return thread_stack__call_return(thread, ts, --ts->cnt, 8338c2ecf20Sopenharmony_ci timestamp, ref, false); 8348c2ecf20Sopenharmony_ci } else { 8358c2ecf20Sopenharmony_ci size_t i = ts->cnt - 1; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci while (i--) { 8388c2ecf20Sopenharmony_ci if (ts->stack[i].ret_addr != ret_addr || 8398c2ecf20Sopenharmony_ci ts->stack[i].non_call) 8408c2ecf20Sopenharmony_ci continue; 8418c2ecf20Sopenharmony_ci i += 1; 8428c2ecf20Sopenharmony_ci while (ts->cnt > i) { 8438c2ecf20Sopenharmony_ci err = thread_stack__call_return(thread, ts, 8448c2ecf20Sopenharmony_ci --ts->cnt, 8458c2ecf20Sopenharmony_ci timestamp, ref, 8468c2ecf20Sopenharmony_ci true); 8478c2ecf20Sopenharmony_ci if (err) 8488c2ecf20Sopenharmony_ci return err; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci return thread_stack__call_return(thread, ts, --ts->cnt, 8518c2ecf20Sopenharmony_ci timestamp, ref, false); 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return 1; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic int thread_stack__bottom(struct thread_stack *ts, 8598c2ecf20Sopenharmony_ci struct perf_sample *sample, 8608c2ecf20Sopenharmony_ci struct addr_location *from_al, 8618c2ecf20Sopenharmony_ci struct addr_location *to_al, u64 ref) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci struct call_path_root *cpr = ts->crp->cpr; 8648c2ecf20Sopenharmony_ci struct call_path *cp; 8658c2ecf20Sopenharmony_ci struct symbol *sym; 8668c2ecf20Sopenharmony_ci u64 ip; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (sample->ip) { 8698c2ecf20Sopenharmony_ci ip = sample->ip; 8708c2ecf20Sopenharmony_ci sym = from_al->sym; 8718c2ecf20Sopenharmony_ci } else if (sample->addr) { 8728c2ecf20Sopenharmony_ci ip = sample->addr; 8738c2ecf20Sopenharmony_ci sym = to_al->sym; 8748c2ecf20Sopenharmony_ci } else { 8758c2ecf20Sopenharmony_ci return 0; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci cp = call_path__findnew(cpr, &cpr->call_path, sym, ip, 8798c2ecf20Sopenharmony_ci ts->kernel_start); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci return thread_stack__push_cp(ts, ip, sample->time, ref, cp, 8828c2ecf20Sopenharmony_ci true, false); 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic int thread_stack__pop_ks(struct thread *thread, struct thread_stack *ts, 8868c2ecf20Sopenharmony_ci struct perf_sample *sample, u64 ref) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci u64 tm = sample->time; 8898c2ecf20Sopenharmony_ci int err; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci /* Return to userspace, so pop all kernel addresses */ 8928c2ecf20Sopenharmony_ci while (thread_stack__in_kernel(ts)) { 8938c2ecf20Sopenharmony_ci err = thread_stack__call_return(thread, ts, --ts->cnt, 8948c2ecf20Sopenharmony_ci tm, ref, true); 8958c2ecf20Sopenharmony_ci if (err) 8968c2ecf20Sopenharmony_ci return err; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return 0; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic int thread_stack__no_call_return(struct thread *thread, 9038c2ecf20Sopenharmony_ci struct thread_stack *ts, 9048c2ecf20Sopenharmony_ci struct perf_sample *sample, 9058c2ecf20Sopenharmony_ci struct addr_location *from_al, 9068c2ecf20Sopenharmony_ci struct addr_location *to_al, u64 ref) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci struct call_path_root *cpr = ts->crp->cpr; 9098c2ecf20Sopenharmony_ci struct call_path *root = &cpr->call_path; 9108c2ecf20Sopenharmony_ci struct symbol *fsym = from_al->sym; 9118c2ecf20Sopenharmony_ci struct symbol *tsym = to_al->sym; 9128c2ecf20Sopenharmony_ci struct call_path *cp, *parent; 9138c2ecf20Sopenharmony_ci u64 ks = ts->kernel_start; 9148c2ecf20Sopenharmony_ci u64 addr = sample->addr; 9158c2ecf20Sopenharmony_ci u64 tm = sample->time; 9168c2ecf20Sopenharmony_ci u64 ip = sample->ip; 9178c2ecf20Sopenharmony_ci int err; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci if (ip >= ks && addr < ks) { 9208c2ecf20Sopenharmony_ci /* Return to userspace, so pop all kernel addresses */ 9218c2ecf20Sopenharmony_ci err = thread_stack__pop_ks(thread, ts, sample, ref); 9228c2ecf20Sopenharmony_ci if (err) 9238c2ecf20Sopenharmony_ci return err; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci /* If the stack is empty, push the userspace address */ 9268c2ecf20Sopenharmony_ci if (!ts->cnt) { 9278c2ecf20Sopenharmony_ci cp = call_path__findnew(cpr, root, tsym, addr, ks); 9288c2ecf20Sopenharmony_ci return thread_stack__push_cp(ts, 0, tm, ref, cp, true, 9298c2ecf20Sopenharmony_ci false); 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci } else if (thread_stack__in_kernel(ts) && ip < ks) { 9328c2ecf20Sopenharmony_ci /* Return to userspace, so pop all kernel addresses */ 9338c2ecf20Sopenharmony_ci err = thread_stack__pop_ks(thread, ts, sample, ref); 9348c2ecf20Sopenharmony_ci if (err) 9358c2ecf20Sopenharmony_ci return err; 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci if (ts->cnt) 9398c2ecf20Sopenharmony_ci parent = ts->stack[ts->cnt - 1].cp; 9408c2ecf20Sopenharmony_ci else 9418c2ecf20Sopenharmony_ci parent = root; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (parent->sym == from_al->sym) { 9448c2ecf20Sopenharmony_ci /* 9458c2ecf20Sopenharmony_ci * At the bottom of the stack, assume the missing 'call' was 9468c2ecf20Sopenharmony_ci * before the trace started. So, pop the current symbol and push 9478c2ecf20Sopenharmony_ci * the 'to' symbol. 9488c2ecf20Sopenharmony_ci */ 9498c2ecf20Sopenharmony_ci if (ts->cnt == 1) { 9508c2ecf20Sopenharmony_ci err = thread_stack__call_return(thread, ts, --ts->cnt, 9518c2ecf20Sopenharmony_ci tm, ref, false); 9528c2ecf20Sopenharmony_ci if (err) 9538c2ecf20Sopenharmony_ci return err; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci if (!ts->cnt) { 9578c2ecf20Sopenharmony_ci cp = call_path__findnew(cpr, root, tsym, addr, ks); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci return thread_stack__push_cp(ts, addr, tm, ref, cp, 9608c2ecf20Sopenharmony_ci true, false); 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci /* 9648c2ecf20Sopenharmony_ci * Otherwise assume the 'return' is being used as a jump (e.g. 9658c2ecf20Sopenharmony_ci * retpoline) and just push the 'to' symbol. 9668c2ecf20Sopenharmony_ci */ 9678c2ecf20Sopenharmony_ci cp = call_path__findnew(cpr, parent, tsym, addr, ks); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci err = thread_stack__push_cp(ts, 0, tm, ref, cp, true, false); 9708c2ecf20Sopenharmony_ci if (!err) 9718c2ecf20Sopenharmony_ci ts->stack[ts->cnt - 1].non_call = true; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci return err; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* 9778c2ecf20Sopenharmony_ci * Assume 'parent' has not yet returned, so push 'to', and then push and 9788c2ecf20Sopenharmony_ci * pop 'from'. 9798c2ecf20Sopenharmony_ci */ 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci cp = call_path__findnew(cpr, parent, tsym, addr, ks); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci err = thread_stack__push_cp(ts, addr, tm, ref, cp, true, false); 9848c2ecf20Sopenharmony_ci if (err) 9858c2ecf20Sopenharmony_ci return err; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci cp = call_path__findnew(cpr, cp, fsym, ip, ks); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci err = thread_stack__push_cp(ts, ip, tm, ref, cp, true, false); 9908c2ecf20Sopenharmony_ci if (err) 9918c2ecf20Sopenharmony_ci return err; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci return thread_stack__call_return(thread, ts, --ts->cnt, tm, ref, false); 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int thread_stack__trace_begin(struct thread *thread, 9978c2ecf20Sopenharmony_ci struct thread_stack *ts, u64 timestamp, 9988c2ecf20Sopenharmony_ci u64 ref) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci struct thread_stack_entry *tse; 10018c2ecf20Sopenharmony_ci int err; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (!ts->cnt) 10048c2ecf20Sopenharmony_ci return 0; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci /* Pop trace end */ 10078c2ecf20Sopenharmony_ci tse = &ts->stack[ts->cnt - 1]; 10088c2ecf20Sopenharmony_ci if (tse->trace_end) { 10098c2ecf20Sopenharmony_ci err = thread_stack__call_return(thread, ts, --ts->cnt, 10108c2ecf20Sopenharmony_ci timestamp, ref, false); 10118c2ecf20Sopenharmony_ci if (err) 10128c2ecf20Sopenharmony_ci return err; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci return 0; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cistatic int thread_stack__trace_end(struct thread_stack *ts, 10198c2ecf20Sopenharmony_ci struct perf_sample *sample, u64 ref) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci struct call_path_root *cpr = ts->crp->cpr; 10228c2ecf20Sopenharmony_ci struct call_path *cp; 10238c2ecf20Sopenharmony_ci u64 ret_addr; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* No point having 'trace end' on the bottom of the stack */ 10268c2ecf20Sopenharmony_ci if (!ts->cnt || (ts->cnt == 1 && ts->stack[0].ref == ref)) 10278c2ecf20Sopenharmony_ci return 0; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, NULL, 0, 10308c2ecf20Sopenharmony_ci ts->kernel_start); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci ret_addr = sample->ip + sample->insn_len; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci return thread_stack__push_cp(ts, ret_addr, sample->time, ref, cp, 10358c2ecf20Sopenharmony_ci false, true); 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic bool is_x86_retpoline(const char *name) 10398c2ecf20Sopenharmony_ci{ 10408c2ecf20Sopenharmony_ci const char *p = strstr(name, "__x86_indirect_thunk_"); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci return p == name || !strcmp(name, "__indirect_thunk_start"); 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci/* 10468c2ecf20Sopenharmony_ci * x86 retpoline functions pollute the call graph. This function removes them. 10478c2ecf20Sopenharmony_ci * This does not handle function return thunks, nor is there any improvement 10488c2ecf20Sopenharmony_ci * for the handling of inline thunks or extern thunks. 10498c2ecf20Sopenharmony_ci */ 10508c2ecf20Sopenharmony_cistatic int thread_stack__x86_retpoline(struct thread_stack *ts, 10518c2ecf20Sopenharmony_ci struct perf_sample *sample, 10528c2ecf20Sopenharmony_ci struct addr_location *to_al) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci struct thread_stack_entry *tse = &ts->stack[ts->cnt - 1]; 10558c2ecf20Sopenharmony_ci struct call_path_root *cpr = ts->crp->cpr; 10568c2ecf20Sopenharmony_ci struct symbol *sym = tse->cp->sym; 10578c2ecf20Sopenharmony_ci struct symbol *tsym = to_al->sym; 10588c2ecf20Sopenharmony_ci struct call_path *cp; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci if (sym && is_x86_retpoline(sym->name)) { 10618c2ecf20Sopenharmony_ci /* 10628c2ecf20Sopenharmony_ci * This is a x86 retpoline fn. It pollutes the call graph by 10638c2ecf20Sopenharmony_ci * showing up everywhere there is an indirect branch, but does 10648c2ecf20Sopenharmony_ci * not itself mean anything. Here the top-of-stack is removed, 10658c2ecf20Sopenharmony_ci * by decrementing the stack count, and then further down, the 10668c2ecf20Sopenharmony_ci * resulting top-of-stack is replaced with the actual target. 10678c2ecf20Sopenharmony_ci * The result is that the retpoline functions will no longer 10688c2ecf20Sopenharmony_ci * appear in the call graph. Note this only affects the call 10698c2ecf20Sopenharmony_ci * graph, since all the original branches are left unchanged. 10708c2ecf20Sopenharmony_ci */ 10718c2ecf20Sopenharmony_ci ts->cnt -= 1; 10728c2ecf20Sopenharmony_ci sym = ts->stack[ts->cnt - 2].cp->sym; 10738c2ecf20Sopenharmony_ci if (sym && sym == tsym && to_al->addr != tsym->start) { 10748c2ecf20Sopenharmony_ci /* 10758c2ecf20Sopenharmony_ci * Target is back to the middle of the symbol we came 10768c2ecf20Sopenharmony_ci * from so assume it is an indirect jmp and forget it 10778c2ecf20Sopenharmony_ci * altogether. 10788c2ecf20Sopenharmony_ci */ 10798c2ecf20Sopenharmony_ci ts->cnt -= 1; 10808c2ecf20Sopenharmony_ci return 0; 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci } else if (sym && sym == tsym) { 10838c2ecf20Sopenharmony_ci /* 10848c2ecf20Sopenharmony_ci * Target is back to the symbol we came from so assume it is an 10858c2ecf20Sopenharmony_ci * indirect jmp and forget it altogether. 10868c2ecf20Sopenharmony_ci */ 10878c2ecf20Sopenharmony_ci ts->cnt -= 1; 10888c2ecf20Sopenharmony_ci return 0; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci cp = call_path__findnew(cpr, ts->stack[ts->cnt - 2].cp, tsym, 10928c2ecf20Sopenharmony_ci sample->addr, ts->kernel_start); 10938c2ecf20Sopenharmony_ci if (!cp) 10948c2ecf20Sopenharmony_ci return -ENOMEM; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci /* Replace the top-of-stack with the actual target */ 10978c2ecf20Sopenharmony_ci ts->stack[ts->cnt - 1].cp = cp; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci return 0; 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ciint thread_stack__process(struct thread *thread, struct comm *comm, 11038c2ecf20Sopenharmony_ci struct perf_sample *sample, 11048c2ecf20Sopenharmony_ci struct addr_location *from_al, 11058c2ecf20Sopenharmony_ci struct addr_location *to_al, u64 ref, 11068c2ecf20Sopenharmony_ci struct call_return_processor *crp) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci struct thread_stack *ts = thread__stack(thread, sample->cpu); 11098c2ecf20Sopenharmony_ci enum retpoline_state_t rstate; 11108c2ecf20Sopenharmony_ci int err = 0; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if (ts && !ts->crp) { 11138c2ecf20Sopenharmony_ci /* Supersede thread_stack__event() */ 11148c2ecf20Sopenharmony_ci thread_stack__reset(thread, ts); 11158c2ecf20Sopenharmony_ci ts = NULL; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci if (!ts) { 11198c2ecf20Sopenharmony_ci ts = thread_stack__new(thread, sample->cpu, crp, true, 0); 11208c2ecf20Sopenharmony_ci if (!ts) 11218c2ecf20Sopenharmony_ci return -ENOMEM; 11228c2ecf20Sopenharmony_ci ts->comm = comm; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci rstate = ts->rstate; 11268c2ecf20Sopenharmony_ci if (rstate == X86_RETPOLINE_DETECTED) 11278c2ecf20Sopenharmony_ci ts->rstate = X86_RETPOLINE_POSSIBLE; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci /* Flush stack on exec */ 11308c2ecf20Sopenharmony_ci if (ts->comm != comm && thread->pid_ == thread->tid) { 11318c2ecf20Sopenharmony_ci err = __thread_stack__flush(thread, ts); 11328c2ecf20Sopenharmony_ci if (err) 11338c2ecf20Sopenharmony_ci return err; 11348c2ecf20Sopenharmony_ci ts->comm = comm; 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci /* If the stack is empty, put the current symbol on the stack */ 11388c2ecf20Sopenharmony_ci if (!ts->cnt) { 11398c2ecf20Sopenharmony_ci err = thread_stack__bottom(ts, sample, from_al, to_al, ref); 11408c2ecf20Sopenharmony_ci if (err) 11418c2ecf20Sopenharmony_ci return err; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci ts->branch_count += 1; 11458c2ecf20Sopenharmony_ci ts->insn_count += sample->insn_cnt; 11468c2ecf20Sopenharmony_ci ts->cyc_count += sample->cyc_cnt; 11478c2ecf20Sopenharmony_ci ts->last_time = sample->time; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (sample->flags & PERF_IP_FLAG_CALL) { 11508c2ecf20Sopenharmony_ci bool trace_end = sample->flags & PERF_IP_FLAG_TRACE_END; 11518c2ecf20Sopenharmony_ci struct call_path_root *cpr = ts->crp->cpr; 11528c2ecf20Sopenharmony_ci struct call_path *cp; 11538c2ecf20Sopenharmony_ci u64 ret_addr; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci if (!sample->ip || !sample->addr) 11568c2ecf20Sopenharmony_ci return 0; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci ret_addr = sample->ip + sample->insn_len; 11598c2ecf20Sopenharmony_ci if (ret_addr == sample->addr) 11608c2ecf20Sopenharmony_ci return 0; /* Zero-length calls are excluded */ 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, 11638c2ecf20Sopenharmony_ci to_al->sym, sample->addr, 11648c2ecf20Sopenharmony_ci ts->kernel_start); 11658c2ecf20Sopenharmony_ci err = thread_stack__push_cp(ts, ret_addr, sample->time, ref, 11668c2ecf20Sopenharmony_ci cp, false, trace_end); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci /* 11698c2ecf20Sopenharmony_ci * A call to the same symbol but not the start of the symbol, 11708c2ecf20Sopenharmony_ci * may be the start of a x86 retpoline. 11718c2ecf20Sopenharmony_ci */ 11728c2ecf20Sopenharmony_ci if (!err && rstate == X86_RETPOLINE_POSSIBLE && to_al->sym && 11738c2ecf20Sopenharmony_ci from_al->sym == to_al->sym && 11748c2ecf20Sopenharmony_ci to_al->addr != to_al->sym->start) 11758c2ecf20Sopenharmony_ci ts->rstate = X86_RETPOLINE_DETECTED; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci } else if (sample->flags & PERF_IP_FLAG_RETURN) { 11788c2ecf20Sopenharmony_ci if (!sample->addr) { 11798c2ecf20Sopenharmony_ci u32 return_from_kernel = PERF_IP_FLAG_SYSCALLRET | 11808c2ecf20Sopenharmony_ci PERF_IP_FLAG_INTERRUPT; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci if (!(sample->flags & return_from_kernel)) 11838c2ecf20Sopenharmony_ci return 0; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci /* Pop kernel stack */ 11868c2ecf20Sopenharmony_ci return thread_stack__pop_ks(thread, ts, sample, ref); 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci if (!sample->ip) 11908c2ecf20Sopenharmony_ci return 0; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci /* x86 retpoline 'return' doesn't match the stack */ 11938c2ecf20Sopenharmony_ci if (rstate == X86_RETPOLINE_DETECTED && ts->cnt > 2 && 11948c2ecf20Sopenharmony_ci ts->stack[ts->cnt - 1].ret_addr != sample->addr) 11958c2ecf20Sopenharmony_ci return thread_stack__x86_retpoline(ts, sample, to_al); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci err = thread_stack__pop_cp(thread, ts, sample->addr, 11988c2ecf20Sopenharmony_ci sample->time, ref, from_al->sym); 11998c2ecf20Sopenharmony_ci if (err) { 12008c2ecf20Sopenharmony_ci if (err < 0) 12018c2ecf20Sopenharmony_ci return err; 12028c2ecf20Sopenharmony_ci err = thread_stack__no_call_return(thread, ts, sample, 12038c2ecf20Sopenharmony_ci from_al, to_al, ref); 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci } else if (sample->flags & PERF_IP_FLAG_TRACE_BEGIN) { 12068c2ecf20Sopenharmony_ci err = thread_stack__trace_begin(thread, ts, sample->time, ref); 12078c2ecf20Sopenharmony_ci } else if (sample->flags & PERF_IP_FLAG_TRACE_END) { 12088c2ecf20Sopenharmony_ci err = thread_stack__trace_end(ts, sample, ref); 12098c2ecf20Sopenharmony_ci } else if (sample->flags & PERF_IP_FLAG_BRANCH && 12108c2ecf20Sopenharmony_ci from_al->sym != to_al->sym && to_al->sym && 12118c2ecf20Sopenharmony_ci to_al->addr == to_al->sym->start) { 12128c2ecf20Sopenharmony_ci struct call_path_root *cpr = ts->crp->cpr; 12138c2ecf20Sopenharmony_ci struct call_path *cp; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci /* 12168c2ecf20Sopenharmony_ci * The compiler might optimize a call/ret combination by making 12178c2ecf20Sopenharmony_ci * it a jmp. Make that visible by recording on the stack a 12188c2ecf20Sopenharmony_ci * branch to the start of a different symbol. Note, that means 12198c2ecf20Sopenharmony_ci * when a ret pops the stack, all jmps must be popped off first. 12208c2ecf20Sopenharmony_ci */ 12218c2ecf20Sopenharmony_ci cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, 12228c2ecf20Sopenharmony_ci to_al->sym, sample->addr, 12238c2ecf20Sopenharmony_ci ts->kernel_start); 12248c2ecf20Sopenharmony_ci err = thread_stack__push_cp(ts, 0, sample->time, ref, cp, false, 12258c2ecf20Sopenharmony_ci false); 12268c2ecf20Sopenharmony_ci if (!err) 12278c2ecf20Sopenharmony_ci ts->stack[ts->cnt - 1].non_call = true; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci return err; 12318c2ecf20Sopenharmony_ci} 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_cisize_t thread_stack__depth(struct thread *thread, int cpu) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci struct thread_stack *ts = thread__stack(thread, cpu); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci if (!ts) 12388c2ecf20Sopenharmony_ci return 0; 12398c2ecf20Sopenharmony_ci return ts->cnt; 12408c2ecf20Sopenharmony_ci} 1241