Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf tools: Bind callchains to the first sort dimension column

Currently, the callchains are displayed using a constant left
margin. So depending on the current sort dimension
configuration, callchains may appear to be well attached to the
first sort dimension column field which is mostly the case,
except when the first dimension of sorting is done by comm,
because these are right aligned.

This patch binds the callchain to the first letter in the first
column, whatever type of column it is (dso, comm, symbol).
Before:

0.80% perf [k] __lock_acquire
__lock_acquire
lock_acquire
|
|--58.33%-- _spin_lock
| |
| |--28.57%-- inotify_should_send_event
| | fsnotify
| | __fsnotify_parent

After:

0.80% perf [k] __lock_acquire
__lock_acquire
lock_acquire
|
|--58.33%-- _spin_lock
| |
| |--28.57%-- inotify_should_send_event
| | fsnotify
| | __fsnotify_parent

Also, for clarity, we don't put anymore the callchain as is but:

- If we have a top level ancestor in the callchain, start it
with a first ascii hook.

Before:

0.80% perf [kernel] [k] __lock_acquire
__lock_acquire
lock_acquire
|
|--58.33%-- _spin_lock
| |
| |--28.57%-- inotify_should_send_event
| | fsnotify
[..] [..]

After:

0.80% perf [kernel] [k] __lock_acquire
|
--- __lock_acquire
lock_acquire
|
|--58.33%-- _spin_lock
| |
| |--28.57%-- inotify_should_send_event
| | fsnotify
[..] [..]

- Otherwise, if we have several top level ancestors, then
display these like we did before:

1.69% Xorg
|
|--21.21%-- vread_hpet
| 0x7fffd85b46fc
| 0x7fffd85b494d
| 0x7f4fafb4e54d
|
|--15.15%-- exaOffscreenAlloc
|
|--9.09%-- I830WaitLpRing

Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Anton Blanchard <anton@samba.org>
LKML-Reference: <1256246604-17156-2-git-send-email-fweisbec@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

authored by

Frederic Weisbecker and committed by
Ingo Molnar
a4fb581b af0a6fa4

