aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2014-10-30 10:09:45 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2014-11-03 15:43:56 -0500
commit92a9e4f7db89a013e1bdef2e548928fc71e9867c (patch)
treea177ee83d628da5379ffbafbf32faabe5c5cbf94 /tools
parent00447ccdf3335ea467841fc3c7d65ffd30748895 (diff)
perf tools: Enhance the thread stack to output call/return data
Enhance the thread stack to output detailed information about paired calls and returns. The enhanced processing consumes sample information via thread_stack__process() and outputs information about paired calls / returns via a call-back. While the call-back makes it possible for the facility to be used by arbitrary tools, a subsequent patch will provide the information to Python scripting via the db-export interface. An important part of the call/return information is the call path which provides a structure that defines a context sensitive call graph. Note that there are now two ways to use the thread stack. For simply providing a call stack (like you would get from the perf record -g option) the interface consists of thread_stack__event() and thread_stack__sample(). Whereas the enhanced interface consists of call_return_processor__new() and thread_stack__process(). 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-5-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/util/thread-stack.c585
-rw-r--r--tools/perf/util/thread-stack.h79
2 files changed, 659 insertions, 5 deletions
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index 85b60d2e738f..9ed59a452d1f 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -13,23 +13,96 @@
13 * 13 *
14 */ 14 */
15 15
16#include <linux/rbtree.h>
17#include <linux/list.h>
16#include "thread.h" 18#include "thread.h"
17#include "event.h" 19#include "event.h"
20#include "machine.h"
18#include "util.h" 21#include "util.h"
19#include "debug.h" 22#include "debug.h"
23#include "symbol.h"
24#include "comm.h"
20#include "thread-stack.h" 25#include "thread-stack.h"
21 26
22#define STACK_GROWTH 4096 27#define CALL_PATH_BLOCK_SHIFT 8
28#define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT)
29#define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1)
23 30
31struct call_path_block {
32 struct call_path cp[CALL_PATH_BLOCK_SIZE];
33 struct list_head node;
34};
35
36/**
37 * struct call_path_root - root of all call paths.
38 * @call_path: root call path
39 * @blocks: list of blocks to store call paths
40 * @next: next free space
41 * @sz: number of spaces
42 */
43struct call_path_root {
44 struct call_path call_path;
45 struct list_head blocks;
46 size_t next;
47 size_t sz;
48};
49
50/**
51 * struct call_return_processor - provides a call-back to consume call-return
52 * information.
53 * @cpr: call path root
54 * @process: call-back that accepts call/return information
55 * @data: anonymous data for call-back
56 */
57struct call_return_processor {
58 struct call_path_root *cpr;
59 int (*process)(struct call_return *cr, void *data);
60 void *data;
61};
62
63#define STACK_GROWTH 2048
64
65/**
66 * struct thread_stack_entry - thread stack entry.
67 * @ret_addr: return address
68 * @timestamp: timestamp (if known)
69 * @ref: external reference (e.g. db_id of sample)
70 * @branch_count: the branch count when the entry was created
71 * @cp: call path
72 * @no_call: a 'call' was not seen
73 */
24struct thread_stack_entry { 74struct thread_stack_entry {
25 u64 ret_addr; 75 u64 ret_addr;
76 u64 timestamp;
77 u64 ref;
78 u64 branch_count;
79 struct call_path *cp;
80 bool no_call;
26}; 81};
27 82
83/**
84 * struct thread_stack - thread stack constructed from 'call' and 'return'
85 * branch samples.
86 * @stack: array that holds the stack
87 * @cnt: number of entries in the stack
88 * @sz: current maximum stack size
89 * @trace_nr: current trace number
90 * @branch_count: running branch count
91 * @kernel_start: kernel start address
92 * @last_time: last timestamp
93 * @crp: call/return processor
94 * @comm: current comm
95 */
28struct thread_stack { 96struct thread_stack {
29 struct thread_stack_entry *stack; 97 struct thread_stack_entry *stack;
30 size_t cnt; 98 size_t cnt;
31 size_t sz; 99 size_t sz;
32 u64 trace_nr; 100 u64 trace_nr;
101 u64 branch_count;
102 u64 kernel_start;
103 u64 last_time;
104 struct call_return_processor *crp;
105 struct comm *comm;
33}; 106};
34 107
35static int thread_stack__grow(struct thread_stack *ts) 108static int thread_stack__grow(struct thread_stack *ts)
@@ -50,7 +123,8 @@ static int thread_stack__grow(struct thread_stack *ts)
50 return 0; 123 return 0;
51} 124}
52 125
53static struct thread_stack *thread_stack__new(void) 126static struct thread_stack *thread_stack__new(struct thread *thread,
127 struct call_return_processor *crp)
54{ 128{
55 struct thread_stack *ts; 129 struct thread_stack *ts;
56 130
@@ -63,6 +137,12 @@ static struct thread_stack *thread_stack__new(void)
63 return NULL; 137 return NULL;
64 } 138 }
65 139
140 if (thread->mg && thread->mg->machine)
141 ts->kernel_start = machine__kernel_start(thread->mg->machine);
142 else
143 ts->kernel_start = 1ULL << 63;
144 ts->crp = crp;
145
66 return ts; 146 return ts;
67} 147}
68 148
@@ -104,6 +184,64 @@ static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr)
104 } 184 }
105} 185}
106 186
187static bool thread_stack__in_kernel(struct thread_stack *ts)
188{
189 if (!ts->cnt)
190 return false;
191
192 return ts->stack[ts->cnt - 1].cp->in_kernel;
193}
194
195static int thread_stack__call_return(struct thread *thread,
196 struct thread_stack *ts, size_t idx,
197 u64 timestamp, u64 ref, bool no_return)
198{
199 struct call_return_processor *crp = ts->crp;
200 struct thread_stack_entry *tse;
201 struct call_return cr = {
202 .thread = thread,
203 .comm = ts->comm,
204 .db_id = 0,
205 };
206
207 tse = &ts->stack[idx];
208 cr.cp = tse->cp;
209 cr.call_time = tse->timestamp;
210 cr.return_time = timestamp;
211 cr.branch_count = ts->branch_count - tse->branch_count;
212 cr.call_ref = tse->ref;
213 cr.return_ref = ref;
214 if (tse->no_call)
215 cr.flags |= CALL_RETURN_NO_CALL;
216 if (no_return)
217 cr.flags |= CALL_RETURN_NO_RETURN;
218
219 return crp->process(&cr, crp->data);
220}
221
222static int thread_stack__flush(struct thread *thread, struct thread_stack *ts)
223{
224 struct call_return_processor *crp = ts->crp;
225 int err;
226
227 if (!crp) {
228 ts->cnt = 0;
229 return 0;
230 }
231
232 while (ts->cnt) {
233 err = thread_stack__call_return(thread, ts, --ts->cnt,
234 ts->last_time, 0, true);
235 if (err) {
236