Commit a4fb581b15949cfd10b64c8af37bc106e95307f3

Authored by Frederic Weisbecker
Committed by Ingo Molnar
1 parent af0a6fa463

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>

Showing 5 changed files with 99 additions and 24 deletions Side-by-side Diff

tools/perf/builtin-report.c
... ... @@ -59,12 +59,28 @@
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)
63 65 {
64 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)
  79 +{
  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))
71 87  
... ... @@ -79,12 +95,12 @@
79 95 static size_t
80 96 ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth,
81 97 int depth_mask, int count, u64 total_samples,
82   - int hits)
  98 + int hits, int left_margin)
83 99 {
84 100 int i;
85 101 size_t ret = 0;
86 102  
87   - ret += fprintf(fp, "%s", " ");
  103 + ret += callchain__fprintf_left_margin(fp, left_margin);
88 104 for (i = 0; i < depth; i++) {
89 105 if (depth_mask & (1 << i))
90 106 ret += fprintf(fp, "|");
... ... @@ -123,7 +139,8 @@
123 139  
124 140 static size_t
125 141 __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
126   - u64 total_samples, int depth, int depth_mask)
  142 + u64 total_samples, int depth, int depth_mask,
  143 + int left_margin)
127 144 {
128 145 struct rb_node *node, *next;
129 146 struct callchain_node *child;
... ... @@ -164,7 +181,8 @@
164 181 * But we keep the older depth mask for the line seperator
165 182 * to keep the level link until we reach the last child
166 183 */
167   - ret += ipchain__fprintf_graph_line(fp, depth, depth_mask);
  184 + ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
  185 + left_margin);
168 186 i = 0;
169 187 list_for_each_entry(chain, &child->val, list) {
170 188 if (chain->ip >= PERF_CONTEXT_MAX)
171 189  
... ... @@ -172,11 +190,13 @@
172 190 ret += ipchain__fprintf_graph(fp, chain, depth,
173 191 new_depth_mask, i++,
174 192 new_total,
175   - cumul);
  193 + cumul,
  194 + left_margin);
176 195 }
177 196 ret += __callchain__fprintf_graph(fp, child, new_total,
178 197 depth + 1,
179   - new_depth_mask | (1 << depth));
  198 + new_depth_mask | (1 << depth),
  199 + left_margin);
180 200 node = next;
181 201 }
182 202  
183 203  
184 204  
185 205  
... ... @@ -190,17 +210,19 @@
190 210  
191 211 ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
192 212 new_depth_mask, 0, new_total,
193   - remaining);
  213 + remaining, left_margin);
194 214 }
195 215  
196 216 return ret;
197 217 }
198 218  
  219 +
199 220 static size_t
200 221 callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
201   - u64 total_samples)
  222 + u64 total_samples, int left_margin)
202 223 {
203 224 struct callchain_list *chain;
  225 + bool printed = false;
204 226 int i = 0;
205 227 int ret = 0;
206 228  
207 229  
208 230  
209 231  
210 232  
... ... @@ -208,17 +230,27 @@
208 230 if (chain->ip >= PERF_CONTEXT_MAX)
209 231 continue;
210 232  
211   - if (!i++ && sort_by_sym_first)
  233 + if (!i++ && sort__first_dimension == SORT_SYM)
212 234 continue;
213 235  
  236 + if (!printed) {
  237 + ret += callchain__fprintf_left_margin(fp, left_margin);
  238 + ret += fprintf(fp, "|\n");
  239 + ret += callchain__fprintf_left_margin(fp, left_margin);
  240 + ret += fprintf(fp, "---");
  241 +
  242 + left_margin += 3;
  243 + printed = true;
  244 + } else
  245 + ret += callchain__fprintf_left_margin(fp, left_margin);
  246 +
214 247 if (chain->sym)
215   - ret += fprintf(fp, " %s\n", chain->sym->name);
  248 + ret += fprintf(fp, " %s\n", chain->sym->name);
216 249 else
217   - ret += fprintf(fp, " %p\n",
218   - (void *)(long)chain->ip);
  250 + ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
219 251 }
220 252  
221   - ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1);
  253 + ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
222 254  
223 255 return ret;
224 256 }
... ... @@ -251,7 +283,7 @@
251 283  
252 284 static size_t
253 285 hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
254   - u64 total_samples)
  286 + u64 total_samples, int left_margin)
255 287 {
256 288 struct rb_node *rb_node;
257 289 struct callchain_node *chain;
... ... @@ -271,7 +303,8 @@
271 303 break;
272 304 case CHAIN_GRAPH_ABS: /* Falldown */
273 305 case CHAIN_GRAPH_REL:
274   - ret += callchain__fprintf_graph(fp, chain, total_samples);
  306 + ret += callchain__fprintf_graph(fp, chain, total_samples,
  307 + left_margin);
275 308 case CHAIN_NONE:
276 309 default:
277 310 break;
... ... @@ -316,8 +349,19 @@
316 349  
317 350 ret += fprintf(fp, "\n");
318 351  
319   - if (callchain)
320   - hist_entry_callchain__fprintf(fp, self, total_samples);
  352 + if (callchain) {
  353 + int left_margin = 0;
  354 +
  355 + if (sort__first_dimension == SORT_COMM) {
  356 + se = list_first_entry(&hist_entry__sort_list, typeof(*se),
  357 + list);
  358 + left_margin = se->width ? *se->width : 0;
  359 + left_margin -= thread__comm_len(self->thread);
  360 + }
  361 +
  362 + hist_entry_callchain__fprintf(fp, self, total_samples,
  363 + left_margin);
  364 + }
321 365  
322 366 return ret;
323 367 }
tools/perf/util/sort.c
... ... @@ -7,8 +7,9 @@
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;
11 10  
  11 +enum sort_type sort__first_dimension;
  12 +
12 13 unsigned int dsos__col_width;
13 14 unsigned int comms__col_width;
14 15 unsigned int threads__col_width;
... ... @@ -266,9 +267,18 @@
266 267 sort__has_parent = 1;
267 268 }
268 269  
269   - if (list_empty(&hist_entry__sort_list) &&
270   - !strcmp(sd->name, "symbol"))
271   - sort_by_sym_first = true;
  270 + if (list_empty(&hist_entry__sort_list)) {
  271 + if (!strcmp(sd->name, "pid"))
  272 + sort__first_dimension = SORT_PID;
  273 + else if (!strcmp(sd->name, "comm"))
  274 + sort__first_dimension = SORT_COMM;
  275 + else if (!strcmp(sd->name, "dso"))
  276 + sort__first_dimension = SORT_DSO;
  277 + else if (!strcmp(sd->name, "symbol"))
  278 + sort__first_dimension = SORT_SYM;
  279 + else if (!strcmp(sd->name, "parent"))
  280 + sort__first_dimension = SORT_PARENT;
  281 + }
272 282  
273 283 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
274 284 sd->taken = 1;
tools/perf/util/sort.h
... ... @@ -39,7 +39,7 @@
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,6 +52,14 @@
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 /*
tools/perf/util/thread.c
... ... @@ -33,6 +33,17 @@
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;
tools/perf/util/thread.h
... ... @@ -12,9 +12,11 @@
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);