aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2010-03-22 12:09:33 -0400
committerIngo Molnar <mingo@elte.hu>2010-03-22 13:47:34 -0400
commit301fde27c7fcd0380b02b175d547e894ff65d78a (patch)
tree65548981b45e99d63f64e6cb819028825b543c9a /tools/perf/util
parentd2f1e15b661e71fd52111f51c99a6ce41384e9ef (diff)
perf: Fix orphan callchain branches
Callchains have markers inside their capture to tell we enter a context (kernel, user, ...). Those are not displayed in the callchains but they are incidentally an active part of the radix tree where callchains are stored, just like any other address. If we have the two following callchains: addr1 -> addr2 -> user context -> addr3 addr1 -> addr2 -> user context -> addr4 addr1 -> addr2 -> addr 5 This is pretty common if addr1 and addr2 are part of an interrupt path, addr3 and addr4 are user addresses and addr5 is a kernel non interrupt path. This will be stored as follows in the tree: addr1 addr2 / \ / addr5 user context / \ addr3 addr4 But we ignore the context markers in the report, hence the addr3 and addr4 will appear as orphan branches: |--28.30%-- hrtimer_interrupt | smp_apic_timer_interrupt | apic_timer_interrupt | | <------------- here, no parent! | | | | | |--11.11%-- 0x7fae7bccb875 | | | | | |--11.11%-- 0xffffffffff60013b | | | | | |--11.11%-- __pthread_mutex_lock_internal | | | | | |--11.11%-- __errno_location Fix this by removing the context markers when we process the callchains to the tree. Reported-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net> Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> LKML-Reference: <1269274173-20328-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/callchain.c109
-rw-r--r--tools/perf/util/callchain.h4
-rw-r--r--tools/perf/util/hist.c5
3 files changed, 83 insertions, 35 deletions
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index b3b71258272a..883844eb4b0a 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2009, Frederic Weisbecker <fweisbec@gmail.com> 2 * Copyright (C) 2009-2010, Frederic Weisbecker <fweisbec@gmail.com>
3 * 3 *
4 * Handle the callchains from the stream in an ad-hoc radix tree and then 4 * Handle the callchains from the stream in an ad-hoc radix tree and then
5 * sort them in an rbtree. 5 * sort them in an rbtree.
@@ -183,12 +183,23 @@ create_child(struct callchain_node *parent, bool inherit_children)
183 return new; 183 return new;
184} 184}
185 185
186
187struct resolved_ip {
188 u64 ip;
189 struct symbol *sym;
190};
191
192struct resolved_chain {
193 u64 nr;
194 struct resolved_ip ips[0];
195};
196
197
186/* 198/*
187 * Fill the node with callchain values 199 * Fill the node with callchain values
188 */ 200 */
189static void 201static void
190fill_node(struct callchain_node *node, struct ip_callchain *chain, 202fill_node(struct callchain_node *node, struct resolved_chain *chain, int start)
191 int start, struct symbol **syms)
192{ 203{
193 unsigned int i; 204 unsigned int i;
194 205
@@ -200,8 +211,8 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain,
200 perror("not enough memory for the code path tree"); 211 perror("not enough memory for the code path tree");
201 return; 212 return;
202 } 213 }
203 call->ip = chain->ips[i]; 214 call->ip = chain->ips[i].ip;
204 call->sym = syms[i]; 215 call->sym = chain->ips[i].sym;
205 list_add_tail(&call->list, &node->val); 216 list_add_tail(&call->list, &node->val);
206 } 217 }
207 node->val_nr = chain->nr - start; 218 node->val_nr = chain->nr - start;
@@ -210,13 +221,13 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain,
210} 221}
211 222
212static void 223static void
213add_child(struct callchain_node *parent, struct ip_callchain *chain, 224add_child(struct callchain_node *parent, struct resolved_chain *chain,
214 int start, struct symbol **syms) 225 int start)
215{ 226{
216 struct callchain_node *new; 227 struct callchain_node *new;
217 228
218 new = create_child(parent, false); 229 new = create_child(parent, false);
219 fill_node(new, chain, start, syms); 230 fill_node(new, chain, start);
220 231
221 new->children_hit = 0; 232 new->children_hit = 0;
222 new->hit = 1; 233 new->hit = 1;
@@ -228,9 +239,8 @@ add_child(struct callchain_node *parent, struct ip_callchain *chain,
228 * Then create another child to host the given callchain of new branch 239 * Then create another child to host the given callchain of new branch
229 */ 240 */
230static void 241static void
231split_add_child(struct callchain_node *parent, struct ip_callchain *chain, 242split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
232 struct callchain_list *to_split, int idx_parents, int idx_local, 243 struct callchain_list *to_split, int idx_parents, int idx_local)
233 struct symbol **syms)
234{ 244{
235 struct callchain_node *new; 245 struct callchain_node *new;
236 struct list_head *old_tail; 246 struct list_head *old_tail;
@@ -257,7 +267,7 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain,
257 /* create a new child for the new branch if any */ 267 /* create a new child for the new branch if any */
258 if (idx_total < chain->nr) { 268 if (idx_total < chain->nr) {
259 parent->hit = 0; 269 parent->hit = 0;
260 add_child(parent, chain, idx_total, syms); 270 add_child(parent, chain, idx_total);
261 parent->children_hit++; 271 parent->children_hit++;
262 } else { 272 } else {
263 parent->hit = 1; 273 parent->hit = 1;
@@ -265,32 +275,33 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain,
265} 275}
266 276
267static int 277static int
268__append_chain(struct callchain_node *root, struct ip_callchain *chain, 278__append_chain(struct callchain_node *root, struct resolved_chain *chain,
269 unsigned int start, struct symbol **syms); 279 unsigned int start);
270 280
271static void 281static void
272__append_chain_children(struct callchain_node *root, struct ip_callchain *chain, 282__append_chain_children(struct callchain_node *root,
273 struct symbol **syms, unsigned int start) 283 struct resolved_chain *chain,
284 unsigned int start)
274{ 285{
275 struct callchain_node *rnode; 286 struct callchain_node *rnode;
276 287
277 /* lookup in childrens */ 288 /* lookup in childrens */
278 chain_for_each_child(rnode, root) { 289 chain_for_each_child(rnode, root) {
279 unsigned int ret = __append_chain(rnode, chain, start, syms); 290 unsigned int ret = __append_chain(rnode, chain, start);
280 291
281 if (!ret) 292 if (!ret)
282 goto inc_children_hit; 293 goto inc_children_hit;
283 } 294 }
284 /* nothing in children, add to the current node */ 295 /* nothing in children, add to the current node */
285 add_child(root, chain, start, syms); 296 add_child(root, chain, start);
286 297
287inc_children_hit: 298inc_children_hit:
288 root->children_hit++; 299 root->children_hit++;
289} 300}
290 301
291static int 302static int
292__append_chain(struct callchain_node *root, struct ip_callchain *chain, 303__append_chain(struct callchain_node *root, struct resolved_chain *chain,
293 unsigned int start, struct symbol **syms) 304 unsigned int start)
294{ 305{
295 struct callchain_list *cnode; 306 struct callchain_list *cnode;
296 unsigned int i = start; 307 unsigned int i = start;
@@ -302,13 +313,19 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain,
302 * anywhere inside a function. 313 * anywhere inside a function.
303 */ 314 */
304 list_for_each_entry(cnode, &root->val, list) { 315 list_for_each_entry(cnode, &root->val, list) {
316 struct symbol *sym;
317
305 if (i == chain->nr) 318 if (i == chain->nr)
306 break; 319 break;
307 if (cnode->sym && syms[i]) { 320
308 if (cnode->sym->start != syms[i]->start) 321 sym = chain->ips[i].sym;
322
323 if (cnode->sym && sym) {
324 if (cnode->sym->start != sym->start)
309 break; 325 break;
310 } else if (cnode->ip != chain->ips[i]) 326 } else if (cnode->ip != chain->ips[i].ip)
311 break; 327 break;
328
312 if (!found) 329 if (!found)
313 found = true; 330 found = true;
314 i++; 331 i++;
@@ -320,7 +337,7 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain,
320 337
321 /* we match only a part of the node. Split it and add the new chain */ 338 /* we match only a part of the node. Split it and add the new chain */
322 if (i - start < root->val_nr) { 339 if (i - start < root->val_nr) {
323 split_add_child(root, chain, cnode, start, i - start, syms); 340 split_add_child(root, chain, cnode, start, i - start);
324 return 0; 341 return 0;
325 } 342 }
326 343
@@ -331,15 +348,51 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain,
331 } 348 }
332 349
333 /* We match the node and still have a part remaining */ 350 /* We match the node and still have a part remaining */
334 __append_chain_children(root, chain, syms, i); 351 __append_chain_children(root, chain, i);
335 352
336 return 0; 353 return 0;
337} 354}
338 355
339void append_chain(struct callchain_node *root, struct ip_callchain *chain, 356static void
357filter_context(struct ip_callchain *old, struct resolved_chain *new,
358 struct symbol **syms)
359{
360 int i, j = 0;
361
362 for (i = 0; i < (int)old->nr; i++) {
363 if (old->ips[i] >= PERF_CONTEXT_MAX)
364 continue;
365
366 new->ips[j].ip = old->ips[i];
367 new->ips[j].sym = syms[i];
368 j++;
369 }
370
371 new->nr = j;
372}
373
374
375int append_chain(struct callchain_node *root, struct ip_callchain *chain,
340 struct symbol **syms) 376 struct symbol **syms)
341{ 377{
378 struct resolved_chain *filtered;
379
342 if (!chain->nr) 380 if (!chain->nr)
343 return; 381 return 0;
344 __append_chain_children(root, chain, syms, 0); 382
383 filtered = malloc(sizeof(*filtered) +
384 chain->nr * sizeof(struct resolved_ip));
385 if (!filtered)
386 return -ENOMEM;
387
388 filter_context(chain, filtered, syms);
389
390 if (!filtered->nr)
391 goto end;
392
393 __append_chain_children(root, filtered, 0);
394end:
395 free(filtered);
396
397 return 0;
345} 398}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index ad4626de4c2b..bbd76da27f22 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -56,6 +56,6 @@ static inline u64 cumul_hits(struct callchain_node *node)
56} 56}
57 57
58int register_callchain_param(struct callchain_param *param); 58int register_callchain_param(struct callchain_param *param);
59void append_chain(struct callchain_node *root, struct ip_callchain *chain, 59int append_chain(struct callchain_node *root, struct ip_callchain *chain,
60 struct symbol **syms); 60 struct symbol **syms);
61#endif /* __PERF_CALLCHAIN_H */ 61#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index c37da8b88573..5843a9c572ad 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -328,8 +328,6 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
328 left_margin); 328 left_margin);
329 i = 0; 329 i = 0;
330 list_for_each_entry(chain, &child->val, list) { 330 list_for_each_entry(chain, &child->val, list) {
331 if (chain->ip >= PERF_CONTEXT_MAX)
332 continue;
333 ret += ipchain__fprintf_graph(fp, chain, depth, 331 ret += ipchain__fprintf_graph(fp, chain, depth,
334 new_depth_mask, i++, 332 new_depth_mask, i++,
335 new_total, 333 new_total,
@@ -368,9 +366,6 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
368 int ret = 0; 366 int ret = 0;
369 367
370 list_for_each_entry(chain, &self->val, list) { 368 list_for_each_entry(chain, &self->val, list) {
371 if (chain->ip >= PERF_CONTEXT_MAX)
372 continue;
373
374 if (!i++ && sort__first_dimension == SORT_SYM) 369 if (!i++ && sort__first_dimension == SORT_SYM)
375 continue; 370 continue;
376 371