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