xref: /openbmc/linux/tools/perf/util/thread-stack.c (revision 00447ccd)
100447ccdSAdrian Hunter /*
200447ccdSAdrian Hunter  * thread-stack.c: Synthesize a thread's stack using call / return events
300447ccdSAdrian Hunter  * Copyright (c) 2014, Intel Corporation.
400447ccdSAdrian Hunter  *
500447ccdSAdrian Hunter  * This program is free software; you can redistribute it and/or modify it
600447ccdSAdrian Hunter  * under the terms and conditions of the GNU General Public License,
700447ccdSAdrian Hunter  * version 2, as published by the Free Software Foundation.
800447ccdSAdrian Hunter  *
900447ccdSAdrian Hunter  * This program is distributed in the hope it will be useful, but WITHOUT
1000447ccdSAdrian Hunter  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1100447ccdSAdrian Hunter  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1200447ccdSAdrian Hunter  * more details.
1300447ccdSAdrian Hunter  *
1400447ccdSAdrian Hunter  */
1500447ccdSAdrian Hunter 
1600447ccdSAdrian Hunter #include "thread.h"
1700447ccdSAdrian Hunter #include "event.h"
1800447ccdSAdrian Hunter #include "util.h"
1900447ccdSAdrian Hunter #include "debug.h"
2000447ccdSAdrian Hunter #include "thread-stack.h"
2100447ccdSAdrian Hunter 
2200447ccdSAdrian Hunter #define STACK_GROWTH 4096
2300447ccdSAdrian Hunter 
2400447ccdSAdrian Hunter struct thread_stack_entry {
2500447ccdSAdrian Hunter 	u64 ret_addr;
2600447ccdSAdrian Hunter };
2700447ccdSAdrian Hunter 
2800447ccdSAdrian Hunter struct thread_stack {
2900447ccdSAdrian Hunter 	struct thread_stack_entry *stack;
3000447ccdSAdrian Hunter 	size_t cnt;
3100447ccdSAdrian Hunter 	size_t sz;
3200447ccdSAdrian Hunter 	u64 trace_nr;
3300447ccdSAdrian Hunter };
3400447ccdSAdrian Hunter 
3500447ccdSAdrian Hunter static int thread_stack__grow(struct thread_stack *ts)
3600447ccdSAdrian Hunter {
3700447ccdSAdrian Hunter 	struct thread_stack_entry *new_stack;
3800447ccdSAdrian Hunter 	size_t sz, new_sz;
3900447ccdSAdrian Hunter 
4000447ccdSAdrian Hunter 	new_sz = ts->sz + STACK_GROWTH;
4100447ccdSAdrian Hunter 	sz = new_sz * sizeof(struct thread_stack_entry);
4200447ccdSAdrian Hunter 
4300447ccdSAdrian Hunter 	new_stack = realloc(ts->stack, sz);
4400447ccdSAdrian Hunter 	if (!new_stack)
4500447ccdSAdrian Hunter 		return -ENOMEM;
4600447ccdSAdrian Hunter 
4700447ccdSAdrian Hunter 	ts->stack = new_stack;
4800447ccdSAdrian Hunter 	ts->sz = new_sz;
4900447ccdSAdrian Hunter 
5000447ccdSAdrian Hunter 	return 0;
5100447ccdSAdrian Hunter }
5200447ccdSAdrian Hunter 
5300447ccdSAdrian Hunter static struct thread_stack *thread_stack__new(void)
5400447ccdSAdrian Hunter {
5500447ccdSAdrian Hunter 	struct thread_stack *ts;
5600447ccdSAdrian Hunter 
5700447ccdSAdrian Hunter 	ts = zalloc(sizeof(struct thread_stack));
5800447ccdSAdrian Hunter 	if (!ts)
5900447ccdSAdrian Hunter 		return NULL;
6000447ccdSAdrian Hunter 
6100447ccdSAdrian Hunter 	if (thread_stack__grow(ts)) {
6200447ccdSAdrian Hunter 		free(ts);
6300447ccdSAdrian Hunter 		return NULL;
6400447ccdSAdrian Hunter 	}
6500447ccdSAdrian Hunter 
6600447ccdSAdrian Hunter 	return ts;
6700447ccdSAdrian Hunter }
6800447ccdSAdrian Hunter 
6900447ccdSAdrian Hunter static int thread_stack__push(struct thread_stack *ts, u64 ret_addr)
7000447ccdSAdrian Hunter {
7100447ccdSAdrian Hunter 	int err = 0;
7200447ccdSAdrian Hunter 
7300447ccdSAdrian Hunter 	if (ts->cnt == ts->sz) {
7400447ccdSAdrian Hunter 		err = thread_stack__grow(ts);
7500447ccdSAdrian Hunter 		if (err) {
7600447ccdSAdrian Hunter 			pr_warning("Out of memory: discarding thread stack\n");
7700447ccdSAdrian Hunter 			ts->cnt = 0;
7800447ccdSAdrian Hunter 		}
7900447ccdSAdrian Hunter 	}
8000447ccdSAdrian Hunter 
8100447ccdSAdrian Hunter 	ts->stack[ts->cnt++].ret_addr = ret_addr;
8200447ccdSAdrian Hunter 
8300447ccdSAdrian Hunter 	return err;
8400447ccdSAdrian Hunter }
8500447ccdSAdrian Hunter 
8600447ccdSAdrian Hunter static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr)
8700447ccdSAdrian Hunter {
8800447ccdSAdrian Hunter 	size_t i;
8900447ccdSAdrian Hunter 
9000447ccdSAdrian Hunter 	/*
9100447ccdSAdrian Hunter 	 * In some cases there may be functions which are not seen to return.
9200447ccdSAdrian Hunter 	 * For example when setjmp / longjmp has been used.  Or the perf context
9300447ccdSAdrian Hunter 	 * switch in the kernel which doesn't stop and start tracing in exactly
9400447ccdSAdrian Hunter 	 * the same code path.  When that happens the return address will be
9500447ccdSAdrian Hunter 	 * further down the stack.  If the return address is not found at all,
9600447ccdSAdrian Hunter 	 * we assume the opposite (i.e. this is a return for a call that wasn't
9700447ccdSAdrian Hunter 	 * seen for some reason) and leave the stack alone.
9800447ccdSAdrian Hunter 	 */
9900447ccdSAdrian Hunter 	for (i = ts->cnt; i; ) {
10000447ccdSAdrian Hunter 		if (ts->stack[--i].ret_addr == ret_addr) {
10100447ccdSAdrian Hunter 			ts->cnt = i;
10200447ccdSAdrian Hunter 			return;
10300447ccdSAdrian Hunter 		}
10400447ccdSAdrian Hunter 	}
10500447ccdSAdrian Hunter }
10600447ccdSAdrian Hunter 
10700447ccdSAdrian Hunter int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
10800447ccdSAdrian Hunter 			u64 to_ip, u16 insn_len, u64 trace_nr)
10900447ccdSAdrian Hunter {
11000447ccdSAdrian Hunter 	if (!thread)
11100447ccdSAdrian Hunter 		return -EINVAL;
11200447ccdSAdrian Hunter 
11300447ccdSAdrian Hunter 	if (!thread->ts) {
11400447ccdSAdrian Hunter 		thread->ts = thread_stack__new();
11500447ccdSAdrian Hunter 		if (!thread->ts) {
11600447ccdSAdrian Hunter 			pr_warning("Out of memory: no thread stack\n");
11700447ccdSAdrian Hunter 			return -ENOMEM;
11800447ccdSAdrian Hunter 		}
11900447ccdSAdrian Hunter 		thread->ts->trace_nr = trace_nr;
12000447ccdSAdrian Hunter 	}
12100447ccdSAdrian Hunter 
12200447ccdSAdrian Hunter 	/*
12300447ccdSAdrian Hunter 	 * When the trace is discontinuous, the trace_nr changes.  In that case
12400447ccdSAdrian Hunter 	 * the stack might be completely invalid.  Better to report nothing than
12500447ccdSAdrian Hunter 	 * to report something misleading, so reset the stack count to zero.
12600447ccdSAdrian Hunter 	 */
12700447ccdSAdrian Hunter 	if (trace_nr != thread->ts->trace_nr) {
12800447ccdSAdrian Hunter 		thread->ts->trace_nr = trace_nr;
12900447ccdSAdrian Hunter 		thread->ts->cnt = 0;
13000447ccdSAdrian Hunter 	}
13100447ccdSAdrian Hunter 
13200447ccdSAdrian Hunter 	if (flags & PERF_IP_FLAG_CALL) {
13300447ccdSAdrian Hunter 		u64 ret_addr;
13400447ccdSAdrian Hunter 
13500447ccdSAdrian Hunter 		if (!to_ip)
13600447ccdSAdrian Hunter 			return 0;
13700447ccdSAdrian Hunter 		ret_addr = from_ip + insn_len;
13800447ccdSAdrian Hunter 		if (ret_addr == to_ip)
13900447ccdSAdrian Hunter 			return 0; /* Zero-length calls are excluded */
14000447ccdSAdrian Hunter 		return thread_stack__push(thread->ts, ret_addr);
14100447ccdSAdrian Hunter 	} else if (flags & PERF_IP_FLAG_RETURN) {
14200447ccdSAdrian Hunter 		if (!from_ip)
14300447ccdSAdrian Hunter 			return 0;
14400447ccdSAdrian Hunter 		thread_stack__pop(thread->ts, to_ip);
14500447ccdSAdrian Hunter 	}
14600447ccdSAdrian Hunter 
14700447ccdSAdrian Hunter 	return 0;
14800447ccdSAdrian Hunter }
14900447ccdSAdrian Hunter 
15000447ccdSAdrian Hunter void thread_stack__free(struct thread *thread)
15100447ccdSAdrian Hunter {
15200447ccdSAdrian Hunter 	if (thread->ts) {
15300447ccdSAdrian Hunter 		zfree(&thread->ts->stack);
15400447ccdSAdrian Hunter 		zfree(&thread->ts);
15500447ccdSAdrian Hunter 	}
15600447ccdSAdrian Hunter }
15700447ccdSAdrian Hunter 
15800447ccdSAdrian Hunter void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
15900447ccdSAdrian Hunter 			  size_t sz, u64 ip)
16000447ccdSAdrian Hunter {
16100447ccdSAdrian Hunter 	size_t i;
16200447ccdSAdrian Hunter 
16300447ccdSAdrian Hunter 	if (!thread || !thread->ts)
16400447ccdSAdrian Hunter 		chain->nr = 1;
16500447ccdSAdrian Hunter 	else
16600447ccdSAdrian Hunter 		chain->nr = min(sz, thread->ts->cnt + 1);
16700447ccdSAdrian Hunter 
16800447ccdSAdrian Hunter 	chain->ips[0] = ip;
16900447ccdSAdrian Hunter 
17000447ccdSAdrian Hunter 	for (i = 1; i < chain->nr; i++)
17100447ccdSAdrian Hunter 		chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr;
17200447ccdSAdrian Hunter }
173