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