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 | |
99571ab3 | 18 | #include "hist.h" |
b36f19d5 | 19 | #include "util.h" |
2dc9fb1a NK |
20 | #include "sort.h" |
21 | #include "machine.h" | |
8cb76d99 FW |
22 | #include "callchain.h" |
23 | ||
47260645 NK |
24 | __thread struct callchain_cursor callchain_cursor; |
25 | ||
deac911c | 26 | static void |
4eb3e478 FW |
27 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, |
28 | enum chain_mode mode) | |
8cb76d99 FW |
29 | { |
30 | struct rb_node **p = &root->rb_node; | |
31 | struct rb_node *parent = NULL; | |
32 | struct callchain_node *rnode; | |
f08c3154 | 33 | u64 chain_cumul = callchain_cumul_hits(chain); |
8cb76d99 FW |
34 | |
35 | while (*p) { | |
1953287b FW |
36 | u64 rnode_cumul; |
37 | ||
8cb76d99 FW |
38 | parent = *p; |
39 | rnode = rb_entry(parent, struct callchain_node, rb_node); | |
f08c3154 | 40 | rnode_cumul = callchain_cumul_hits(rnode); |
8cb76d99 | 41 | |
4eb3e478 | 42 | switch (mode) { |
805d127d | 43 | case CHAIN_FLAT: |
4eb3e478 FW |
44 | if (rnode->hit < chain->hit) |
45 | p = &(*p)->rb_left; | |
46 | else | |
47 | p = &(*p)->rb_right; | |
48 | break; | |
805d127d FW |
49 | case CHAIN_GRAPH_ABS: /* Falldown */ |
50 | case CHAIN_GRAPH_REL: | |
1953287b | 51 | if (rnode_cumul < chain_cumul) |
4eb3e478 FW |
52 | p = &(*p)->rb_left; |
53 | else | |
54 | p = &(*p)->rb_right; | |
55 | break; | |
83a0944f | 56 | case CHAIN_NONE: |
4eb3e478 FW |
57 | default: |
58 | break; | |
59 | } | |
8cb76d99 FW |
60 | } |
61 | ||
62 | rb_link_node(&chain->rb_node, parent, p); | |
63 | rb_insert_color(&chain->rb_node, root); | |
64 | } | |
65 | ||
805d127d FW |
66 | static void |
67 | __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, | |
68 | u64 min_hit) | |
69 | { | |
e369517c | 70 | struct rb_node *n; |
805d127d FW |
71 | struct callchain_node *child; |
72 | ||
e369517c NK |
73 | n = rb_first(&node->rb_root_in); |
74 | while (n) { | |
75 | child = rb_entry(n, struct callchain_node, rb_node_in); | |
76 | n = rb_next(n); | |
77 | ||
805d127d | 78 | __sort_chain_flat(rb_root, child, min_hit); |
e369517c | 79 | } |
805d127d FW |
80 | |
81 | if (node->hit && node->hit >= min_hit) | |
82 | rb_insert_callchain(rb_root, node, CHAIN_FLAT); | |
83 | } | |
84 | ||
8cb76d99 FW |
85 | /* |
86 | * Once we get every callchains from the stream, we can now | |
87 | * sort them by hit | |
88 | */ | |
805d127d | 89 | static void |
d2009c51 | 90 | sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root, |
1d037ca1 | 91 | u64 min_hit, struct callchain_param *param __maybe_unused) |
805d127d | 92 | { |
d2009c51 | 93 | __sort_chain_flat(rb_root, &root->node, min_hit); |
805d127d FW |
94 | } |
95 | ||
96 | static void __sort_chain_graph_abs(struct callchain_node *node, | |
97 | u64 min_hit) | |
8cb76d99 | 98 | { |
e369517c | 99 | struct rb_node *n; |
8cb76d99 FW |
100 | struct callchain_node *child; |
101 | ||
805d127d | 102 | node->rb_root = RB_ROOT; |
e369517c NK |
103 | n = rb_first(&node->rb_root_in); |
104 | ||
105 | while (n) { | |
106 | child = rb_entry(n, struct callchain_node, rb_node_in); | |
107 | n = rb_next(n); | |
8cb76d99 | 108 | |
805d127d | 109 | __sort_chain_graph_abs(child, min_hit); |
f08c3154 | 110 | if (callchain_cumul_hits(child) >= min_hit) |
805d127d FW |
111 | rb_insert_callchain(&node->rb_root, child, |
112 | CHAIN_GRAPH_ABS); | |
113 | } | |
114 | } | |
115 | ||
116 | static void | |
d2009c51 | 117 | sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root, |
1d037ca1 | 118 | u64 min_hit, struct callchain_param *param __maybe_unused) |
805d127d | 119 | { |
d2009c51 FW |
120 | __sort_chain_graph_abs(&chain_root->node, min_hit); |
121 | rb_root->rb_node = chain_root->node.rb_root.rb_node; | |
4eb3e478 FW |
122 | } |
123 | ||
805d127d FW |
124 | static void __sort_chain_graph_rel(struct callchain_node *node, |
125 | double min_percent) | |
4eb3e478 | 126 | { |
e369517c | 127 | struct rb_node *n; |
4eb3e478 | 128 | struct callchain_node *child; |
805d127d | 129 | u64 min_hit; |
4eb3e478 FW |
130 | |
131 | node->rb_root = RB_ROOT; | |
c0a8865e | 132 | min_hit = ceil(node->children_hit * min_percent); |
4eb3e478 | 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_graph_rel(child, min_percent); |
f08c3154 | 140 | if (callchain_cumul_hits(child) >= min_hit) |
805d127d FW |
141 | rb_insert_callchain(&node->rb_root, child, |
142 | CHAIN_GRAPH_REL); | |
4eb3e478 FW |
143 | } |
144 | } | |
145 | ||
805d127d | 146 | static void |
d2009c51 | 147 | sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root, |
1d037ca1 | 148 | u64 min_hit __maybe_unused, struct callchain_param *param) |
4eb3e478 | 149 | { |
d2009c51 FW |
150 | __sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0); |
151 | rb_root->rb_node = chain_root->node.rb_root.rb_node; | |
8cb76d99 FW |
152 | } |
153 | ||
16537f13 | 154 | int callchain_register_param(struct callchain_param *param) |
805d127d FW |
155 | { |
156 | switch (param->mode) { | |
157 | case CHAIN_GRAPH_ABS: | |
158 | param->sort = sort_chain_graph_abs; | |
159 | break; | |
160 | case CHAIN_GRAPH_REL: | |
161 | param->sort = sort_chain_graph_rel; | |
162 | break; | |
163 | case CHAIN_FLAT: | |
164 | param->sort = sort_chain_flat; | |
165 | break; | |
83a0944f | 166 | case CHAIN_NONE: |
805d127d FW |
167 | default: |
168 | return -1; | |
169 | } | |
170 | return 0; | |
171 | } | |
172 | ||
deac911c FW |
173 | /* |
174 | * Create a child for a parent. If inherit_children, then the new child | |
175 | * will become the new parent of it's parent children | |
176 | */ | |
177 | static struct callchain_node * | |
178 | create_child(struct callchain_node *parent, bool inherit_children) | |
8cb76d99 FW |
179 | { |
180 | struct callchain_node *new; | |
181 | ||
cdd5b75b | 182 | new = zalloc(sizeof(*new)); |
8cb76d99 FW |
183 | if (!new) { |
184 | perror("not enough memory to create child for code path tree"); | |
185 | return NULL; | |
186 | } | |
187 | new->parent = parent; | |
8cb76d99 | 188 | INIT_LIST_HEAD(&new->val); |
deac911c FW |
189 | |
190 | if (inherit_children) { | |
e369517c NK |
191 | struct rb_node *n; |
192 | struct callchain_node *child; | |
193 | ||
194 | new->rb_root_in = parent->rb_root_in; | |
195 | parent->rb_root_in = RB_ROOT; | |
deac911c | 196 | |
e369517c NK |
197 | n = rb_first(&new->rb_root_in); |
198 | while (n) { | |
199 | child = rb_entry(n, struct callchain_node, rb_node_in); | |
200 | child->parent = new; | |
201 | n = rb_next(n); | |
202 | } | |
deac911c | 203 | |
e369517c NK |
204 | /* make it the first child */ |
205 | rb_link_node(&new->rb_node_in, NULL, &parent->rb_root_in.rb_node); | |
206 | rb_insert_color(&new->rb_node_in, &parent->rb_root_in); | |
deac911c | 207 | } |
8cb76d99 FW |
208 | |
209 | return new; | |
210 | } | |
211 | ||
301fde27 | 212 | |
deac911c FW |
213 | /* |
214 | * Fill the node with callchain values | |
215 | */ | |
8cb76d99 | 216 | static void |
1b3a0e95 | 217 | fill_node(struct callchain_node *node, struct callchain_cursor *cursor) |
8cb76d99 | 218 | { |
1b3a0e95 FW |
219 | struct callchain_cursor_node *cursor_node; |
220 | ||
221 | node->val_nr = cursor->nr - cursor->pos; | |
222 | if (!node->val_nr) | |
223 | pr_warning("Warning: empty node in callchain tree\n"); | |
8cb76d99 | 224 | |
1b3a0e95 FW |
225 | cursor_node = callchain_cursor_current(cursor); |
226 | ||
227 | while (cursor_node) { | |
8cb76d99 FW |
228 | struct callchain_list *call; |
229 | ||
cdd5b75b | 230 | call = zalloc(sizeof(*call)); |
8cb76d99 FW |
231 | if (!call) { |
232 | perror("not enough memory for the code path tree"); | |
233 | return; | |
234 | } | |
1b3a0e95 FW |
235 | call->ip = cursor_node->ip; |
236 | call->ms.sym = cursor_node->sym; | |
237 | call->ms.map = cursor_node->map; | |
8cb76d99 | 238 | list_add_tail(&call->list, &node->val); |
1b3a0e95 FW |
239 | |
240 | callchain_cursor_advance(cursor); | |
241 | cursor_node = callchain_cursor_current(cursor); | |
8cb76d99 | 242 | } |
8cb76d99 FW |
243 | } |
244 | ||
e369517c | 245 | static struct callchain_node * |
1b3a0e95 FW |
246 | add_child(struct callchain_node *parent, |
247 | struct callchain_cursor *cursor, | |
248 | u64 period) | |
8cb76d99 FW |
249 | { |
250 | struct callchain_node *new; | |
251 | ||
deac911c | 252 | new = create_child(parent, false); |
1b3a0e95 | 253 | fill_node(new, cursor); |
8cb76d99 | 254 | |
1953287b | 255 | new->children_hit = 0; |
108553e1 | 256 | new->hit = period; |
e369517c NK |
257 | return new; |
258 | } | |
259 | ||
260 | static s64 match_chain(struct callchain_cursor_node *node, | |
261 | struct callchain_list *cnode) | |
262 | { | |
263 | struct symbol *sym = node->sym; | |
264 | ||
265 | if (cnode->ms.sym && sym && | |
266 | callchain_param.key == CCKEY_FUNCTION) | |
267 | return cnode->ms.sym->start - sym->start; | |
268 | else | |
269 | return cnode->ip - node->ip; | |
8cb76d99 FW |
270 | } |
271 | ||
deac911c FW |
272 | /* |
273 | * Split the parent in two parts (a new child is created) and | |
274 | * give a part of its callchain to the created child. | |
275 | * Then create another child to host the given callchain of new branch | |
276 | */ | |
8cb76d99 | 277 | static void |
1b3a0e95 FW |
278 | split_add_child(struct callchain_node *parent, |
279 | struct callchain_cursor *cursor, | |
280 | struct callchain_list *to_split, | |
281 | u64 idx_parents, u64 idx_local, u64 period) | |
8cb76d99 FW |
282 | { |
283 | struct callchain_node *new; | |
deac911c | 284 | struct list_head *old_tail; |
f37a291c | 285 | unsigned int idx_total = idx_parents + idx_local; |
8cb76d99 FW |
286 | |
287 | /* split */ | |
deac911c FW |
288 | new = create_child(parent, true); |
289 | ||
290 | /* split the callchain and move a part to the new child */ | |
291 | old_tail = parent->val.prev; | |
292 | list_del_range(&to_split->list, old_tail); | |
293 | new->val.next = &to_split->list; | |
294 | new->val.prev = old_tail; | |
295 | to_split->list.prev = &new->val; | |
296 | old_tail->next = &new->val; | |
8cb76d99 | 297 | |
deac911c FW |
298 | /* split the hits */ |
299 | new->hit = parent->hit; | |
1953287b | 300 | new->children_hit = parent->children_hit; |
f08c3154 | 301 | parent->children_hit = callchain_cumul_hits(new); |
deac911c FW |
302 | new->val_nr = parent->val_nr - idx_local; |
303 | parent->val_nr = idx_local; | |
304 | ||
305 | /* create a new child for the new branch if any */ | |
1b3a0e95 | 306 | if (idx_total < cursor->nr) { |
e369517c NK |
307 | struct callchain_node *first; |
308 | struct callchain_list *cnode; | |
309 | struct callchain_cursor_node *node; | |
310 | struct rb_node *p, **pp; | |
311 | ||
deac911c | 312 | parent->hit = 0; |
108553e1 | 313 | parent->children_hit += period; |
e369517c NK |
314 | |
315 | node = callchain_cursor_current(cursor); | |
316 | new = add_child(parent, cursor, period); | |
317 | ||
318 | /* | |
319 | * This is second child since we moved parent's children | |
320 | * to new (first) child above. | |
321 | */ | |
322 | p = parent->rb_root_in.rb_node; | |
323 | first = rb_entry(p, struct callchain_node, rb_node_in); | |
324 | cnode = list_first_entry(&first->val, struct callchain_list, | |
325 | list); | |
326 | ||
327 | if (match_chain(node, cnode) < 0) | |
328 | pp = &p->rb_left; | |
329 | else | |
330 | pp = &p->rb_right; | |
331 | ||
332 | rb_link_node(&new->rb_node_in, p, pp); | |
333 | rb_insert_color(&new->rb_node_in, &parent->rb_root_in); | |
deac911c | 334 | } else { |
108553e1 | 335 | parent->hit = period; |
deac911c | 336 | } |
8cb76d99 FW |
337 | } |
338 | ||
339 | static int | |
1b3a0e95 FW |
340 | append_chain(struct callchain_node *root, |
341 | struct callchain_cursor *cursor, | |
342 | u64 period); | |
8cb76d99 | 343 | |
deac911c | 344 | static void |
1b3a0e95 FW |
345 | append_chain_children(struct callchain_node *root, |
346 | struct callchain_cursor *cursor, | |
347 | u64 period) | |
8cb76d99 FW |
348 | { |
349 | struct callchain_node *rnode; | |
e369517c NK |
350 | struct callchain_cursor_node *node; |
351 | struct rb_node **p = &root->rb_root_in.rb_node; | |
352 | struct rb_node *parent = NULL; | |
353 | ||
354 | node = callchain_cursor_current(cursor); | |
355 | if (!node) | |
356 | return; | |
8cb76d99 FW |
357 | |
358 | /* lookup in childrens */ | |
e369517c NK |
359 | while (*p) { |
360 | s64 ret; | |
361 | struct callchain_list *cnode; | |
f37a291c | 362 | |
e369517c NK |
363 | parent = *p; |
364 | rnode = rb_entry(parent, struct callchain_node, rb_node_in); | |
365 | cnode = list_first_entry(&rnode->val, struct callchain_list, | |
366 | list); | |
367 | ||
368 | /* just check first entry */ | |
369 | ret = match_chain(node, cnode); | |
370 | if (ret == 0) { | |
371 | append_chain(rnode, cursor, period); | |
1953287b | 372 | goto inc_children_hit; |
e369517c NK |
373 | } |
374 | ||
375 | if (ret < 0) | |
376 | p = &parent->rb_left; | |
377 | else | |
378 | p = &parent->rb_right; | |
8cb76d99 | 379 | } |
deac911c | 380 | /* nothing in children, add to the current node */ |
e369517c NK |
381 | rnode = add_child(root, cursor, period); |
382 | rb_link_node(&rnode->rb_node_in, parent, p); | |
383 | rb_insert_color(&rnode->rb_node_in, &root->rb_root_in); | |
e05b876c | 384 | |
1953287b | 385 | inc_children_hit: |
108553e1 | 386 | root->children_hit += period; |
8cb76d99 FW |
387 | } |
388 | ||
389 | static int | |
1b3a0e95 FW |
390 | append_chain(struct callchain_node *root, |
391 | struct callchain_cursor *cursor, | |
392 | u64 period) | |
8cb76d99 | 393 | { |
1b3a0e95 | 394 | struct callchain_cursor_node *curr_snap = cursor->curr; |
8cb76d99 | 395 | struct callchain_list *cnode; |
1b3a0e95 | 396 | u64 start = cursor->pos; |
8cb76d99 | 397 | bool found = false; |
1b3a0e95 | 398 | u64 matches; |
8cb76d99 | 399 | |
deac911c FW |
400 | /* |
401 | * Lookup in the current node | |
402 | * If we have a symbol, then compare the start to match | |
99571ab3 AK |
403 | * anywhere inside a function, unless function |
404 | * mode is disabled. | |
deac911c | 405 | */ |
8cb76d99 | 406 | list_for_each_entry(cnode, &root->val, list) { |
1b3a0e95 | 407 | struct callchain_cursor_node *node; |
301fde27 | 408 | |
1b3a0e95 FW |
409 | node = callchain_cursor_current(cursor); |
410 | if (!node) | |
deac911c | 411 | break; |
301fde27 | 412 | |
e369517c | 413 | if (match_chain(node, cnode) != 0) |
8cb76d99 | 414 | break; |
301fde27 | 415 | |
e369517c | 416 | found = true; |
1b3a0e95 FW |
417 | |
418 | callchain_cursor_advance(cursor); | |
8cb76d99 FW |
419 | } |
420 | ||
e369517c | 421 | /* matches not, relay no the parent */ |
1b3a0e95 FW |
422 | if (!found) { |
423 | cursor->curr = curr_snap; | |
424 | cursor->pos = start; | |
8cb76d99 | 425 | return -1; |
1b3a0e95 FW |
426 | } |
427 | ||
428 | matches = cursor->pos - start; | |
8cb76d99 FW |
429 | |
430 | /* we match only a part of the node. Split it and add the new chain */ | |
1b3a0e95 FW |
431 | if (matches < root->val_nr) { |
432 | split_add_child(root, cursor, cnode, start, matches, period); | |
8cb76d99 FW |
433 | return 0; |
434 | } | |
435 | ||
436 | /* we match 100% of the path, increment the hit */ | |
1b3a0e95 | 437 | if (matches == root->val_nr && cursor->pos == cursor->nr) { |
108553e1 | 438 | root->hit += period; |
8cb76d99 FW |
439 | return 0; |
440 | } | |
441 | ||
deac911c | 442 | /* We match the node and still have a part remaining */ |
1b3a0e95 | 443 | append_chain_children(root, cursor, period); |
deac911c FW |
444 | |
445 | return 0; | |
8cb76d99 FW |
446 | } |
447 | ||
1b3a0e95 FW |
448 | int callchain_append(struct callchain_root *root, |
449 | struct callchain_cursor *cursor, | |
450 | u64 period) | |
8cb76d99 | 451 | { |
1b3a0e95 | 452 | if (!cursor->nr) |
301fde27 FW |
453 | return 0; |
454 | ||
1b3a0e95 | 455 | callchain_cursor_commit(cursor); |
301fde27 | 456 | |
1b3a0e95 | 457 | append_chain_children(&root->node, cursor, period); |
d2009c51 | 458 | |
1b3a0e95 FW |
459 | if (cursor->nr > root->max_depth) |
460 | root->max_depth = cursor->nr; | |
301fde27 FW |
461 | |
462 | return 0; | |
8cb76d99 | 463 | } |
612d4fd7 FW |
464 | |
465 | static int | |
1b3a0e95 FW |
466 | merge_chain_branch(struct callchain_cursor *cursor, |
467 | struct callchain_node *dst, struct callchain_node *src) | |
612d4fd7 | 468 | { |
1b3a0e95 | 469 | struct callchain_cursor_node **old_last = cursor->last; |
e369517c | 470 | struct callchain_node *child; |
612d4fd7 | 471 | struct callchain_list *list, *next_list; |
e369517c | 472 | struct rb_node *n; |
1b3a0e95 | 473 | int old_pos = cursor->nr; |
612d4fd7 FW |
474 | int err = 0; |
475 | ||
476 | list_for_each_entry_safe(list, next_list, &src->val, list) { | |
1b3a0e95 FW |
477 | callchain_cursor_append(cursor, list->ip, |
478 | list->ms.map, list->ms.sym); | |
612d4fd7 FW |
479 | list_del(&list->list); |
480 | free(list); | |
481 | } | |
482 | ||
1b3a0e95 FW |
483 | if (src->hit) { |
484 | callchain_cursor_commit(cursor); | |
485 | append_chain_children(dst, cursor, src->hit); | |
486 | } | |
612d4fd7 | 487 | |
e369517c NK |
488 | n = rb_first(&src->rb_root_in); |
489 | while (n) { | |
490 | child = container_of(n, struct callchain_node, rb_node_in); | |
491 | n = rb_next(n); | |
492 | rb_erase(&child->rb_node_in, &src->rb_root_in); | |
493 | ||
1b3a0e95 | 494 | err = merge_chain_branch(cursor, dst, child); |
612d4fd7 FW |
495 | if (err) |
496 | break; | |
497 | ||
612d4fd7 FW |
498 | free(child); |
499 | } | |
500 | ||
1b3a0e95 FW |
501 | cursor->nr = old_pos; |
502 | cursor->last = old_last; | |
612d4fd7 FW |
503 | |
504 | return err; | |
505 | } | |
506 | ||
1b3a0e95 FW |
507 | int callchain_merge(struct callchain_cursor *cursor, |
508 | struct callchain_root *dst, struct callchain_root *src) | |
509 | { | |
510 | return merge_chain_branch(cursor, &dst->node, &src->node); | |
511 | } | |
512 | ||
513 | int callchain_cursor_append(struct callchain_cursor *cursor, | |
514 | u64 ip, struct map *map, struct symbol *sym) | |
612d4fd7 | 515 | { |
1b3a0e95 | 516 | struct callchain_cursor_node *node = *cursor->last; |
612d4fd7 | 517 | |
1b3a0e95 | 518 | if (!node) { |
91b98804 | 519 | node = calloc(1, sizeof(*node)); |
1b3a0e95 FW |
520 | if (!node) |
521 | return -ENOMEM; | |
612d4fd7 | 522 | |
1b3a0e95 FW |
523 | *cursor->last = node; |
524 | } | |
612d4fd7 | 525 | |
1b3a0e95 FW |
526 | node->ip = ip; |
527 | node->map = map; | |
528 | node->sym = sym; | |
612d4fd7 | 529 | |
1b3a0e95 | 530 | cursor->nr++; |
612d4fd7 | 531 | |
1b3a0e95 FW |
532 | cursor->last = &node->next; |
533 | ||
534 | return 0; | |
612d4fd7 | 535 | } |
2dc9fb1a NK |
536 | |
537 | int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent, | |
538 | struct perf_evsel *evsel, struct addr_location *al, | |
539 | int max_stack) | |
540 | { | |
541 | if (sample->callchain == NULL) | |
542 | return 0; | |
543 | ||
544 | if (symbol_conf.use_callchain || sort__has_parent) { | |
545 | return machine__resolve_callchain(al->machine, evsel, al->thread, | |
546 | sample, parent, al, max_stack); | |
547 | } | |
548 | return 0; | |
549 | } | |
550 | ||
551 | int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample) | |
552 | { | |
553 | if (!symbol_conf.use_callchain) | |
554 | return 0; | |
555 | return callchain_append(he->callchain, &callchain_cursor, sample->period); | |
556 | } |