perf tools: Add +field argument support for --sort option
[deliverable/linux.git] / tools / perf / util / callchain.c
CommitLineData
8cb76d99 1/*
1b3a0e95 2 * Copyright (C) 2009-2011, Frederic Weisbecker <fweisbec@gmail.com>
8cb76d99
FW
3 *
4 * Handle the callchains from the stream in an ad-hoc radix tree and then
5 * sort them in an rbtree.
6 *
deac911c
FW
7 * Using a radix for code path provides a fast retrieval and factorizes
8 * memory use. Also that lets us use the paths in a hierarchical graph view.
9 *
8cb76d99
FW
10 */
11
12#include <stdlib.h>
13#include <stdio.h>
14#include <stdbool.h>
15#include <errno.h>
c0a8865e 16#include <math.h>
8cb76d99 17
b965bb41
FW
18#include "asm/bug.h"
19
99571ab3 20#include "hist.h"
b36f19d5 21#include "util.h"
2dc9fb1a
NK
22#include "sort.h"
23#include "machine.h"
8cb76d99
FW
24#include "callchain.h"
25
47260645
NK
26__thread struct callchain_cursor callchain_cursor;
27
cff6bb46
DZ
28int
29parse_callchain_report_opt(const char *arg)
30{
e8232f1a 31 char *tok;
cff6bb46 32 char *endptr;
e8232f1a 33 bool minpcnt_set = false;
cff6bb46
DZ
34
35 symbol_conf.use_callchain = true;
36
37 if (!arg)
38 return 0;
39
e8232f1a
NK
40 while ((tok = strtok((char *)arg, ",")) != NULL) {
41 if (!strncmp(tok, "none", strlen(tok))) {
42 callchain_param.mode = CHAIN_NONE;
43 symbol_conf.use_callchain = false;
44 return 0;
45 }
46
47 /* try to get the output mode */
48 if (!strncmp(tok, "graph", strlen(tok)))
49 callchain_param.mode = CHAIN_GRAPH_ABS;
50 else if (!strncmp(tok, "flat", strlen(tok)))
51 callchain_param.mode = CHAIN_FLAT;
52 else if (!strncmp(tok, "fractal", strlen(tok)))
53 callchain_param.mode = CHAIN_GRAPH_REL;
54 /* try to get the call chain order */
55 else if (!strncmp(tok, "caller", strlen(tok)))
56 callchain_param.order = ORDER_CALLER;
57 else if (!strncmp(tok, "callee", strlen(tok)))
58 callchain_param.order = ORDER_CALLEE;
59 /* try to get the sort key */
60 else if (!strncmp(tok, "function", strlen(tok)))
61 callchain_param.key = CCKEY_FUNCTION;
62 else if (!strncmp(tok, "address", strlen(tok)))
63 callchain_param.key = CCKEY_ADDRESS;
64 /* try to get the min percent */
65 else if (!minpcnt_set) {
66 callchain_param.min_percent = strtod(tok, &endptr);
67 if (tok == endptr)
68 return -1;
69 minpcnt_set = true;
70 } else {
71 /* try print limit at last */
72 callchain_param.print_limit = strtoul(tok, &endptr, 0);
73 if (tok == endptr)
74 return -1;
75 }
76
77 arg = NULL;
cff6bb46
DZ
78 }
79
cff6bb46
DZ
80 if (callchain_register_param(&callchain_param) < 0) {
81 pr_err("Can't register callchain params\n");
82 return -1;
83 }
84 return 0;
85}
86
deac911c 87static void
4eb3e478
FW
88rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
89 enum chain_mode mode)
8cb76d99
FW
90{
91 struct rb_node **p = &root->rb_node;
92 struct rb_node *parent = NULL;
93 struct callchain_node *rnode;
f08c3154 94 u64 chain_cumul = callchain_cumul_hits(chain);
8cb76d99
FW
95
96 while (*p) {
1953287b
FW
97 u64 rnode_cumul;
98
8cb76d99
FW
99 parent = *p;
100 rnode = rb_entry(parent, struct callchain_node, rb_node);
f08c3154 101 rnode_cumul = callchain_cumul_hits(rnode);
8cb76d99 102
4eb3e478 103 switch (mode) {
805d127d 104 case CHAIN_FLAT:
4eb3e478
FW
105 if (rnode->hit < chain->hit)
106 p = &(*p)->rb_left;
107 else
108 p = &(*p)->rb_right;
109 break;
805d127d
FW
110 case CHAIN_GRAPH_ABS: /* Falldown */
111 case CHAIN_GRAPH_REL:
1953287b 112 if (rnode_cumul < chain_cumul)
4eb3e478
FW
113 p = &(*p)->rb_left;
114 else
115 p = &(*p)->rb_right;
116 break;
83a0944f 117 case CHAIN_NONE:
4eb3e478
FW
118 default:
119 break;
120 }
8cb76d99
FW
121 }
122
123 rb_link_node(&chain->rb_node, parent, p);
124 rb_insert_color(&chain->rb_node, root);
125}
126
805d127d
FW
127static void
128__sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
129 u64 min_hit)
130{
e369517c 131 struct rb_node *n;
805d127d
FW
132 struct callchain_node *child;
133
e369517c
NK
134 n = rb_first(&node->rb_root_in);
135 while (n) {
136 child = rb_entry(n, struct callchain_node, rb_node_in);
137 n = rb_next(n);
138
805d127d 139 __sort_chain_flat(rb_root, child, min_hit);
e369517c 140 }
805d127d
FW
141
142 if (node->hit && node->hit >= min_hit)
143 rb_insert_callchain(rb_root, node, CHAIN_FLAT);
144}
145
8cb76d99
FW
146/*
147 * Once we get every callchains from the stream, we can now
148 * sort them by hit
149 */
805d127d 150static void
d2009c51 151sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
1d037ca1 152 u64 min_hit, struct callchain_param *param __maybe_unused)
805d127d 153{
d2009c51 154 __sort_chain_flat(rb_root, &root->node, min_hit);
805d127d
FW
155}
156
157static void __sort_chain_graph_abs(struct callchain_node *node,
158 u64 min_hit)
8cb76d99 159{
e369517c 160 struct rb_node *n;
8cb76d99
FW
161 struct callchain_node *child;
162
805d127d 163 node->rb_root = RB_ROOT;
e369517c
NK
164 n = rb_first(&node->rb_root_in);
165
166 while (n) {
167 child = rb_entry(n, struct callchain_node, rb_node_in);
168 n = rb_next(n);
8cb76d99 169
805d127d 170 __sort_chain_graph_abs(child, min_hit);
f08c3154 171 if (callchain_cumul_hits(child) >= min_hit)
805d127d
FW
172 rb_insert_callchain(&node->rb_root, child,
173 CHAIN_GRAPH_ABS);
174 }
175}
176
177static void
d2009c51 178sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,
1d037ca1 179 u64 min_hit, struct callchain_param *param __maybe_unused)
805d127d 180{
d2009c51
FW
181 __sort_chain_graph_abs(&chain_root->node, min_hit);
182 rb_root->rb_node = chain_root->node.rb_root.rb_node;
4eb3e478
FW
183}
184
805d127d
FW
185static void __sort_chain_graph_rel(struct callchain_node *node,
186 double min_percent)
4eb3e478 187{
e369517c 188 struct rb_node *n;
4eb3e478 189 struct callchain_node *child;
805d127d 190 u64 min_hit;
4eb3e478
FW
191
192 node->rb_root = RB_ROOT;
c0a8865e 193 min_hit = ceil(node->children_hit * min_percent);
4eb3e478 194
e369517c
NK
195 n = rb_first(&node->rb_root_in);
196 while (n) {
197 child = rb_entry(n, struct callchain_node, rb_node_in);
198 n = rb_next(n);
199
805d127d 200 __sort_chain_graph_rel(child, min_percent);
f08c3154 201 if (callchain_cumul_hits(child) >= min_hit)
805d127d
FW
202 rb_insert_callchain(&node->rb_root, child,
203 CHAIN_GRAPH_REL);
4eb3e478
FW
204 }
205}
206
805d127d 207static void
d2009c51 208sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root,
1d037ca1 209 u64 min_hit __maybe_unused, struct callchain_param *param)
4eb3e478 210{
d2009c51
FW
211 __sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0);
212 rb_root->rb_node = chain_root->node.rb_root.rb_node;
8cb76d99
FW
213}
214
16537f13 215int callchain_register_param(struct callchain_param *param)
805d127d
FW
216{
217 switch (param->mode) {
218 case CHAIN_GRAPH_ABS:
219 param->sort = sort_chain_graph_abs;
220 break;
221 case CHAIN_GRAPH_REL:
222 param->sort = sort_chain_graph_rel;
223 break;
224 case CHAIN_FLAT:
225 param->sort = sort_chain_flat;
226 break;
83a0944f 227 case CHAIN_NONE:
805d127d
FW
228 default:
229 return -1;
230 }
231 return 0;
232}
233
deac911c
FW
234/*
235 * Create a child for a parent. If inherit_children, then the new child
236 * will become the new parent of it's parent children
237 */
238static struct callchain_node *
239create_child(struct callchain_node *parent, bool inherit_children)
8cb76d99
FW
240{
241 struct callchain_node *new;
242
cdd5b75b 243 new = zalloc(sizeof(*new));
8cb76d99
FW
244 if (!new) {
245 perror("not enough memory to create child for code path tree");
246 return NULL;
247 }
248 new->parent = parent;
8cb76d99 249 INIT_LIST_HEAD(&new->val);
deac911c
FW
250
251 if (inherit_children) {
e369517c
NK
252 struct rb_node *n;
253 struct callchain_node *child;
254
255 new->rb_root_in = parent->rb_root_in;
256 parent->rb_root_in = RB_ROOT;
deac911c 257
e369517c
NK
258 n = rb_first(&new->rb_root_in);
259 while (n) {
260 child = rb_entry(n, struct callchain_node, rb_node_in);
261 child->parent = new;
262 n = rb_next(n);
263 }
deac911c 264
e369517c
NK
265 /* make it the first child */
266 rb_link_node(&new->rb_node_in, NULL, &parent->rb_root_in.rb_node);
267 rb_insert_color(&new->rb_node_in, &parent->rb_root_in);
deac911c 268 }
8cb76d99
FW
269
270 return new;
271}
272
301fde27 273
deac911c
FW
274/*
275 * Fill the node with callchain values
276 */
8cb76d99 277static void
1b3a0e95 278fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
8cb76d99 279{
1b3a0e95
FW
280 struct callchain_cursor_node *cursor_node;
281
282 node->val_nr = cursor->nr - cursor->pos;
283 if (!node->val_nr)
284 pr_warning("Warning: empty node in callchain tree\n");
8cb76d99 285
1b3a0e95
FW
286 cursor_node = callchain_cursor_current(cursor);
287
288 while (cursor_node) {
8cb76d99
FW
289 struct callchain_list *call;
290
cdd5b75b 291 call = zalloc(sizeof(*call));
8cb76d99
FW
292 if (!call) {
293 perror("not enough memory for the code path tree");
294 return;
295 }
1b3a0e95
FW
296 call->ip = cursor_node->ip;
297 call->ms.sym = cursor_node->sym;
298 call->ms.map = cursor_node->map;
8cb76d99 299 list_add_tail(&call->list, &node->val);
1b3a0e95
FW
300
301 callchain_cursor_advance(cursor);
302 cursor_node = callchain_cursor_current(cursor);
8cb76d99 303 }
8cb76d99
FW
304}
305
e369517c 306static struct callchain_node *
1b3a0e95
FW
307add_child(struct callchain_node *parent,
308 struct callchain_cursor *cursor,
309 u64 period)
8cb76d99
FW
310{
311 struct callchain_node *new;
312
deac911c 313 new = create_child(parent, false);
1b3a0e95 314 fill_node(new, cursor);
8cb76d99 315
1953287b 316 new->children_hit = 0;
108553e1 317 new->hit = period;
e369517c
NK
318 return new;
319}
320
321static s64 match_chain(struct callchain_cursor_node *node,
322 struct callchain_list *cnode)
323{
324 struct symbol *sym = node->sym;
325
326 if (cnode->ms.sym && sym &&
327 callchain_param.key == CCKEY_FUNCTION)
328 return cnode->ms.sym->start - sym->start;
329 else
330 return cnode->ip - node->ip;
8cb76d99
FW
331}
332
deac911c
FW
333/*
334 * Split the parent in two parts (a new child is created) and
335 * give a part of its callchain to the created child.
336 * Then create another child to host the given callchain of new branch
337 */
8cb76d99 338static void
1b3a0e95
FW
339split_add_child(struct callchain_node *parent,
340 struct callchain_cursor *cursor,
341 struct callchain_list *to_split,
342 u64 idx_parents, u64 idx_local, u64 period)
8cb76d99
FW
343{
344 struct callchain_node *new;
deac911c 345 struct list_head *old_tail;
f37a291c 346 unsigned int idx_total = idx_parents + idx_local;
8cb76d99
FW
347
348 /* split */
deac911c
FW
349 new = create_child(parent, true);
350
351 /* split the callchain and move a part to the new child */
352 old_tail = parent->val.prev;
353 list_del_range(&to_split->list, old_tail);
354 new->val.next = &to_split->list;
355 new->val.prev = old_tail;
356 to_split->list.prev = &new->val;
357 old_tail->next = &new->val;
8cb76d99 358
deac911c
FW
359 /* split the hits */
360 new->hit = parent->hit;
1953287b 361 new->children_hit = parent->children_hit;
f08c3154 362 parent->children_hit = callchain_cumul_hits(new);
deac911c
FW
363 new->val_nr = parent->val_nr - idx_local;
364 parent->val_nr = idx_local;
365
366 /* create a new child for the new branch if any */
1b3a0e95 367 if (idx_total < cursor->nr) {
e369517c
NK
368 struct callchain_node *first;
369 struct callchain_list *cnode;
370 struct callchain_cursor_node *node;
371 struct rb_node *p, **pp;
372
deac911c 373 parent->hit = 0;
108553e1 374 parent->children_hit += period;
e369517c
NK
375
376 node = callchain_cursor_current(cursor);
377 new = add_child(parent, cursor, period);
378
379 /*
380 * This is second child since we moved parent's children
381 * to new (first) child above.
382 */
383 p = parent->rb_root_in.rb_node;
384 first = rb_entry(p, struct callchain_node, rb_node_in);
385 cnode = list_first_entry(&first->val, struct callchain_list,
386 list);
387
388 if (match_chain(node, cnode) < 0)
389 pp = &p->rb_left;
390 else
391 pp = &p->rb_right;
392
393 rb_link_node(&new->rb_node_in, p, pp);
394 rb_insert_color(&new->rb_node_in, &parent->rb_root_in);
deac911c 395 } else {
108553e1 396 parent->hit = period;
deac911c 397 }
8cb76d99
FW
398}
399
400static int
1b3a0e95
FW
401append_chain(struct callchain_node *root,
402 struct callchain_cursor *cursor,
403 u64 period);
8cb76d99 404
deac911c 405static void
1b3a0e95
FW
406append_chain_children(struct callchain_node *root,
407 struct callchain_cursor *cursor,
408 u64 period)
8cb76d99
FW
409{
410 struct callchain_node *rnode;
e369517c
NK
411 struct callchain_cursor_node *node;
412 struct rb_node **p = &root->rb_root_in.rb_node;
413 struct rb_node *parent = NULL;
414
415 node = callchain_cursor_current(cursor);
416 if (!node)
417 return;
8cb76d99
FW
418
419 /* lookup in childrens */
e369517c
NK
420 while (*p) {
421 s64 ret;
f37a291c 422
e369517c
NK
423 parent = *p;
424 rnode = rb_entry(parent, struct callchain_node, rb_node_in);
e369517c 425
b965bb41
FW
426 /* If at least first entry matches, rely to children */
427 ret = append_chain(rnode, cursor, period);
428 if (ret == 0)
1953287b 429 goto inc_children_hit;
e369517c
NK
430
431 if (ret < 0)
432 p = &parent->rb_left;
433 else
434 p = &parent->rb_right;
8cb76d99 435 }
deac911c 436 /* nothing in children, add to the current node */
e369517c
NK
437 rnode = add_child(root, cursor, period);
438 rb_link_node(&rnode->rb_node_in, parent, p);
439 rb_insert_color(&rnode->rb_node_in, &root->rb_root_in);
e05b876c 440
1953287b 441inc_children_hit:
108553e1 442 root->children_hit += period;
8cb76d99
FW
443}
444
445static int
1b3a0e95
FW
446append_chain(struct callchain_node *root,
447 struct callchain_cursor *cursor,
448 u64 period)
8cb76d99
FW
449{
450 struct callchain_list *cnode;
1b3a0e95 451 u64 start = cursor->pos;
8cb76d99 452 bool found = false;
1b3a0e95 453 u64 matches;
b965bb41 454 int cmp = 0;
8cb76d99 455
deac911c
FW
456 /*
457 * Lookup in the current node
458 * If we have a symbol, then compare the start to match
99571ab3
AK
459 * anywhere inside a function, unless function
460 * mode is disabled.
deac911c 461 */
8cb76d99 462 list_for_each_entry(cnode, &root->val, list) {
1b3a0e95 463 struct callchain_cursor_node *node;
301fde27 464
1b3a0e95
FW
465 node = callchain_cursor_current(cursor);
466 if (!node)
deac911c 467 break;
301fde27 468
b965bb41
FW
469 cmp = match_chain(node, cnode);
470 if (cmp)
8cb76d99 471 break;
301fde27 472
e369517c 473 found = true;
1b3a0e95
FW
474
475 callchain_cursor_advance(cursor);
8cb76d99
FW
476 }
477
e369517c 478 /* matches not, relay no the parent */
1b3a0e95 479 if (!found) {
b965bb41 480 WARN_ONCE(!cmp, "Chain comparison error\n");
b965bb41 481 return cmp;
1b3a0e95
FW
482 }
483
484 matches = cursor->pos - start;
8cb76d99
FW
485
486 /* we match only a part of the node. Split it and add the new chain */
1b3a0e95
FW
487 if (matches < root->val_nr) {
488 split_add_child(root, cursor, cnode, start, matches, period);
8cb76d99
FW
489 return 0;
490 }
491
492 /* we match 100% of the path, increment the hit */
1b3a0e95 493 if (matches == root->val_nr && cursor->pos == cursor->nr) {
108553e1 494 root->hit += period;
8cb76d99
FW
495 return 0;
496 }
497
deac911c 498 /* We match the node and still have a part remaining */
1b3a0e95 499 append_chain_children(root, cursor, period);
deac911c
FW
500
501 return 0;
8cb76d99
FW
502}
503
1b3a0e95
FW
504int callchain_append(struct callchain_root *root,
505 struct callchain_cursor *cursor,
506 u64 period)
8cb76d99 507{
1b3a0e95 508 if (!cursor->nr)
301fde27
FW
509 return 0;
510
1b3a0e95 511 callchain_cursor_commit(cursor);
301fde27 512
1b3a0e95 513 append_chain_children(&root->node, cursor, period);
d2009c51 514
1b3a0e95
FW
515 if (cursor->nr > root->max_depth)
516 root->max_depth = cursor->nr;
301fde27
FW
517
518 return 0;
8cb76d99 519}
612d4fd7
FW
520
521static int
1b3a0e95
FW
522merge_chain_branch(struct callchain_cursor *cursor,
523 struct callchain_node *dst, struct callchain_node *src)
612d4fd7 524{
1b3a0e95 525 struct callchain_cursor_node **old_last = cursor->last;
e369517c 526 struct callchain_node *child;
612d4fd7 527 struct callchain_list *list, *next_list;
e369517c 528 struct rb_node *n;
1b3a0e95 529 int old_pos = cursor->nr;
612d4fd7
FW
530 int err = 0;
531
532 list_for_each_entry_safe(list, next_list, &src->val, list) {
1b3a0e95
FW
533 callchain_cursor_append(cursor, list->ip,
534 list->ms.map, list->ms.sym);
612d4fd7
FW
535 list_del(&list->list);
536 free(list);
537 }
538
1b3a0e95
FW
539 if (src->hit) {
540 callchain_cursor_commit(cursor);
541 append_chain_children(dst, cursor, src->hit);
542 }
612d4fd7 543
e369517c
NK
544 n = rb_first(&src->rb_root_in);
545 while (n) {
546 child = container_of(n, struct callchain_node, rb_node_in);
547 n = rb_next(n);
548 rb_erase(&child->rb_node_in, &src->rb_root_in);
549
1b3a0e95 550 err = merge_chain_branch(cursor, dst, child);
612d4fd7
FW
551 if (err)
552 break;
553
612d4fd7
FW
554 free(child);
555 }
556
1b3a0e95
FW
557 cursor->nr = old_pos;
558 cursor->last = old_last;
612d4fd7
FW
559
560 return err;
561}
562
1b3a0e95
FW
563int callchain_merge(struct callchain_cursor *cursor,
564 struct callchain_root *dst, struct callchain_root *src)
565{
566 return merge_chain_branch(cursor, &dst->node, &src->node);
567}
568
569int callchain_cursor_append(struct callchain_cursor *cursor,
570 u64 ip, struct map *map, struct symbol *sym)
612d4fd7 571{
1b3a0e95 572 struct callchain_cursor_node *node = *cursor->last;
612d4fd7 573
1b3a0e95 574 if (!node) {
91b98804 575 node = calloc(1, sizeof(*node));
1b3a0e95
FW
576 if (!node)
577 return -ENOMEM;
612d4fd7 578
1b3a0e95
FW
579 *cursor->last = node;
580 }
612d4fd7 581
1b3a0e95
FW
582 node->ip = ip;
583 node->map = map;
584 node->sym = sym;
612d4fd7 585
1b3a0e95 586 cursor->nr++;
612d4fd7 587
1b3a0e95
FW
588 cursor->last = &node->next;
589
590 return 0;
612d4fd7 591}
2dc9fb1a
NK
592
593int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent,
594 struct perf_evsel *evsel, struct addr_location *al,
595 int max_stack)
596{
597 if (sample->callchain == NULL)
598 return 0;
599
7a13aa28
NK
600 if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
601 sort__has_parent) {
2dc9fb1a
NK
602 return machine__resolve_callchain(al->machine, evsel, al->thread,
603 sample, parent, al, max_stack);
604 }
605 return 0;
606}
607
608int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample)
609{
4d40b051 610 if (!symbol_conf.use_callchain || sample->callchain == NULL)
2dc9fb1a
NK
611 return 0;
612 return callchain_append(he->callchain, &callchain_cursor, sample->period);
613}
c7405d85
NK
614
615int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
616 bool hide_unresolved)
617{
618 al->map = node->map;
619 al->sym = node->sym;
620 if (node->map)
621 al->addr = node->map->map_ip(node->map, node->ip);
622 else
623 al->addr = node->ip;
624
625 if (al->sym == NULL) {
626 if (hide_unresolved)
627 return 0;
628 if (al->map == NULL)
629 goto out;
630 }
631
632 if (al->map->groups == &al->machine->kmaps) {
633 if (machine__is_host(al->machine)) {
634 al->cpumode = PERF_RECORD_MISC_KERNEL;
635 al->level = 'k';
636 } else {
637 al->cpumode = PERF_RECORD_MISC_GUEST_KERNEL;
638 al->level = 'g';
639 }
640 } else {
641 if (machine__is_host(al->machine)) {
642 al->cpumode = PERF_RECORD_MISC_USER;
643 al->level = '.';
644 } else if (perf_guest) {
645 al->cpumode = PERF_RECORD_MISC_GUEST_USER;
646 al->level = 'u';
647 } else {
648 al->cpumode = PERF_RECORD_MISC_HYPERVISOR;
649 al->level = 'H';
650 }
651 }
652
653out:
654 return 1;
655}
This page took 0.228779 seconds and 5 git commands to generate.