// SPDX-License-Identifier: GPL-2.0-only
/*
* thread-stack.c: Synthesize a thread's stack using call / return events
* Copyright (c) 2014, Intel Corporation.
*/
#include <linux/rbtree.h>
#include <linux/list.h>
#include <linux/log2.h>
#include <linux/zalloc.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "thread.h"
#include "event.h"
#include "machine.h"
#include "env.h"
#include "debug.h"
#include "symbol.h"
#include "comm.h"
#include "call-path.h"
#include "thread-stack.h"
#define STACK_GROWTH 2048
/*
* State of retpoline detection.
*
* RETPOLINE_NONE: no retpoline detection
* X86_RETPOLINE_POSSIBLE: x86 retpoline possible
* X86_RETPOLINE_DETECTED: x86 retpoline detected
*/
enum retpoline_state_t {
RETPOLINE_NONE,
X86_RETPOLINE_POSSIBLE,
X86_RETPOLINE_DETECTED,
};
/**
* struct thread_stack_entry - thread stack entry.
* @ret_addr: return address
* @timestamp: timestamp (if known)
* @ref: external reference (e.g. db_id of sample)
* @branch_count: the branch count when the entry was created
* @insn_count: the instruction count when the entry was created
* @cyc_count the cycle count when the entry was created
* @db_id: id used for db-export
* @cp: call path
* @no_call: a 'call' was not seen
* @trace_end: a 'call' but trace ended
* @non_call: a branch but not a 'call' to the start of a different symbol
*/
struct thread_stack_entry {
u64 ret_addr;
u64 timestamp;
u64 ref;
u64 branch_count;
u64 insn_count;
u64 cyc_count;
u64 db_id;
struct call_path *cp;
bool no_call;
bool trace_end;
bool non_call;
};
/**
* struct thread_stack - thread stack constructed from 'call' and 'return'
* branch samples.
* @stack: array that holds the stack
* @cnt: number of entries in the stack
* @sz: current maximum stack size
* @trace_nr: current trace number
* @branch_count: running branch count
* @insn_count: running instruction count
* @cyc_count running cycle count
* @kernel_start: kernel start address
* @last_time: last timestamp
* @crp: call/return processor
* @comm: current comm
* @arr_sz: size of array if this is the first element of an array
* @rstate: used to detect retpolines
* @br_stack_rb: branch stack (ring buffer)
* @br_stack_sz: maximum branch stack size
* @br_stack_pos: current position in @br_stack_rb
* @mispred_all: mark all branches as mispredicted
*/
struct thread_stack {
struct thread_stack_entry *stack;
size_t cnt;
size_t sz;
u64 trace_nr;
u64 branch_count;
u64 insn_count;
u64 cyc_count;
u64 kernel_start;
u64 last_time;
struct call_return_processor *crp;
struct comm *comm;
unsigned int arr_sz;
enum retpoline_state_t rstate;
struct branch_stack *br_stack_rb;
unsigned int br_stack_sz;
unsigned int br_stack_pos;
bool mispred_all;
};
/*
* Assume pid == tid == 0 identifies the idle task as defined by
* perf_session__register_idle_thread(). The idle task is really 1 task per cpu,
* and therefore requires a stack for each cpu.
*/
static inline bool thread_stack__per_cpu(struct thread *thread)
{
return !(thread__tid(thread) || thread__pid(thread));
}
static int thread_stack__grow(struct thread_stack *ts)
{
struct thread_stack_entry *new_stack;
size_t sz, new_sz;
new_sz = ts->sz + STACK_GROWTH;
sz = new_sz * sizeof(struct thread_stack_entry);
new_stack = realloc(ts->stack, sz);
if (!new_stack)
return -ENOMEM;
ts->stack = new_stack;
ts->sz = new_sz;
return 0;
}
static int thread_stack__init(struct thread_stack *ts, struct thread *thread,
struct call_return_processor *crp,
bool callstack, unsigned int br_stack_sz)
{
int err;
if (callstack) {
err = thread_stack__grow(ts);
if (err)
return err;
}
if (br_stack_sz) {
size_t sz = sizeof(struct branch_stack);
sz += br_stack_sz * sizeof(struct branch_entry);
ts->br_stack_rb = zalloc(sz);
if (!ts->br_stack_rb)
return -ENOMEM;
ts->br_stack_sz = br_stack_sz;
}
if (thread__maps(t