diff options
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 | pr_err("Error flushing thread stack!\n"); | ||
| 237 | ts->cnt = 0; | ||
| 238 | return err; | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | return 0; | ||
| 243 | } | ||
| 244 | |||
| 107 | int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, | 245 | int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, |
| 108 | u64 to_ip, u16 insn_len, u64 trace_nr) | 246 | u64 to_ip, u16 insn_len, u64 trace_nr) |
