diff options
| author | Adrian Hunter <adrian.hunter@intel.com> | 2014-10-30 10:09:45 -0400 |
|---|---|---|
| committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2014-11-03 15:43:56 -0500 |
| commit | 92a9e4f7db89a013e1bdef2e548928fc71e9867c (patch) | |
| tree | a177ee83d628da5379ffbafbf32faabe5c5cbf94 /tools | |
| parent | 00447ccdf3335ea467841fc3c7d65ffd30748895 (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.c | 585 | ||||
| -rw-r--r-- | tools/perf/util/thread-stack.h | 79 |
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 | ||
| 31 | struct 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 | */ | ||
| 43 | struct 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 | */ | ||
| 57 | struct 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 | */ | ||
| 24 | struct thread_stack_entry { | 74 | struct 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 | */ | ||
| 28 | struct thread_stack { | 96 | struct 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 | ||
| 35 | static int thread_stack__grow(struct thread_stack *ts) | 108 | static 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 | ||
| 53 | static struct thread_stack *thread_stack__new(void) | 126 | static 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 | ||
| 187 | static 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 | |||
| 195 | static 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 | |||
| 222 | static 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 | |||