+99 -24
+63 -19
tools/perf/builtin-report.c
··· 59 59 60 60 static u64 sample_type; 61 61 62 - static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask) 62 + 63 + static size_t 64 + callchain__fprintf_left_margin(FILE *fp, int left_margin) 65 + { 66 + int i; 67 + int ret; 68 + 69 + ret = fprintf(fp, " "); 70 + 71 + for (i = 0; i < left_margin; i++) 72 + ret += fprintf(fp, " "); 73 + 74 + return ret; 75 + } 76 + 77 + static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, 78 + int left_margin) 63 79 { 64 80 int i; 65 81 size_t ret = 0; 66 82 67 - ret += fprintf(fp, "%s", " "); 83 + ret += callchain__fprintf_left_margin(fp, left_margin); 68 84 69 85 for (i = 0; i < depth; i++) 70 86 if (depth_mask & (1 << i)) ··· 95 79 static size_t 96 80 ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, 97 81 int depth_mask, int count, u64 total_samples, 98 - int hits) 82 + int hits, int left_margin) 99 83 { 100 84 int i; 101 85 size_t ret = 0; 102 86 103 - ret += fprintf(fp, "%s", " "); 87 + ret += callchain__fprintf_left_margin(fp, left_margin); 104 88 for (i = 0; i < depth; i++) { 105 89 if (depth_mask & (1 << i)) 106 90 ret += fprintf(fp, "|"); ··· 139 123 140 124 static size_t 141 125 __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, 142 - u64 total_samples, int depth, int depth_mask) 126 + u64 total_samples, int depth, int depth_mask, 127 + int left_margin) 143 128 { 144 129 struct rb_node *node, *next; 145 130 struct callchain_node *child; ··· 181 164 * But we keep the older depth mask for the line seperator 182 165 * to keep the level link until we reach the last child 183 166 */ 184 - ret += ipchain__fprintf_graph_line(fp, depth, depth_mask); 167 + ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, 168 + left_margin); 185 169 i = 0; 186 170 list_for_each_entry(chain, &child->val, list) { 187 171 if (chain->ip >= PERF_CONTEXT_MAX) ··· 190 172 ret += ipchain__fprintf_graph(fp, chain, depth, 191 173 new_depth_mask, i++, 192 174 new_total, 193 - cumul); 175 + cumul, 176 + left_margin); 194 177 } 195 178 ret += __callchain__fprintf_graph(fp, child, new_total, 196 179 depth + 1, 197 - new_depth_mask | (1 << depth)); 180 + new_depth_mask | (1 << depth), 181 + left_margin); 198 182 node = next; 199 183 } 200 184 ··· 210 190 211 191 ret += ipchain__fprintf_graph(fp, &rem_hits, depth, 212 192 new_depth_mask, 0, new_total, 213 - remaining); 193 + remaining, left_margin); 214 194 } 215 195 216 196 return ret; 217 197 } 218 198 199 + 219 200 static size_t 220 201 callchain__fprintf_graph(FILE *fp, struct callchain_node *self, 221 - u64 total_samples) 202 + u64 total_samples, int left_margin) 222 203 { 223 204 struct callchain_list *chain; 205 + bool printed = false; 224 206 int i = 0; 225 207 int ret = 0; 226 208 ··· 230 208 if (chain->ip >= PERF_CONTEXT_MAX) 231 209 continue; 232 210 233 - if (!i++ && sort_by_sym_first) 211 + if (!i++ && sort__first_dimension == SORT_SYM) 234 212 continue; 235 213 214 + if (!printed) { 215 + ret += callchain__fprintf_left_margin(fp, left_margin); 216 + ret += fprintf(fp, "|\n"); 217 + ret += callchain__fprintf_left_margin(fp, left_margin); 218 + ret += fprintf(fp, "---"); 219 + 220 + left_margin += 3; 221 + printed = true; 222 + } else 223 + ret += callchain__fprintf_left_margin(fp, left_margin); 224 + 236 225 if (chain->sym) 237 - ret += fprintf(fp, " %s\n", chain->sym->name); 226 + ret += fprintf(fp, " %s\n", chain->sym->name); 238 227 else 239 - ret += fprintf(fp, " %p\n", 240 - (void *)(long)chain->ip); 228 + ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); 241 229 } 242 230 243 - ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1); 231 + ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); 244 232 245 233 return ret; 246 234 } ··· 283 251 284 252 static size_t 285 253 hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, 286 - u64 total_samples) 254 + u64 total_samples, int left_margin) 287 255 { 288 256 struct rb_node *rb_node; 289 257 struct callchain_node *chain; ··· 303 271 break; 304 272 case CHAIN_GRAPH_ABS: /* Falldown */ 305 273 case CHAIN_GRAPH_REL: 306 - ret += callchain__fprintf_graph(fp, chain, total_samples); 274 + ret += callchain__fprintf_graph(fp, chain, total_samples, 275 + left_margin); 307 276 case CHAIN_NONE: 308 277 default: 309 278 break; ··· 349 316 350 317 ret += fprintf(fp, "\n"); 351 318 352 - if (callchain) 353 - hist_entry_callchain__fprintf(fp, self, total_samples); 319 + if (callchain) { 320 + int left_margin = 0; 321 + 322 + if (sort__first_dimension == SORT_COMM) { 323 + se = list_first_entry(&hist_entry__sort_list, typeof(*se), 324 + list); 325 + left_margin = se->width ? *se->width : 0; 326 + left_margin -= thread__comm_len(self->thread); 327 + } 328 + 329 + hist_entry_callchain__fprintf(fp, self, total_samples, 330 + left_margin); 331 + } 354 332 355 333 return ret; 356 334 }
+14 -4
tools/perf/util/sort.c
··· 7 7 char *sort_order = default_sort_order; 8 8 int sort__need_collapse = 0; 9 9 int sort__has_parent = 0; 10 - int sort_by_sym_first; 10 + 11 + enum sort_type sort__first_dimension; 11 12 12 13 unsigned int dsos__col_width; 13 14 unsigned int comms__col_width; ··· 267 266 sort__has_parent = 1; 268 267 } 269 268 270 - if (list_empty(&hist_entry__sort_list) && 271 - !strcmp(sd->name, "symbol")) 272 - sort_by_sym_first = true; 269 + if (list_empty(&hist_entry__sort_list)) { 270 + if (!strcmp(sd->name, "pid")) 271 + sort__first_dimension = SORT_PID; 272 + else if (!strcmp(sd->name, "comm")) 273 + sort__first_dimension = SORT_COMM; 274 + else if (!strcmp(sd->name, "dso")) 275 + sort__first_dimension = SORT_DSO; 276 + else if (!strcmp(sd->name, "symbol")) 277 + sort__first_dimension = SORT_SYM; 278 + else if (!strcmp(sd->name, "parent")) 279 + sort__first_dimension = SORT_PARENT; 280 + } 273 281 274 282 list_add_tail(&sd->entry->list, &hist_entry__sort_list); 275 283 sd->taken = 1;
+9 -1
tools/perf/util/sort.h
··· 39 39 extern unsigned int dsos__col_width; 40 40 extern unsigned int comms__col_width; 41 41 extern unsigned int threads__col_width; 42 - extern int sort_by_sym_first; 42 + extern enum sort_type sort__first_dimension; 43 43 44 44 struct hist_entry { 45 45 struct rb_node rb_node; ··· 52 52 struct symbol *parent; 53 53 struct callchain_node callchain; 54 54 struct rb_root sorted_chain; 55 + }; 56 + 57 + enum sort_type { 58 + SORT_PID, 59 + SORT_COMM, 60 + SORT_DSO, 61 + SORT_SYM, 62 + SORT_PARENT 55 63 }; 56 64 57 65 /*
+11
tools/perf/util/thread.c
··· 33 33 return self->comm ? 0 : -ENOMEM; 34 34 } 35 35 36 + int thread__comm_len(struct thread *self) 37 + { 38 + if (!self->comm_len) { 39 + if (!self->comm) 40 + return 0; 41 + self->comm_len = strlen(self->comm); 42 + } 43 + 44 + return self->comm_len; 45 + } 46 + 36 47 static size_t thread__fprintf(struct thread *self, FILE *fp) 37 48 { 38 49 struct rb_node *nd;
+2
tools/perf/util/thread.h
··· 12 12 pid_t pid; 13 13 char shortname[3]; 14 14 char *comm; 15 + int comm_len; 15 16 }; 16 17 17 18 int thread__set_comm(struct thread *self, const char *comm); 19 + int thread__comm_len(struct thread *self); 18 20 struct thread *threads__findnew(pid_t pid); 19 21 struct thread *register_idle_thread(void); 20 22 void thread__insert_map(struct thread *self, struct map *map);