Commit | Line | Data |
---|---|---|
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 |
28 | int |
29 | parse_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 | 87 | static void |
4eb3e478 FW |
88 | rb_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 |
127 | static 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 | 150 | static void |
d2009c51 | 151 | sort_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 | ||
157 | static 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 | ||
177 | static void | |
d2009c51 | 178 | sort_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 |
185 | static 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 | 207 | static void |
d2009c51 | 208 | sort_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 | 215 | int 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 | */ | |
238 | static struct callchain_node * | |
239 | create_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 | 277 | static void |
1b3a0e95 | 278 | fill_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 | 306 | static struct callchain_node * |
1b3a0e95 FW |
307 | add_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 | ||
321 | static 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 | 338 | static void |
1b3a0e95 FW |
339 | split_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 | ||
400 | static int | |
1b3a0e95 FW |
401 | append_chain(struct callchain_node *root, |
402 | struct callchain_cursor *cursor, | |
403 | u64 period); | |
8cb76d99 | 404 | |
deac911c | 405 | static void |
1b3a0e95 FW |
406 | append_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 | 441 | inc_children_hit: |
108553e1 | 442 | root->children_hit += period; |
8cb76d99 FW |
443 | } |
444 | ||
445 | static int | |
1b3a0e95 FW |
446 | append_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 |
504 | int 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 | |
521 | static int | |
1b3a0e95 FW |
522 | merge_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 |
563 | int 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 | ||
569 | int 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 | |
593 | int 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 | ||
608 | int 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 | |
615 | int 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 | ||
653 | out: | |
654 | return 1; | |
655 | } |