-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perf tools: Add a thread stack for synthesizing call chains
Add a thread stack for synthesizing call chains from call and return events. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Acked-by: Jiri Olsa <jolsa@kernel.org> Cc: David Ahern <dsahern@gmail.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Namhyung Kim <namhyung@gmail.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Link: http://lkml.kernel.org/r/1414678188-14946-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
- Loading branch information
Adrian Hunter
authored and
Arnaldo Carvalho de Melo
committed
Nov 3, 2014
1 parent
05b2537
commit 00447cc
Showing
6 changed files
with
238 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
/* | ||
* thread-stack.c: Synthesize a thread's stack using call / return events | ||
* Copyright (c) 2014, Intel Corporation. | ||
* | ||
* This program is free software; you can redistribute it and/or modify it | ||
* under the terms and conditions of the GNU General Public License, | ||
* version 2, as published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
* more details. | ||
* | ||
*/ | ||
|
||
#include "thread.h" | ||
#include "event.h" | ||
#include "util.h" | ||
#include "debug.h" | ||
#include "thread-stack.h" | ||
|
||
#define STACK_GROWTH 4096 | ||
|
||
struct thread_stack_entry { | ||
u64 ret_addr; | ||
}; | ||
|
||
struct thread_stack { | ||
struct thread_stack_entry *stack; | ||
size_t cnt; | ||
size_t sz; | ||
u64 trace_nr; | ||
}; | ||
|
||
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 struct thread_stack *thread_stack__new(void) | ||
{ | ||
struct thread_stack *ts; | ||
|
||
ts = zalloc(sizeof(struct thread_stack)); | ||
if (!ts) | ||
return NULL; | ||
|
||
if (thread_stack__grow(ts)) { | ||
free(ts); | ||
return NULL; | ||
} | ||
|
||
return ts; | ||
} | ||
|
||
static int thread_stack__push(struct thread_stack *ts, u64 ret_addr) | ||
{ | ||
int err = 0; | ||
|
||
if (ts->cnt == ts->sz) { | ||
err = thread_stack__grow(ts); | ||
if (err) { | ||
pr_warning("Out of memory: discarding thread stack\n"); | ||
ts->cnt = 0; | ||
} | ||
} | ||
|
||
ts->stack[ts->cnt++].ret_addr = ret_addr; | ||
|
||
return err; | ||
} | ||
|
||
static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr) | ||
{ | ||
size_t i; | ||
|
||
/* | ||
* In some cases there may be functions which are not seen to return. | ||
* For example when setjmp / longjmp has been used. Or the perf context | ||
* switch in the kernel which doesn't stop and start tracing in exactly | ||
* the same code path. When that happens the return address will be | ||
* further down the stack. If the return address is not found at all, | ||
* we assume the opposite (i.e. this is a return for a call that wasn't | ||
* seen for some reason) and leave the stack alone. | ||
*/ | ||
for (i = ts->cnt; i; ) { | ||
if (ts->stack[--i].ret_addr == ret_addr) { | ||
ts->cnt = i; | ||
return; | ||
} | ||
} | ||
} | ||
|
||
int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, | ||
u64 to_ip, u16 insn_len, u64 trace_nr) | ||
{ | ||
if (!thread) | ||
return -EINVAL; | ||
|
||
if (!thread->ts) { | ||
thread->ts = thread_stack__new(); | ||
if (!thread->ts) { | ||
pr_warning("Out of memory: no thread stack\n"); | ||
return -ENOMEM; | ||
} | ||
thread->ts->trace_nr = trace_nr; | ||
} | ||
|
||
/* | ||
* When the trace is discontinuous, the trace_nr changes. In that case | ||
* the stack might be completely invalid. Better to report nothing than | ||
* to report something misleading, so reset the stack count to zero. | ||
*/ | ||
if (trace_nr != thread->ts->trace_nr) { | ||
thread->ts->trace_nr = trace_nr; | ||
thread->ts->cnt = 0; | ||
} | ||
|
||
if (flags & PERF_IP_FLAG_CALL) { | ||
u64 ret_addr; | ||
|
||
if (!to_ip) | ||
return 0; | ||
ret_addr = from_ip + insn_len; | ||
if (ret_addr == to_ip) | ||
return 0; /* Zero-length calls are excluded */ | ||
return thread_stack__push(thread->ts, ret_addr); | ||
} else if (flags & PERF_IP_FLAG_RETURN) { | ||
if (!from_ip) | ||
return 0; | ||
thread_stack__pop(thread->ts, to_ip); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
void thread_stack__free(struct thread *thread) | ||
{ | ||
if (thread->ts) { | ||
zfree(&thread->ts->stack); | ||
zfree(&thread->ts); | ||
} | ||
} | ||
|
||
void thread_stack__sample(struct thread *thread, struct ip_callchain *chain, | ||
size_t sz, u64 ip) | ||
{ | ||
size_t i; | ||
|
||
if (!thread || !thread->ts) | ||
chain->nr = 1; | ||
else | ||
chain->nr = min(sz, thread->ts->cnt + 1); | ||
|
||
chain->ips[0] = ip; | ||
|
||
for (i = 1; i < chain->nr; i++) | ||
chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
* thread-stack.h: Synthesize a thread's stack using call / return events | ||
* Copyright (c) 2014, Intel Corporation. | ||
* | ||
* This program is free software; you can redistribute it and/or modify it | ||
* under the terms and conditions of the GNU General Public License, | ||
* version 2, as published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
* more details. | ||
* | ||
*/ | ||
|
||
#ifndef __PERF_THREAD_STACK_H | ||
#define __PERF_THREAD_STACK_H | ||
|
||
#include <sys/types.h> | ||
|
||
#include <linux/types.h> | ||
|
||
struct thread; | ||
struct ip_callchain; | ||
|
||
int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, | ||
u64 to_ip, u16 insn_len, u64 trace_nr); | ||
void thread_stack__sample(struct thread *thread, struct ip_callchain *chain, | ||
size_t sz, u64 ip); | ||
void thread_stack__free(struct thread *thread); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters