diff options
Diffstat (limited to 'tools/perf/util')
27 files changed, 1158 insertions, 585 deletions
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index e12d539417b2..f8c66d1435e0 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2009-2010, Frederic Weisbecker <fweisbec@gmail.com> | 2 | * Copyright (C) 2009-2011, 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. |
@@ -26,10 +26,10 @@ bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) | |||
26 | } | 26 | } |
27 | 27 | ||
28 | #define chain_for_each_child(child, parent) \ | 28 | #define chain_for_each_child(child, parent) \ |
29 | list_for_each_entry(child, &parent->children, brothers) | 29 | list_for_each_entry(child, &parent->children, siblings) |
30 | 30 | ||
31 | #define chain_for_each_child_safe(child, next, parent) \ | 31 | #define chain_for_each_child_safe(child, next, parent) \ |
32 | list_for_each_entry_safe(child, next, &parent->children, brothers) | 32 | list_for_each_entry_safe(child, next, &parent->children, siblings) |
33 | 33 | ||
34 | static void | 34 | static void |
35 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | 35 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, |
@@ -38,14 +38,14 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | |||
38 | struct rb_node **p = &root->rb_node; | 38 | struct rb_node **p = &root->rb_node; |
39 | struct rb_node *parent = NULL; | 39 | struct rb_node *parent = NULL; |
40 | struct callchain_node *rnode; | 40 | struct callchain_node *rnode; |
41 | u64 chain_cumul = cumul_hits(chain); | 41 | u64 chain_cumul = callchain_cumul_hits(chain); |
42 | 42 | ||
43 | while (*p) { | 43 | while (*p) { |
44 | u64 rnode_cumul; | 44 | u64 rnode_cumul; |
45 | 45 | ||
46 | parent = *p; | 46 | parent = *p; |
47 | rnode = rb_entry(parent, struct callchain_node, rb_node); | 47 | rnode = rb_entry(parent, struct callchain_node, rb_node); |
48 | rnode_cumul = cumul_hits(rnode); | 48 | rnode_cumul = callchain_cumul_hits(rnode); |
49 | 49 | ||
50 | switch (mode) { | 50 | switch (mode) { |
51 | case CHAIN_FLAT: | 51 | case CHAIN_FLAT: |
@@ -104,7 +104,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node, | |||
104 | 104 | ||
105 | chain_for_each_child(child, node) { | 105 | chain_for_each_child(child, node) { |
106 | __sort_chain_graph_abs(child, min_hit); | 106 | __sort_chain_graph_abs(child, min_hit); |
107 | if (cumul_hits(child) >= min_hit) | 107 | if (callchain_cumul_hits(child) >= min_hit) |
108 | rb_insert_callchain(&node->rb_root, child, | 108 | rb_insert_callchain(&node->rb_root, child, |
109 | CHAIN_GRAPH_ABS); | 109 | CHAIN_GRAPH_ABS); |
110 | } | 110 | } |
@@ -129,7 +129,7 @@ static void __sort_chain_graph_rel(struct callchain_node *node, | |||
129 | 129 | ||
130 | chain_for_each_child(child, node) { | 130 | chain_for_each_child(child, node) { |
131 | __sort_chain_graph_rel(child, min_percent); | 131 | __sort_chain_graph_rel(child, min_percent); |
132 | if (cumul_hits(child) >= min_hit) | 132 | if (callchain_cumul_hits(child) >= min_hit) |
133 | rb_insert_callchain(&node->rb_root, child, | 133 | rb_insert_callchain(&node->rb_root, child, |
134 | CHAIN_GRAPH_REL); | 134 | CHAIN_GRAPH_REL); |
135 | } | 135 | } |
@@ -143,7 +143,7 @@ sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root, | |||
143 | rb_root->rb_node = chain_root->node.rb_root.rb_node; | 143 | rb_root->rb_node = chain_root->node.rb_root.rb_node; |
144 | } | 144 | } |
145 | 145 | ||
146 | int register_callchain_param(struct callchain_param *param) | 146 | int callchain_register_param(struct callchain_param *param) |
147 | { | 147 | { |
148 | switch (param->mode) { | 148 | switch (param->mode) { |
149 | case CHAIN_GRAPH_ABS: | 149 | case CHAIN_GRAPH_ABS: |
@@ -189,32 +189,27 @@ create_child(struct callchain_node *parent, bool inherit_children) | |||
189 | chain_for_each_child(next, new) | 189 | chain_for_each_child(next, new) |
190 | next->parent = new; | 190 | next->parent = new; |
191 | } | 191 | } |
192 | list_add_tail(&new->brothers, &parent->children); | 192 | list_add_tail(&new->siblings, &parent->children); |
193 | 193 | ||
194 | return new; | 194 | return new; |
195 | } | 195 | } |
196 | 196 | ||
197 | 197 | ||
198 | struct resolved_ip { | ||
199 | u64 ip; | ||
200 | struct map_symbol ms; | ||
201 | }; | ||
202 | |||
203 | struct resolved_chain { | ||
204 | u64 nr; | ||
205 | struct resolved_ip ips[0]; | ||
206 | }; | ||
207 | |||
208 | |||
209 | /* | 198 | /* |
210 | * Fill the node with callchain values | 199 | * Fill the node with callchain values |
211 | */ | 200 | */ |
212 | static void | 201 | static void |
213 | fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) | 202 | fill_node(struct callchain_node *node, struct callchain_cursor *cursor) |
214 | { | 203 | { |
215 | unsigned int i; | 204 | struct callchain_cursor_node *cursor_node; |
205 | |||
206 | node->val_nr = cursor->nr - cursor->pos; | ||
207 | if (!node->val_nr) | ||
208 | pr_warning("Warning: empty node in callchain tree\n"); | ||
216 | 209 | ||
217 | for (i = start; i < chain->nr; i++) { | 210 | cursor_node = callchain_cursor_current(cursor); |
211 | |||
212 | while (cursor_node) { | ||
218 | struct callchain_list *call; | 213 | struct callchain_list *call; |
219 | 214 | ||
220 | call = zalloc(sizeof(*call)); | 215 | call = zalloc(sizeof(*call)); |
@@ -222,23 +217,25 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) | |||
222 | perror("not enough memory for the code path tree"); | 217 | perror("not enough memory for the code path tree"); |
223 | return; | 218 | return; |
224 | } | 219 | } |
225 | call->ip = chain->ips[i].ip; | 220 | call->ip = cursor_node->ip; |
226 | call->ms = chain->ips[i].ms; | 221 | call->ms.sym = cursor_node->sym; |
222 | call->ms.map = cursor_node->map; | ||
227 | list_add_tail(&call->list, &node->val); | 223 | list_add_tail(&call->list, &node->val); |
224 | |||
225 | callchain_cursor_advance(cursor); | ||
226 | cursor_node = callchain_cursor_current(cursor); | ||
228 | } | 227 | } |
229 | node->val_nr = chain->nr - start; | ||
230 | if (!node->val_nr) | ||
231 | pr_warning("Warning: empty node in callchain tree\n"); | ||
232 | } | 228 | } |
233 | 229 | ||
234 | static void | 230 | static void |
235 | add_child(struct callchain_node *parent, struct resolved_chain *chain, | 231 | add_child(struct callchain_node *parent, |
236 | int start, u64 period) | 232 | struct callchain_cursor *cursor, |
233 | u64 period) | ||
237 | { | 234 | { |
238 | struct callchain_node *new; | 235 | struct callchain_node *new; |
239 | 236 | ||
240 | new = create_child(parent, false); | 237 | new = create_child(parent, false); |
241 | fill_node(new, chain, start); | 238 | fill_node(new, cursor); |
242 | 239 | ||
243 | new->children_hit = 0; | 240 | new->children_hit = 0; |
244 | new->hit = period; | 241 | new->hit = period; |
@@ -250,9 +247,10 @@ add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
250 | * Then create another child to host the given callchain of new branch | 247 | * Then create another child to host the given callchain of new branch |
251 | */ | 248 | */ |
252 | static void | 249 | static void |
253 | split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | 250 | split_add_child(struct callchain_node *parent, |
254 | struct callchain_list *to_split, int idx_parents, int idx_local, | 251 | struct callchain_cursor *cursor, |
255 | u64 period) | 252 | struct callchain_list *to_split, |
253 | u64 idx_parents, u64 idx_local, u64 period) | ||
256 | { | 254 | { |
257 | struct callchain_node *new; | 255 | struct callchain_node *new; |
258 | struct list_head *old_tail; | 256 | struct list_head *old_tail; |
@@ -272,14 +270,14 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
272 | /* split the hits */ | 270 | /* split the hits */ |
273 | new->hit = parent->hit; | 271 | new->hit = parent->hit; |
274 | new->children_hit = parent->children_hit; | 272 | new->children_hit = parent->children_hit; |
275 | parent->children_hit = cumul_hits(new); | 273 | parent->children_hit = callchain_cumul_hits(new); |
276 | new->val_nr = parent->val_nr - idx_local; | 274 | new->val_nr = parent->val_nr - idx_local; |
277 | parent->val_nr = idx_local; | 275 | parent->val_nr = idx_local; |
278 | 276 | ||
279 | /* create a new child for the new branch if any */ | 277 | /* create a new child for the new branch if any */ |
280 | if (idx_total < chain->nr) { | 278 | if (idx_total < cursor->nr) { |
281 | parent->hit = 0; | 279 | parent->hit = 0; |
282 | add_child(parent, chain, idx_total, period); | 280 | add_child(parent, cursor, period); |
283 | parent->children_hit += period; | 281 | parent->children_hit += period; |
284 | } else { | 282 | } else { |
285 | parent->hit = period; | 283 | parent->hit = period; |
@@ -287,36 +285,41 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
287 | } | 285 | } |
288 | 286 | ||
289 | static int | 287 | static int |
290 | append_chain(struct callchain_node *root, struct resolved_chain *chain, | 288 | append_chain(struct callchain_node *root, |
291 | unsigned int start, u64 period); | 289 | struct callchain_cursor *cursor, |
290 | u64 period); | ||
292 | 291 | ||
293 | static void | 292 | static void |
294 | append_chain_children(struct callchain_node *root, struct resolved_chain *chain, | 293 | append_chain_children(struct callchain_node *root, |
295 | unsigned int start, u64 period) | 294 | struct callchain_cursor *cursor, |
295 | u64 period) | ||
296 | { | 296 | { |
297 | struct callchain_node *rnode; | 297 | struct callchain_node *rnode; |
298 | 298 | ||
299 | /* lookup in childrens */ | 299 | /* lookup in childrens */ |
300 | chain_for_each_child(rnode, root) { | 300 | chain_for_each_child(rnode, root) { |
301 | unsigned int ret = append_chain(rnode, chain, start, period); | 301 | unsigned int ret = append_chain(rnode, cursor, period); |
302 | 302 | ||
303 | if (!ret) | 303 | if (!ret) |
304 | goto inc_children_hit; | 304 | goto inc_children_hit; |
305 | } | 305 | } |
306 | /* nothing in children, add to the current node */ | 306 | /* nothing in children, add to the current node */ |
307 | add_child(root, chain, start, period); | 307 | add_child(root, cursor, period); |
308 | 308 | ||
309 | inc_children_hit: | 309 | inc_children_hit: |
310 | root->children_hit += period; | 310 | root->children_hit += period; |
311 | } | 311 | } |
312 | 312 | ||
313 | static int | 313 | static int |
314 | append_chain(struct callchain_node *root, struct resolved_chain *chain, | 314 | append_chain(struct callchain_node *root, |
315 | unsigned int start, u64 period) | 315 | struct callchain_cursor *cursor, |
316 | u64 period) | ||
316 | { | 317 | { |
318 | struct callchain_cursor_node *curr_snap = cursor->curr; | ||
317 | struct callchain_list *cnode; | 319 | struct callchain_list *cnode; |
318 | unsigned int i = start; | 320 | u64 start = cursor->pos; |
319 | bool found = false; | 321 | bool found = false; |
322 | u64 matches; | ||
320 | 323 | ||
321 | /* | 324 | /* |
322 | * Lookup in the current node | 325 | * Lookup in the current node |
@@ -324,141 +327,134 @@ append_chain(struct callchain_node *root, struct resolved_chain *chain, | |||
324 | * anywhere inside a function. | 327 | * anywhere inside a function. |
325 | */ | 328 | */ |
326 | list_for_each_entry(cnode, &root->val, list) { | 329 | list_for_each_entry(cnode, &root->val, list) { |
330 | struct callchain_cursor_node *node; | ||
327 | struct symbol *sym; | 331 | struct symbol *sym; |
328 | 332 | ||
329 | if (i == chain->nr) | 333 | node = callchain_cursor_current(cursor); |
334 | if (!node) | ||
330 | break; | 335 | break; |
331 | 336 | ||
332 | sym = chain->ips[i].ms.sym; | 337 | sym = node->sym; |
333 | 338 | ||
334 | if (cnode->ms.sym && sym) { | 339 | if (cnode->ms.sym && sym) { |
335 | if (cnode->ms.sym->start != sym->start) | 340 | if (cnode->ms.sym->start != sym->start) |
336 | break; | 341 | break; |
337 | } else if (cnode->ip != chain->ips[i].ip) | 342 | } else if (cnode->ip != node->ip) |
338 | break; | 343 | break; |
339 | 344 | ||
340 | if (!found) | 345 | if (!found) |
341 | found = true; | 346 | found = true; |
342 | i++; | 347 | |
348 | callchain_cursor_advance(cursor); | ||
343 | } | 349 | } |
344 | 350 | ||
345 | /* matches not, relay on the parent */ | 351 | /* matches not, relay on the parent */ |
346 | if (!found) | 352 | if (!found) { |
353 | cursor->curr = curr_snap; | ||
354 | cursor->pos = start; | ||
347 | return -1; | 355 | return -1; |
356 | } | ||
357 | |||
358 | matches = cursor->pos - start; | ||
348 | 359 | ||
349 | /* we match only a part of the node. Split it and add the new chain */ | 360 | /* we match only a part of the node. Split it and add the new chain */ |
350 | if (i - start < root->val_nr) { | 361 | if (matches < root->val_nr) { |
351 | split_add_child(root, chain, cnode, start, i - start, period); | 362 | split_add_child(root, cursor, cnode, start, matches, period); |
352 | return 0; | 363 | return 0; |
353 | } | 364 | } |
354 | 365 | ||
355 | /* we match 100% of the path, increment the hit */ | 366 | /* we match 100% of the path, increment the hit */ |
356 | if (i - start == root->val_nr && i == chain->nr) { | 367 | if (matches == root->val_nr && cursor->pos == cursor->nr) { |
357 | root->hit += period; | 368 | root->hit += period; |
358 | return 0; | 369 | return 0; |
359 | } | 370 | } |
360 | 371 | ||
361 | /* We match the node and still have a part remaining */ | 372 | /* We match the node and still have a part remaining */ |
362 | append_chain_children(root, chain, i, period); | 373 | append_chain_children(root, cursor, period); |
363 | 374 | ||
364 | return 0; | 375 | return 0; |
365 | } | 376 | } |
366 | 377 | ||
367 | static void filter_context(struct ip_callchain *old, struct resolved_chain *new, | 378 | int callchain_append(struct callchain_root *root, |
368 | struct map_symbol *syms) | 379 | struct callchain_cursor *cursor, |
369 | { | 380 | u64 period) |
370 | int i, j = 0; | ||
371 | |||
372 | for (i = 0; i < (int)old->nr; i++) { | ||
373 | if (old->ips[i] >= PERF_CONTEXT_MAX) | ||
374 | continue; | ||
375 | |||
376 | new->ips[j].ip = old->ips[i]; | ||
377 | new->ips[j].ms = syms[i]; | ||
378 | j++; | ||
379 | } | ||
380 | |||
381 | new->nr = j; | ||
382 | } | ||
383 | |||
384 | |||
385 | int callchain_append(struct callchain_root *root, struct ip_callchain *chain, | ||
386 | struct map_symbol *syms, u64 period) | ||
387 | { | 381 | { |
388 | struct resolved_chain *filtered; | 382 | if (!cursor->nr) |
389 | |||
390 | if (!chain->nr) | ||
391 | return 0; | 383 | return 0; |
392 | 384 | ||
393 | filtered = zalloc(sizeof(*filtered) + | 385 | callchain_cursor_commit(cursor); |
394 | chain->nr * sizeof(struct resolved_ip)); | ||
395 | if (!filtered) | ||
396 | return -ENOMEM; | ||
397 | |||
398 | filter_context(chain, filtered, syms); | ||
399 | |||
400 | if (!filtered->nr) | ||
401 | goto end; | ||
402 | 386 | ||
403 | append_chain_children(&root->node, filtered, 0, period); | 387 | append_chain_children(&root->node, cursor, period); |
404 | 388 | ||
405 | if (filtered->nr > root->max_depth) | 389 | if (cursor->nr > root->max_depth) |
406 | root->max_depth = filtered->nr; | 390 | root->max_depth = cursor->nr; |
407 | end: | ||
408 | free(filtered); | ||
409 | 391 | ||
410 | return 0; | 392 | return 0; |
411 | } | 393 | } |
412 | 394 | ||
413 | static int | 395 | static int |
414 | merge_chain_branch(struct callchain_node *dst, struct callchain_node *src, | 396 | merge_chain_branch(struct callchain_cursor *cursor, |
415 | struct resolved_chain *chain) | 397 | struct callchain_node *dst, struct callchain_node *src) |
416 | { | 398 | { |
399 | struct callchain_cursor_node **old_last = cursor->last; | ||
417 | struct callchain_node *child, *next_child; | 400 | struct callchain_node *child, *next_child; |
418 | struct callchain_list *list, *next_list; | 401 | struct callchain_list *list, *next_list; |
419 | int old_pos = chain->nr; | 402 | int old_pos = cursor->nr; |
420 | int err = 0; | 403 | int err = 0; |
421 | 404 | ||
422 | list_for_each_entry_safe(list, next_list, &src->val, list) { | 405 | list_for_each_entry_safe(list, next_list, &src->val, list) { |
423 | chain->ips[chain->nr].ip = list->ip; | 406 | callchain_cursor_append(cursor, list->ip, |
424 | chain->ips[chain->nr].ms = list->ms; | 407 | list->ms.map, list->ms.sym); |
425 | chain->nr++; | ||
426 | list_del(&list->list); | 408 | list_del(&list->list); |
427 | free(list); | 409 | free(list); |
428 | } | 410 | } |
429 | 411 | ||
430 | if (src->hit) | 412 | if (src->hit) { |
431 | append_chain_children(dst, chain, 0, src->hit); | 413 | callchain_cursor_commit(cursor); |
414 | append_chain_children(dst, cursor, src->hit); | ||
415 | } | ||
432 | 416 | ||
433 | chain_for_each_child_safe(child, next_child, src) { | 417 | chain_for_each_child_safe(child, next_child, src) { |
434 | err = merge_chain_branch(dst, child, chain); | 418 | err = merge_chain_branch(cursor, dst, child); |
435 | if (err) | 419 | if (err) |
436 | break; | 420 | break; |
437 | 421 | ||
438 | list_del(&child->brothers); | 422 | list_del(&child->siblings); |
439 | free(child); | 423 | free(child); |
440 | } | 424 | } |
441 | 425 | ||
442 | chain->nr = old_pos; | 426 | cursor->nr = old_pos; |
427 | cursor->last = old_last; | ||
443 | 428 | ||
444 | return err; | 429 | return err; |
445 | } | 430 | } |
446 | 431 | ||
447 | int callchain_merge(struct callchain_root *dst, struct callchain_root *src) | 432 | int callchain_merge(struct callchain_cursor *cursor, |
433 | struct callchain_root *dst, struct callchain_root *src) | ||
434 | { | ||
435 | return merge_chain_branch(cursor, &dst->node, &src->node); | ||
436 | } | ||
437 | |||
438 | int callchain_cursor_append(struct callchain_cursor *cursor, | ||
439 | u64 ip, struct map *map, struct symbol *sym) | ||
448 | { | 440 | { |
449 | struct resolved_chain *chain; | 441 | struct callchain_cursor_node *node = *cursor->last; |
450 | int err; | ||
451 | 442 | ||
452 | chain = malloc(sizeof(*chain) + | 443 | if (!node) { |
453 | src->max_depth * sizeof(struct resolved_ip)); | 444 | node = calloc(sizeof(*node), 1); |
454 | if (!chain) | 445 | if (!node) |
455 | return -ENOMEM; | 446 | return -ENOMEM; |
456 | 447 | ||
457 | chain->nr = 0; | 448 | *cursor->last = node; |
449 | } | ||
458 | 450 | ||
459 | err = merge_chain_branch(&dst->node, &src->node, chain); | 451 | node->ip = ip; |
452 | node->map = map; | ||
453 | node->sym = sym; | ||
460 | 454 | ||
461 | free(chain); | 455 | cursor->nr++; |
462 | 456 | ||
463 | return err; | 457 | cursor->last = &node->next; |
458 | |||
459 | return 0; | ||
464 | } | 460 | } |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index c15fb8c24ad2..67137256a1cd 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -16,7 +16,7 @@ enum chain_mode { | |||
16 | 16 | ||
17 | struct callchain_node { | 17 | struct callchain_node { |
18 | struct callchain_node *parent; | 18 | struct callchain_node *parent; |
19 | struct list_head brothers; | 19 | struct list_head siblings; |
20 | struct list_head children; | 20 | struct list_head children; |
21 | struct list_head val; | 21 | struct list_head val; |
22 | struct rb_node rb_node; /* to sort nodes in an rbtree */ | 22 | struct rb_node rb_node; /* to sort nodes in an rbtree */ |
@@ -49,9 +49,30 @@ struct callchain_list { | |||
49 | struct list_head list; | 49 | struct list_head list; |
50 | }; | 50 | }; |
51 | 51 | ||
52 | /* | ||
53 | * A callchain cursor is a single linked list that | ||
54 | * let one feed a callchain progressively. | ||
55 | * It keeps persitent allocated entries to minimize | ||
56 | * allocations. | ||
57 | */ | ||
58 | struct callchain_cursor_node { | ||
59 | u64 ip; | ||
60 | struct map *map; | ||
61 | struct symbol *sym; | ||
62 | struct callchain_cursor_node *next; | ||
63 | }; | ||
64 | |||
65 | struct callchain_cursor { | ||
66 | u64 nr; | ||
67 | struct callchain_cursor_node *first; | ||
68 | struct callchain_cursor_node **last; | ||
69 | u64 pos; | ||
70 | struct callchain_cursor_node *curr; | ||
71 | }; | ||
72 | |||
52 | static inline void callchain_init(struct callchain_root *root) | 73 | static inline void callchain_init(struct callchain_root *root) |
53 | { | 74 | { |
54 | INIT_LIST_HEAD(&root->node.brothers); | 75 | INIT_LIST_HEAD(&root->node.siblings); |
55 | INIT_LIST_HEAD(&root->node.children); | 76 | INIT_LIST_HEAD(&root->node.children); |
56 | INIT_LIST_HEAD(&root->node.val); | 77 | INIT_LIST_HEAD(&root->node.val); |
57 | 78 | ||
@@ -61,15 +82,54 @@ static inline void callchain_init(struct callchain_root *root) | |||
61 | root->max_depth = 0; | 82 | root->max_depth = 0; |
62 | } | 83 | } |
63 | 84 | ||
64 | static inline u64 cumul_hits(struct callchain_node *node) | 85 | static inline u64 callchain_cumul_hits(struct callchain_node *node) |
65 | { | 86 | { |
66 | return node->hit + node->children_hit; | 87 | return node->hit + node->children_hit; |
67 | } | 88 | } |
68 | 89 | ||
69 | int register_callchain_param(struct callchain_param *param); | 90 | int callchain_register_param(struct callchain_param *param); |
70 | int callchain_append(struct callchain_root *root, struct ip_callchain *chain, | 91 | int callchain_append(struct callchain_root *root, |
71 | struct map_symbol *syms, u64 period); | 92 | struct callchain_cursor *cursor, |
72 | int callchain_merge(struct callchain_root *dst, struct callchain_root *src); | 93 | u64 period); |
94 | |||
95 | int callchain_merge(struct callchain_cursor *cursor, | ||
96 | struct callchain_root *dst, struct callchain_root *src); | ||
73 | 97 | ||
74 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); | 98 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); |
99 | |||
100 | /* | ||
101 | * Initialize a cursor before adding entries inside, but keep | ||
102 | * the previously allocated entries as a cache. | ||
103 | */ | ||
104 | static inline void callchain_cursor_reset(struct callchain_cursor *cursor) | ||
105 | { | ||
106 | cursor->nr = 0; | ||
107 | cursor->last = &cursor->first; | ||
108 | } | ||
109 | |||
110 | int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip, | ||
111 | struct map *map, struct symbol *sym); | ||
112 | |||
113 | /* Close a cursor writing session. Initialize for the reader */ | ||
114 | static inline void callchain_cursor_commit(struct callchain_cursor *cursor) | ||
115 | { | ||
116 | cursor->curr = cursor->first; | ||
117 | cursor->pos = 0; | ||
118 | } | ||
119 | |||
120 | /* Cursor reading iteration helpers */ | ||
121 | static inline struct callchain_cursor_node * | ||
122 | callchain_cursor_current(struct callchain_cursor *cursor) | ||
123 | { | ||
124 | if (cursor->pos == cursor->nr) | ||
125 | return NULL; | ||
126 | |||
127 | return cursor->curr; | ||
128 | } | ||
129 | |||
130 | static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | ||
131 | { | ||
132 | cursor->curr = cursor->curr->next; | ||
133 | cursor->pos++; | ||
134 | } | ||
75 | #endif /* __PERF_CALLCHAIN_H */ | 135 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 3ccaa1043383..6893eec693ab 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -177,3 +177,8 @@ struct cpu_map *cpu_map__dummy_new(void) | |||
177 | 177 | ||
178 | return cpus; | 178 | return cpus; |
179 | } | 179 | } |
180 | |||
181 | void cpu_map__delete(struct cpu_map *map) | ||
182 | { | ||
183 | free(map); | ||
184 | } | ||
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index f7a4f42f6307..072c0a374794 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -8,6 +8,6 @@ struct cpu_map { | |||
8 | 8 | ||
9 | struct cpu_map *cpu_map__new(const char *cpu_list); | 9 | struct cpu_map *cpu_map__new(const char *cpu_list); |
10 | struct cpu_map *cpu_map__dummy_new(void); | 10 | struct cpu_map *cpu_map__dummy_new(void); |
11 | void *cpu_map__delete(struct cpu_map *map); | 11 | void cpu_map__delete(struct cpu_map *map); |
12 | 12 | ||
13 | #endif /* __PERF_CPUMAP_H */ | 13 | #endif /* __PERF_CPUMAP_H */ |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1478ab4ee222..e4db8b888546 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -826,128 +826,3 @@ out_filtered: | |||
826 | al->filtered = true; | 826 | al->filtered = true; |
827 | return 0; | 827 | return 0; |
828 | } | 828 | } |
829 | |||
830 | static int event__parse_id_sample(const event_t *event, | ||
831 | struct perf_session *session, | ||
832 | struct sample_data *sample) | ||
833 | { | ||
834 | const u64 *array; | ||
835 | u64 type; | ||
836 | |||
837 | sample->cpu = sample->pid = sample->tid = -1; | ||
838 | sample->stream_id = sample->id = sample->time = -1ULL; | ||
839 | |||
840 | if (!session->sample_id_all) | ||
841 | return 0; | ||
842 | |||
843 | array = event->sample.array; | ||
844 | array += ((event->header.size - | ||
845 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
846 | type = session->sample_type; | ||
847 | |||
848 | if (type & PERF_SAMPLE_CPU) { | ||
849 | u32 *p = (u32 *)array; | ||
850 | sample->cpu = *p; | ||
851 | array--; | ||
852 | } | ||
853 | |||
854 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
855 | sample->stream_id = *array; | ||
856 | array--; | ||
857 | } | ||
858 | |||
859 | if (type & PERF_SAMPLE_ID) { | ||
860 | sample->id = *array; | ||
861 | array--; | ||
862 | } | ||
863 | |||
864 | if (type & PERF_SAMPLE_TIME) { | ||
865 | sample->time = *array; | ||
866 | array--; | ||
867 | } | ||
868 | |||
869 | if (type & PERF_SAMPLE_TID) { | ||
870 | u32 *p = (u32 *)array; | ||
871 | sample->pid = p[0]; | ||
872 | sample->tid = p[1]; | ||
873 | } | ||
874 | |||
875 | return 0; | ||
876 | } | ||
877 | |||
878 | int event__parse_sample(const event_t *event, struct perf_session *session, | ||
879 | struct sample_data *data) | ||
880 | { | ||
881 | const u64 *array; | ||
882 | u64 type; | ||
883 | |||
884 | if (event->header.type != PERF_RECORD_SAMPLE) | ||
885 | return event__parse_id_sample(event, session, data); | ||
886 | |||
887 | array = event->sample.array; | ||
888 | type = session->sample_type; | ||
889 | |||
890 | if (type & PERF_SAMPLE_IP) { | ||
891 | data->ip = event->ip.ip; | ||
892 | array++; | ||
893 | } | ||
894 | |||
895 | if (type & PERF_SAMPLE_TID) { | ||
896 | u32 *p = (u32 *)array; | ||
897 | data->pid = p[0]; | ||
898 | data->tid = p[1]; | ||
899 | array++; | ||
900 | } | ||
901 | |||
902 | if (type & PERF_SAMPLE_TIME) { | ||
903 | data->time = *array; | ||
904 | array++; | ||
905 | } | ||
906 | |||
907 | if (type & PERF_SAMPLE_ADDR) { | ||
908 | data->addr = *array; | ||
909 | array++; | ||
910 | } | ||
911 | |||
912 | data->id = -1ULL; | ||
913 | if (type & PERF_SAMPLE_ID) { | ||
914 | data->id = *array; | ||
915 | array++; | ||
916 | } | ||
917 | |||
918 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
919 | data->stream_id = *array; | ||
920 | array++; | ||
921 | } | ||
922 | |||
923 | if (type & PERF_SAMPLE_CPU) { | ||
924 | u32 *p = (u32 *)array; | ||
925 | data->cpu = *p; | ||
926 | array++; | ||
927 | } else | ||
928 | data->cpu = -1; | ||
929 | |||
930 | if (type & PERF_SAMPLE_PERIOD) { | ||
931 | data->period = *array; | ||
932 | array++; | ||
933 | } | ||
934 | |||
935 | if (type & PERF_SAMPLE_READ) { | ||
936 | pr_debug("PERF_SAMPLE_READ is unsuported for now\n"); | ||
937 | return -1; | ||
938 | } | ||
939 | |||
940 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
941 | data->callchain = (struct ip_callchain *)array; | ||
942 | array += 1 + data->callchain->nr; | ||
943 | } | ||
944 | |||
945 | if (type & PERF_SAMPLE_RAW) { | ||
946 | u32 *p = (u32 *)array; | ||
947 | data->raw_size = *p; | ||
948 | p++; | ||
949 | data->raw_data = p; | ||
950 | } | ||
951 | |||
952 | return 0; | ||
953 | } | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 2b7e91902f10..d79e4edd82f9 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -169,9 +169,10 @@ struct addr_location; | |||
169 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 169 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
170 | struct addr_location *al, struct sample_data *data, | 170 | struct addr_location *al, struct sample_data *data, |
171 | symbol_filter_t filter); | 171 | symbol_filter_t filter); |
172 | int event__parse_sample(const event_t *event, struct perf_session *session, | ||
173 | struct sample_data *sample); | ||
174 | 172 | ||
175 | const char *event__get_event_name(unsigned int id); | 173 | const char *event__get_event_name(unsigned int id); |
176 | 174 | ||
175 | int event__parse_sample(const event_t *event, u64 type, bool sample_id_all, | ||
176 | struct sample_data *sample); | ||
177 | |||
177 | #endif /* __PERF_RECORD_H */ | 178 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c new file mode 100644 index 000000000000..df0610e9c61b --- /dev/null +++ b/tools/perf/util/evlist.c | |||
@@ -0,0 +1,170 @@ | |||
1 | #include <poll.h> | ||
2 | #include "evlist.h" | ||
3 | #include "evsel.h" | ||
4 | #include "util.h" | ||
5 | |||
6 | #include <linux/bitops.h> | ||
7 | #include <linux/hash.h> | ||
8 | |||
9 | void perf_evlist__init(struct perf_evlist *evlist) | ||
10 | { | ||
11 | int i; | ||
12 | |||
13 | for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) | ||
14 | INIT_HLIST_HEAD(&evlist->heads[i]); | ||
15 | INIT_LIST_HEAD(&evlist->entries); | ||
16 | } | ||
17 | |||
18 | struct perf_evlist *perf_evlist__new(void) | ||
19 | { | ||
20 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); | ||
21 | |||
22 | if (evlist != NULL) | ||
23 | perf_evlist__init(evlist); | ||
24 | |||
25 | return evlist; | ||
26 | } | ||
27 | |||
28 | static void perf_evlist__purge(struct perf_evlist *evlist) | ||
29 | { | ||
30 | struct perf_evsel *pos, *n; | ||
31 | |||
32 | list_for_each_entry_safe(pos, n, &evlist->entries, node) { | ||
33 | list_del_init(&pos->node); | ||
34 | perf_evsel__delete(pos); | ||
35 | } | ||
36 | |||
37 | evlist->nr_entries = 0; | ||
38 | } | ||
39 | |||
40 | void perf_evlist__exit(struct perf_evlist *evlist) | ||
41 | { | ||
42 | free(evlist->mmap); | ||
43 | free(evlist->pollfd); | ||
44 | evlist->mmap = NULL; | ||
45 | evlist->pollfd = NULL; | ||
46 | } | ||
47 | |||
48 | void perf_evlist__delete(struct perf_evlist *evlist) | ||
49 | { | ||
50 | perf_evlist__purge(evlist); | ||
51 | perf_evlist__exit(evlist); | ||
52 | free(evlist); | ||
53 | } | ||
54 | |||
55 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | ||
56 | { | ||
57 | list_add_tail(&entry->node, &evlist->entries); | ||
58 | ++evlist->nr_entries; | ||
59 | } | ||
60 | |||
61 | int perf_evlist__add_default(struct perf_evlist *evlist) | ||
62 | { | ||
63 | struct perf_event_attr attr = { | ||
64 | .type = PERF_TYPE_HARDWARE, | ||
65 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
66 | }; | ||
67 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); | ||
68 | |||
69 | if (evsel == NULL) | ||
70 | return -ENOMEM; | ||
71 | |||
72 | perf_evlist__add(evlist, evsel); | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist, int ncpus, int nthreads) | ||
77 | { | ||
78 | int nfds = ncpus * nthreads * evlist->nr_entries; | ||
79 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | ||
80 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | ||
81 | } | ||
82 | |||
83 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | ||
84 | { | ||
85 | fcntl(fd, F_SETFL, O_NONBLOCK); | ||
86 | evlist->pollfd[evlist->nr_fds].fd = fd; | ||
87 | evlist->pollfd[evlist->nr_fds].events = POLLIN; | ||
88 | evlist->nr_fds++; | ||
89 | } | ||
90 | |||
91 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | ||
92 | { | ||
93 | struct hlist_head *head; | ||
94 | struct hlist_node *pos; | ||
95 | struct perf_sample_id *sid; | ||
96 | int hash; | ||
97 | |||
98 | if (evlist->nr_entries == 1) | ||
99 | return list_entry(evlist->entries.next, struct perf_evsel, node); | ||
100 | |||
101 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | ||
102 | head = &evlist->heads[hash]; | ||
103 | |||
104 | hlist_for_each_entry(sid, pos, head, node) | ||
105 | if (sid->id == id) | ||
106 | return sid->evsel; | ||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | event_t *perf_evlist__read_on_cpu(struct perf_evlist *evlist, int cpu) | ||
111 | { | ||
112 | /* XXX Move this to perf.c, making it generally available */ | ||
113 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
114 | struct perf_mmap *md = &evlist->mmap[cpu]; | ||
115 | unsigned int head = perf_mmap__read_head(md); | ||
116 | unsigned int old = md->prev; | ||
117 | unsigned char *data = md->base + page_size; | ||
118 | event_t *event = NULL; | ||
119 | int diff; | ||
120 | |||
121 | /* | ||
122 | * If we're further behind than half the buffer, there's a chance | ||
123 | * the writer will bite our tail and mess up the samples under us. | ||
124 | * | ||
125 | * If we somehow ended up ahead of the head, we got messed up. | ||
126 | * | ||
127 | * In either case, truncate and restart at head. | ||
128 | */ | ||
129 | diff = head - old; | ||
130 | if (diff > md->mask / 2 || diff < 0) { | ||
131 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); | ||
132 | |||
133 | /* | ||
134 | * head points to a known good entry, start there. | ||
135 | */ | ||
136 | old = head; | ||
137 | } | ||
138 | |||
139 | if (old != head) { | ||
140 | size_t size; | ||
141 | |||
142 | event = (event_t *)&data[old & md->mask]; | ||
143 | size = event->header.size; | ||
144 | |||
145 | /* | ||
146 | * Event straddles the mmap boundary -- header should always | ||
147 | * be inside due to u64 alignment of output. | ||
148 | */ | ||
149 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | ||
150 | unsigned int offset = old; | ||
151 | unsigned int len = min(sizeof(*event), size), cpy; | ||
152 | void *dst = &evlist->event_copy; | ||
153 | |||
154 | do { | ||
155 | cpy = min(md->mask + 1 - (offset & md->mask), len); | ||
156 | memcpy(dst, &data[offset & md->mask], cpy); | ||
157 | offset += cpy; | ||
158 | dst += cpy; | ||
159 | len -= cpy; | ||
160 | } while (len); | ||
161 | |||
162 | event = &evlist->event_copy; | ||
163 | } | ||
164 | |||
165 | old += size; | ||
166 | } | ||
167 | |||
168 | md->prev = old; | ||
169 | return event; | ||
170 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h new file mode 100644 index 000000000000..acbe48eac608 --- /dev/null +++ b/tools/perf/util/evlist.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #ifndef __PERF_EVLIST_H | ||
2 | #define __PERF_EVLIST_H 1 | ||
3 | |||
4 | #include <linux/list.h> | ||
5 | #include "../perf.h" | ||
6 | #include "event.h" | ||
7 | |||
8 | struct pollfd; | ||
9 | |||
10 | #define PERF_EVLIST__HLIST_BITS 8 | ||
11 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) | ||
12 | |||
13 | struct perf_evlist { | ||
14 | struct list_head entries; | ||
15 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; | ||
16 | int nr_entries; | ||
17 | int nr_fds; | ||
18 | int mmap_len; | ||
19 | event_t event_copy; | ||
20 | struct perf_mmap *mmap; | ||
21 | struct pollfd *pollfd; | ||
22 | }; | ||
23 | |||
24 | struct perf_evsel; | ||
25 | |||
26 | struct perf_evlist *perf_evlist__new(void); | ||
27 | void perf_evlist__init(struct perf_evlist *evlist); | ||
28 | void perf_evlist__exit(struct perf_evlist *evlist); | ||
29 | void perf_evlist__delete(struct perf_evlist *evlist); | ||
30 | |||
31 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry); | ||
32 | int perf_evlist__add_default(struct perf_evlist *evlist); | ||
33 | |||
34 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist, int ncpus, int nthreads); | ||
35 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); | ||
36 | |||
37 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | ||
38 | |||
39 | event_t *perf_evlist__read_on_cpu(struct perf_evlist *self, int cpu); | ||
40 | |||
41 | #endif /* __PERF_EVLIST_H */ | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f5cfed60af98..76ab553637d6 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -1,20 +1,33 @@ | |||
1 | #include "evsel.h" | 1 | #include "evsel.h" |
2 | #include "evlist.h" | ||
2 | #include "../perf.h" | 3 | #include "../perf.h" |
3 | #include "util.h" | 4 | #include "util.h" |
4 | #include "cpumap.h" | 5 | #include "cpumap.h" |
5 | #include "thread.h" | 6 | #include "thread_map.h" |
7 | |||
8 | #include <unistd.h> | ||
9 | #include <sys/mman.h> | ||
10 | |||
11 | #include <linux/bitops.h> | ||
12 | #include <linux/hash.h> | ||
6 | 13 | ||
7 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 14 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
15 | #define SID(e, x, y) xyarray__entry(e->id, x, y) | ||
16 | |||
17 | void perf_evsel__init(struct perf_evsel *evsel, | ||
18 | struct perf_event_attr *attr, int idx) | ||
19 | { | ||
20 | evsel->idx = idx; | ||
21 | evsel->attr = *attr; | ||
22 | INIT_LIST_HEAD(&evsel->node); | ||
23 | } | ||
8 | 24 | ||
9 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 25 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
10 | { | 26 | { |
11 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 27 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); |
12 | 28 | ||
13 | if (evsel != NULL) { | 29 | if (evsel != NULL) |
14 | evsel->idx = idx; | 30 | perf_evsel__init(evsel, attr, idx); |
15 | evsel->attr = *attr; | ||
16 | INIT_LIST_HEAD(&evsel->node); | ||
17 | } | ||
18 | 31 | ||
19 | return evsel; | 32 | return evsel; |
20 | } | 33 | } |
@@ -25,6 +38,12 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
25 | return evsel->fd != NULL ? 0 : -ENOMEM; | 38 | return evsel->fd != NULL ? 0 : -ENOMEM; |
26 | } | 39 | } |
27 | 40 | ||
41 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
42 | { | ||
43 | evsel->id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | ||
44 | return evsel->id != NULL ? 0 : -ENOMEM; | ||
45 | } | ||
46 | |||
28 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | 47 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) |
29 | { | 48 | { |
30 | evsel->counts = zalloc((sizeof(*evsel->counts) + | 49 | evsel->counts = zalloc((sizeof(*evsel->counts) + |
@@ -38,6 +57,12 @@ void perf_evsel__free_fd(struct perf_evsel *evsel) | |||
38 | evsel->fd = NULL; | 57 | evsel->fd = NULL; |
39 | } | 58 | } |
40 | 59 | ||
60 | void perf_evsel__free_id(struct perf_evsel *evsel) | ||
61 | { | ||
62 | xyarray__delete(evsel->id); | ||
63 | evsel->id = NULL; | ||
64 | } | ||
65 | |||
41 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 66 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
42 | { | 67 | { |
43 | int cpu, thread; | 68 | int cpu, thread; |
@@ -49,10 +74,34 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
49 | } | 74 | } |
50 | } | 75 | } |
51 | 76 | ||
52 | void perf_evsel__delete(struct perf_evsel *evsel) | 77 | void perf_evlist__munmap(struct perf_evlist *evlist, int ncpus) |
78 | { | ||
79 | int cpu; | ||
80 | |||
81 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
82 | if (evlist->mmap[cpu].base != NULL) { | ||
83 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
84 | evlist->mmap[cpu].base = NULL; | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus) | ||
90 | { | ||
91 | evlist->mmap = zalloc(ncpus * sizeof(struct perf_mmap)); | ||
92 | return evlist->mmap != NULL ? 0 : -ENOMEM; | ||
93 | } | ||
94 | |||
95 | void perf_evsel__exit(struct perf_evsel *evsel) | ||
53 | { | 96 | { |
54 | assert(list_empty(&evsel->node)); | 97 | assert(list_empty(&evsel->node)); |
55 | xyarray__delete(evsel->fd); | 98 | xyarray__delete(evsel->fd); |
99 | xyarray__delete(evsel->id); | ||
100 | } | ||
101 | |||
102 | void perf_evsel__delete(struct perf_evsel *evsel) | ||
103 | { | ||
104 | perf_evsel__exit(evsel); | ||
56 | free(evsel); | 105 | free(evsel); |
57 | } | 106 | } |
58 | 107 | ||
@@ -128,7 +177,7 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
128 | } | 177 | } |
129 | 178 | ||
130 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 179 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
131 | struct thread_map *threads) | 180 | struct thread_map *threads, bool group, bool inherit) |
132 | { | 181 | { |
133 | int cpu, thread; | 182 | int cpu, thread; |
134 | 183 | ||
@@ -137,12 +186,20 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
137 | return -1; | 186 | return -1; |
138 | 187 | ||
139 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 188 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
189 | int group_fd = -1; | ||
190 | |||
191 | evsel->attr.inherit = (cpus->map[cpu] < 0) && inherit; | ||
192 | |||
140 | for (thread = 0; thread < threads->nr; thread++) { | 193 | for (thread = 0; thread < threads->nr; thread++) { |
141 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | 194 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, |
142 | threads->map[thread], | 195 | threads->map[thread], |
143 | cpus->map[cpu], -1, 0); | 196 | cpus->map[cpu], |
197 | group_fd, 0); | ||
144 | if (FD(evsel, cpu, thread) < 0) | 198 | if (FD(evsel, cpu, thread) < 0) |
145 | goto out_close; | 199 | goto out_close; |
200 | |||
201 | if (group && group_fd == -1) | ||
202 | group_fd = FD(evsel, cpu, thread); | ||
146 | } | 203 | } |
147 | } | 204 | } |
148 | 205 | ||
@@ -175,10 +232,9 @@ static struct { | |||
175 | .threads = { -1, }, | 232 | .threads = { -1, }, |
176 | }; | 233 | }; |
177 | 234 | ||
178 | int perf_evsel__open(struct perf_evsel *evsel, | 235 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
179 | struct cpu_map *cpus, struct thread_map *threads) | 236 | struct thread_map *threads, bool group, bool inherit) |
180 | { | 237 | { |
181 | |||
182 | if (cpus == NULL) { | 238 | if (cpus == NULL) { |
183 | /* Work around old compiler warnings about strict aliasing */ | 239 | /* Work around old compiler warnings about strict aliasing */ |
184 | cpus = &empty_cpu_map.map; | 240 | cpus = &empty_cpu_map.map; |
@@ -187,15 +243,243 @@ int perf_evsel__open(struct perf_evsel *evsel, | |||
187 | if (threads == NULL) | 243 | if (threads == NULL) |
188 | threads = &empty_thread_map.map; | 244 | threads = &empty_thread_map.map; |
189 | 245 | ||
190 | return __perf_evsel__open(evsel, cpus, threads); | 246 | return __perf_evsel__open(evsel, cpus, threads, group, inherit); |
247 | } | ||
248 | |||
249 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | ||
250 | struct cpu_map *cpus, bool group, bool inherit) | ||
251 | { | ||
252 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit); | ||
253 | } | ||
254 | |||
255 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | ||
256 | struct thread_map *threads, bool group, bool inherit) | ||
257 | { | ||
258 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit); | ||
259 | } | ||
260 | |||
261 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int cpu, int prot, | ||
262 | int mask, int fd) | ||
263 | { | ||
264 | evlist->mmap[cpu].prev = 0; | ||
265 | evlist->mmap[cpu].mask = mask; | ||
266 | evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot, | ||
267 | MAP_SHARED, fd, 0); | ||
268 | if (evlist->mmap[cpu].base == MAP_FAILED) | ||
269 | return -1; | ||
270 | |||
271 | perf_evlist__add_pollfd(evlist, fd); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int perf_evlist__id_hash(struct perf_evlist *evlist, struct perf_evsel *evsel, | ||
276 | int cpu, int thread, int fd) | ||
277 | { | ||
278 | struct perf_sample_id *sid; | ||
279 | u64 read_data[4] = { 0, }; | ||
280 | int hash, id_idx = 1; /* The first entry is the counter value */ | ||
281 | |||
282 | if (!(evsel->attr.read_format & PERF_FORMAT_ID) || | ||
283 | read(fd, &read_data, sizeof(read_data)) == -1) | ||
284 | return -1; | ||
285 | |||
286 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
287 | ++id_idx; | ||
288 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
289 | ++id_idx; | ||
290 | |||
291 | sid = SID(evsel, cpu, thread); | ||
292 | sid->id = read_data[id_idx]; | ||
293 | sid->evsel = evsel; | ||
294 | hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); | ||
295 | hlist_add_head(&sid->node, &evlist->heads[hash]); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | /** perf_evlist__mmap - Create per cpu maps to receive events | ||
300 | * | ||
301 | * @evlist - list of events | ||
302 | * @cpus - cpu map being monitored | ||
303 | * @threads - threads map being monitored | ||
304 | * @pages - map length in pages | ||
305 | * @overwrite - overwrite older events? | ||
306 | * | ||
307 | * If overwrite is false the user needs to signal event consuption using: | ||
308 | * | ||
309 | * struct perf_mmap *m = &evlist->mmap[cpu]; | ||
310 | * unsigned int head = perf_mmap__read_head(m); | ||
311 | * | ||
312 | * perf_mmap__write_tail(m, head) | ||
313 | */ | ||
314 | int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus, | ||
315 | struct thread_map *threads, int pages, bool overwrite) | ||
316 | { | ||
317 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
318 | int mask = pages * page_size - 1, cpu; | ||
319 | struct perf_evsel *first_evsel, *evsel; | ||
320 | int thread, prot = PROT_READ | (overwrite ? 0 : PROT_WRITE); | ||
321 | |||
322 | if (evlist->mmap == NULL && | ||
323 | perf_evlist__alloc_mmap(evlist, cpus->nr) < 0) | ||
324 | return -ENOMEM; | ||
325 | |||
326 | if (evlist->pollfd == NULL && | ||
327 | perf_evlist__alloc_pollfd(evlist, cpus->nr, threads->nr) < 0) | ||
328 | return -ENOMEM; | ||
329 | |||
330 | evlist->mmap_len = (pages + 1) * page_size; | ||
331 | first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
332 | |||
333 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
334 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
335 | evsel->id == NULL && | ||
336 | perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) | ||
337 | return -ENOMEM; | ||
338 | |||
339 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
340 | for (thread = 0; thread < threads->nr; thread++) { | ||
341 | int fd = FD(evsel, cpu, thread); | ||
342 | |||
343 | if (evsel->idx || thread) { | ||
344 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, | ||
345 | FD(first_evsel, cpu, 0)) != 0) | ||
346 | goto out_unmap; | ||
347 | } else if (__perf_evlist__mmap(evlist, cpu, prot, mask, fd) < 0) | ||
348 | goto out_unmap; | ||
349 | |||
350 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
351 | perf_evlist__id_hash(evlist, evsel, cpu, thread, fd) < 0) | ||
352 | goto out_unmap; | ||
353 | } | ||
354 | } | ||
355 | } | ||
356 | |||
357 | return 0; | ||
358 | |||
359 | out_unmap: | ||
360 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
361 | if (evlist->mmap[cpu].base != NULL) { | ||
362 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
363 | evlist->mmap[cpu].base = NULL; | ||
364 | } | ||
365 | } | ||
366 | return -1; | ||
191 | } | 367 | } |
192 | 368 | ||
193 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) | 369 | static int event__parse_id_sample(const event_t *event, u64 type, |
370 | struct sample_data *sample) | ||
194 | { | 371 | { |
195 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); | 372 | const u64 *array = event->sample.array; |
373 | |||
374 | array += ((event->header.size - | ||
375 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
376 | |||
377 | if (type & PERF_SAMPLE_CPU) { | ||
378 | u32 *p = (u32 *)array; | ||
379 | sample->cpu = *p; | ||
380 | array--; | ||
381 | } | ||
382 | |||
383 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
384 | sample->stream_id = *array; | ||
385 | array--; | ||
386 | } | ||
387 | |||
388 | if (type & PERF_SAMPLE_ID) { | ||
389 | sample->id = *array; | ||
390 | array--; | ||
391 | } | ||
392 | |||
393 | if (type & PERF_SAMPLE_TIME) { | ||
394 | sample->time = *array; | ||
395 | array--; | ||
396 | } | ||
397 | |||
398 | if (type & PERF_SAMPLE_TID) { | ||
399 | u32 *p = (u32 *)array; | ||
400 | sample->pid = p[0]; | ||
401 | sample->tid = p[1]; | ||
402 | } | ||
403 | |||
404 | return 0; | ||
196 | } | 405 | } |
197 | 406 | ||
198 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) | 407 | int event__parse_sample(const event_t *event, u64 type, bool sample_id_all, |
408 | struct sample_data *data) | ||
199 | { | 409 | { |
200 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); | 410 | const u64 *array; |
411 | |||
412 | data->cpu = data->pid = data->tid = -1; | ||
413 | data->stream_id = data->id = data->time = -1ULL; | ||
414 | |||
415 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
416 | if (!sample_id_all) | ||
417 | return 0; | ||
418 | return event__parse_id_sample(event, type, data); | ||
419 | } | ||
420 | |||
421 | array = event->sample.array; | ||
422 | |||
423 | if (type & PERF_SAMPLE_IP) { | ||
424 | data->ip = event->ip.ip; | ||
425 | array++; | ||
426 | } | ||
427 | |||
428 | if (type & PERF_SAMPLE_TID) { | ||
429 | u32 *p = (u32 *)array; | ||
430 | data->pid = p[0]; | ||
431 | data->tid = p[1]; | ||
432 | array++; | ||
433 | } | ||
434 | |||
435 | if (type & PERF_SAMPLE_TIME) { | ||
436 | data->time = *array; | ||
437 | array++; | ||
438 | } | ||
439 | |||
440 | if (type & PERF_SAMPLE_ADDR) { | ||
441 | data->addr = *array; | ||
442 | array++; | ||
443 | } | ||
444 | |||
445 | data->id = -1ULL; | ||
446 | if (type & PERF_SAMPLE_ID) { | ||
447 | data->id = *array; | ||
448 | array++; | ||
449 | } | ||
450 | |||
451 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
452 | data->stream_id = *array; | ||
453 | array++; | ||
454 | } | ||
455 | |||
456 | if (type & PERF_SAMPLE_CPU) { | ||
457 | u32 *p = (u32 *)array; | ||
458 | data->cpu = *p; | ||
459 | array++; | ||
460 | } | ||
461 | |||
462 | if (type & PERF_SAMPLE_PERIOD) { | ||
463 | data->period = *array; | ||
464 | array++; | ||
465 | } | ||
466 | |||
467 | if (type & PERF_SAMPLE_READ) { | ||
468 | fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); | ||
469 | return -1; | ||
470 | } | ||
471 | |||
472 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
473 | data->callchain = (struct ip_callchain *)array; | ||
474 | array += 1 + data->callchain->nr; | ||
475 | } | ||
476 | |||
477 | if (type & PERF_SAMPLE_RAW) { | ||
478 | u32 *p = (u32 *)array; | ||
479 | data->raw_size = *p; | ||
480 | p++; | ||
481 | data->raw_data = p; | ||
482 | } | ||
483 | |||
484 | return 0; | ||
201 | } | 485 | } |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b2d755fe88a5..7962e7587dea 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -24,11 +24,24 @@ struct perf_counts { | |||
24 | struct perf_counts_values cpu[]; | 24 | struct perf_counts_values cpu[]; |
25 | }; | 25 | }; |
26 | 26 | ||
27 | struct perf_evsel; | ||
28 | |||
29 | /* | ||
30 | * Per fd, to map back from PERF_SAMPLE_ID to evsel, only used when there are | ||
31 | * more than one entry in the evlist. | ||
32 | */ | ||
33 | struct perf_sample_id { | ||
34 | struct hlist_node node; | ||
35 | u64 id; | ||
36 | struct perf_evsel *evsel; | ||
37 | }; | ||
38 | |||
27 | struct perf_evsel { | 39 | struct perf_evsel { |
28 | struct list_head node; | 40 | struct list_head node; |
29 | struct perf_event_attr attr; | 41 | struct perf_event_attr attr; |
30 | char *filter; | 42 | char *filter; |
31 | struct xyarray *fd; | 43 | struct xyarray *fd; |
44 | struct xyarray *id; | ||
32 | struct perf_counts *counts; | 45 | struct perf_counts *counts; |
33 | int idx; | 46 | int idx; |
34 | void *priv; | 47 | void *priv; |
@@ -36,19 +49,31 @@ struct perf_evsel { | |||
36 | 49 | ||
37 | struct cpu_map; | 50 | struct cpu_map; |
38 | struct thread_map; | 51 | struct thread_map; |
52 | struct perf_evlist; | ||
39 | 53 | ||
40 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); | 54 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); |
55 | void perf_evsel__init(struct perf_evsel *evsel, | ||
56 | struct perf_event_attr *attr, int idx); | ||
57 | void perf_evsel__exit(struct perf_evsel *evsel); | ||
41 | void perf_evsel__delete(struct perf_evsel *evsel); | 58 | void perf_evsel__delete(struct perf_evsel *evsel); |
42 | 59 | ||
43 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 60 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
61 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | ||
44 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); | 62 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); |
63 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus); | ||
45 | void perf_evsel__free_fd(struct perf_evsel *evsel); | 64 | void perf_evsel__free_fd(struct perf_evsel *evsel); |
65 | void perf_evsel__free_id(struct perf_evsel *evsel); | ||
46 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 66 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
47 | 67 | ||
48 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); | 68 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
49 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads); | 69 | struct cpu_map *cpus, bool group, bool inherit); |
50 | int perf_evsel__open(struct perf_evsel *evsel, | 70 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, |
51 | struct cpu_map *cpus, struct thread_map *threads); | 71 | struct thread_map *threads, bool group, bool inherit); |
72 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | ||
73 | struct thread_map *threads, bool group, bool inherit); | ||
74 | int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus, | ||
75 | struct thread_map *threads, int pages, bool overwrite); | ||
76 | void perf_evlist__munmap(struct perf_evlist *evlist, int ncpus); | ||
52 | 77 | ||
53 | #define perf_evsel__match(evsel, t, c) \ | 78 | #define perf_evsel__match(evsel, t, c) \ |
54 | (evsel->attr.type == PERF_TYPE_##t && \ | 79 | (evsel->attr.type == PERF_TYPE_##t && \ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f6a929e74981..f0138d472339 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -8,6 +8,7 @@ | |||
8 | #include <linux/list.h> | 8 | #include <linux/list.h> |
9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
10 | 10 | ||
11 | #include "evlist.h" | ||
11 | #include "util.h" | 12 | #include "util.h" |
12 | #include "header.h" | 13 | #include "header.h" |
13 | #include "../perf.h" | 14 | #include "../perf.h" |
@@ -428,7 +429,8 @@ static bool perf_session__read_build_ids(struct perf_session *self, bool with_hi | |||
428 | return ret; | 429 | return ret; |
429 | } | 430 | } |
430 | 431 | ||
431 | static int perf_header__adds_write(struct perf_header *self, int fd) | 432 | static int perf_header__adds_write(struct perf_header *self, |
433 | struct perf_evlist *evlist, int fd) | ||
432 | { | 434 | { |
433 | int nr_sections; | 435 | int nr_sections; |
434 | struct perf_session *session; | 436 | struct perf_session *session; |
@@ -463,7 +465,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
463 | 465 | ||
464 | /* Write trace info */ | 466 | /* Write trace info */ |
465 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | 467 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); |
466 | read_tracing_data(fd, &evsel_list); | 468 | read_tracing_data(fd, &evlist->entries); |
467 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | 469 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; |
468 | } | 470 | } |
469 | 471 | ||
@@ -513,7 +515,8 @@ int perf_header__write_pipe(int fd) | |||
513 | return 0; | 515 | return 0; |
514 | } | 516 | } |
515 | 517 | ||
516 | int perf_header__write(struct perf_header *self, int fd, bool at_exit) | 518 | int perf_header__write(struct perf_header *self, struct perf_evlist *evlist, |
519 | int fd, bool at_exit) | ||
517 | { | 520 | { |
518 | struct perf_file_header f_header; | 521 | struct perf_file_header f_header; |
519 | struct perf_file_attr f_attr; | 522 | struct perf_file_attr f_attr; |
@@ -566,7 +569,7 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
566 | self->data_offset = lseek(fd, 0, SEEK_CUR); | 569 | self->data_offset = lseek(fd, 0, SEEK_CUR); |
567 | 570 | ||
568 | if (at_exit) { | 571 | if (at_exit) { |
569 | err = perf_header__adds_write(self, fd); | 572 | err = perf_header__adds_write(self, evlist, fd); |
570 | if (err < 0) | 573 | if (err < 0) |
571 | return err; | 574 | return err; |
572 | } | 575 | } |
@@ -1133,7 +1136,7 @@ int event__process_event_type(event_t *self, | |||
1133 | return 0; | 1136 | return 0; |
1134 | } | 1137 | } |
1135 | 1138 | ||
1136 | int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | 1139 | int event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, |
1137 | event__handler_t process, | 1140 | event__handler_t process, |
1138 | struct perf_session *session __unused) | 1141 | struct perf_session *session __unused) |
1139 | { | 1142 | { |
@@ -1144,7 +1147,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | |||
1144 | memset(&ev, 0, sizeof(ev)); | 1147 | memset(&ev, 0, sizeof(ev)); |
1145 | 1148 | ||
1146 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; | 1149 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; |
1147 | size = read_tracing_data_size(fd, pattrs); | 1150 | size = read_tracing_data_size(fd, &evlist->entries); |
1148 | if (size <= 0) | 1151 | if (size <= 0) |
1149 | return size; | 1152 | return size; |
1150 | aligned_size = ALIGN(size, sizeof(u64)); | 1153 | aligned_size = ALIGN(size, sizeof(u64)); |
@@ -1154,7 +1157,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | |||
1154 | 1157 | ||
1155 | process(&ev, NULL, session); | 1158 | process(&ev, NULL, session); |
1156 | 1159 | ||
1157 | err = read_tracing_data(fd, pattrs); | 1160 | err = read_tracing_data(fd, &evlist->entries); |
1158 | write_padded(fd, NULL, 0, padding); | 1161 | write_padded(fd, NULL, 0, padding); |
1159 | 1162 | ||
1160 | return aligned_size; | 1163 | return aligned_size; |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 33f16be7b72f..65afd7f74e0d 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -65,8 +65,11 @@ struct perf_header { | |||
65 | int perf_header__init(struct perf_header *self); | 65 | int perf_header__init(struct perf_header *self); |
66 | void perf_header__exit(struct perf_header *self); | 66 | void perf_header__exit(struct perf_header *self); |
67 | 67 | ||
68 | struct perf_evlist; | ||
69 | |||
68 | int perf_header__read(struct perf_session *session, int fd); | 70 | int perf_header__read(struct perf_session *session, int fd); |
69 | int perf_header__write(struct perf_header *self, int fd, bool at_exit); | 71 | int perf_header__write(struct perf_header *self, struct perf_evlist *evlist, |
72 | int fd, bool at_exit); | ||
70 | int perf_header__write_pipe(int fd); | 73 | int perf_header__write_pipe(int fd); |
71 | 74 | ||
72 | int perf_header__add_attr(struct perf_header *self, | 75 | int perf_header__add_attr(struct perf_header *self, |
@@ -113,7 +116,7 @@ int event__synthesize_event_types(event__handler_t process, | |||
113 | int event__process_event_type(event_t *self, | 116 | int event__process_event_type(event_t *self, |
114 | struct perf_session *session); | 117 | struct perf_session *session); |
115 | 118 | ||
116 | int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | 119 | int event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, |
117 | event__handler_t process, | 120 | event__handler_t process, |
118 | struct perf_session *session); | 121 | struct perf_session *session); |
119 | int event__process_tracing_data(event_t *self, | 122 | int event__process_tracing_data(event_t *self, |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 32f4f1f2f6e4..02ed318d7312 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -211,7 +211,9 @@ void hist_entry__free(struct hist_entry *he) | |||
211 | * collapse the histogram | 211 | * collapse the histogram |
212 | */ | 212 | */ |
213 | 213 | ||
214 | static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | 214 | static bool hists__collapse_insert_entry(struct hists *self, |
215 | struct rb_root *root, | ||
216 | struct hist_entry *he) | ||
215 | { | 217 | { |
216 | struct rb_node **p = &root->rb_node; | 218 | struct rb_node **p = &root->rb_node; |
217 | struct rb_node *parent = NULL; | 219 | struct rb_node *parent = NULL; |
@@ -226,8 +228,11 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | |||
226 | 228 | ||
227 | if (!cmp) { | 229 | if (!cmp) { |
228 | iter->period += he->period; | 230 | iter->period += he->period; |
229 | if (symbol_conf.use_callchain) | 231 | if (symbol_conf.use_callchain) { |
230 | callchain_merge(iter->callchain, he->callchain); | 232 | callchain_cursor_reset(&self->callchain_cursor); |
233 | callchain_merge(&self->callchain_cursor, iter->callchain, | ||
234 | he->callchain); | ||
235 | } | ||
231 | hist_entry__free(he); | 236 | hist_entry__free(he); |
232 | return false; | 237 | return false; |
233 | } | 238 | } |
@@ -262,7 +267,7 @@ void hists__collapse_resort(struct hists *self) | |||
262 | next = rb_next(&n->rb_node); | 267 | next = rb_next(&n->rb_node); |
263 | 268 | ||
264 | rb_erase(&n->rb_node, &self->entries); | 269 | rb_erase(&n->rb_node, &self->entries); |
265 | if (collapse__insert_entry(&tmp, n)) | 270 | if (hists__collapse_insert_entry(self, &tmp, n)) |
266 | hists__inc_nr_entries(self, n); | 271 | hists__inc_nr_entries(self, n); |
267 | } | 272 | } |
268 | 273 | ||
@@ -425,7 +430,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
425 | u64 cumul; | 430 | u64 cumul; |
426 | 431 | ||
427 | child = rb_entry(node, struct callchain_node, rb_node); | 432 | child = rb_entry(node, struct callchain_node, rb_node); |
428 | cumul = cumul_hits(child); | 433 | cumul = callchain_cumul_hits(child); |
429 | remaining -= cumul; | 434 | remaining -= cumul; |
430 | 435 | ||
431 | /* | 436 | /* |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ee789856a8c9..889559b86492 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -77,6 +77,8 @@ struct hists { | |||
77 | u64 event_stream; | 77 | u64 event_stream; |
78 | u32 type; | 78 | u32 type; |
79 | u16 col_len[HISTC_NR_COLS]; | 79 | u16 col_len[HISTC_NR_COLS]; |
80 | /* Best would be to reuse the session callchain cursor */ | ||
81 | struct callchain_cursor callchain_cursor; | ||
80 | }; | 82 | }; |
81 | 83 | ||
82 | struct hist_entry *__hists__add_entry(struct hists *self, | 84 | struct hist_entry *__hists__add_entry(struct hists *self, |
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h index f5ca26e53fbb..356c7e467b83 100644 --- a/tools/perf/util/include/linux/list.h +++ b/tools/perf/util/include/linux/list.h | |||
@@ -1,3 +1,4 @@ | |||
1 | #include <linux/kernel.h> | ||
1 | #include "../../../../include/linux/list.h" | 2 | #include "../../../../include/linux/list.h" |
2 | 3 | ||
3 | #ifndef PERF_LIST_H | 4 | #ifndef PERF_LIST_H |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 135f69baf966..cf082daa43e3 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include "../../../include/linux/hw_breakpoint.h" | 1 | #include "../../../include/linux/hw_breakpoint.h" |
2 | #include "util.h" | 2 | #include "util.h" |
3 | #include "../perf.h" | 3 | #include "../perf.h" |
4 | #include "evlist.h" | ||
4 | #include "evsel.h" | 5 | #include "evsel.h" |
5 | #include "parse-options.h" | 6 | #include "parse-options.h" |
6 | #include "parse-events.h" | 7 | #include "parse-events.h" |
@@ -11,10 +12,6 @@ | |||
11 | #include "header.h" | 12 | #include "header.h" |
12 | #include "debugfs.h" | 13 | #include "debugfs.h" |
13 | 14 | ||
14 | int nr_counters; | ||
15 | |||
16 | LIST_HEAD(evsel_list); | ||
17 | |||
18 | struct event_symbol { | 15 | struct event_symbol { |
19 | u8 type; | 16 | u8 type; |
20 | u64 config; | 17 | u64 config; |
@@ -449,8 +446,8 @@ parse_single_tracepoint_event(char *sys_name, | |||
449 | /* sys + ':' + event + ':' + flags*/ | 446 | /* sys + ':' + event + ':' + flags*/ |
450 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 447 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
451 | static enum event_result | 448 | static enum event_result |
452 | parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, | 449 | parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, |
453 | char *flags) | 450 | const char *evt_exp, char *flags) |
454 | { | 451 | { |
455 | char evt_path[MAXPATHLEN]; | 452 | char evt_path[MAXPATHLEN]; |
456 | struct dirent *evt_ent; | 453 | struct dirent *evt_ent; |
@@ -483,15 +480,16 @@ parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, | |||
483 | if (len < 0) | 480 | if (len < 0) |
484 | return EVT_FAILED; | 481 | return EVT_FAILED; |
485 | 482 | ||
486 | if (parse_events(NULL, event_opt, 0)) | 483 | if (parse_events(opt, event_opt, 0)) |
487 | return EVT_FAILED; | 484 | return EVT_FAILED; |
488 | } | 485 | } |
489 | 486 | ||
490 | return EVT_HANDLED_ALL; | 487 | return EVT_HANDLED_ALL; |
491 | } | 488 | } |
492 | 489 | ||
493 | static enum event_result parse_tracepoint_event(const char **strp, | 490 | static enum event_result |
494 | struct perf_event_attr *attr) | 491 | parse_tracepoint_event(const struct option *opt, const char **strp, |
492 | struct perf_event_attr *attr) | ||
495 | { | 493 | { |
496 | const char *evt_name; | 494 | const char *evt_name; |
497 | char *flags = NULL, *comma_loc; | 495 | char *flags = NULL, *comma_loc; |
@@ -530,7 +528,7 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
530 | return EVT_FAILED; | 528 | return EVT_FAILED; |
531 | if (strpbrk(evt_name, "*?")) { | 529 | if (strpbrk(evt_name, "*?")) { |
532 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ | 530 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ |
533 | return parse_multiple_tracepoint_event(sys_name, evt_name, | 531 | return parse_multiple_tracepoint_event(opt, sys_name, evt_name, |
534 | flags); | 532 | flags); |
535 | } else { | 533 | } else { |
536 | return parse_single_tracepoint_event(sys_name, evt_name, | 534 | return parse_single_tracepoint_event(sys_name, evt_name, |
@@ -740,11 +738,12 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
740 | * Symbolic names are (almost) exactly matched. | 738 | * Symbolic names are (almost) exactly matched. |
741 | */ | 739 | */ |
742 | static enum event_result | 740 | static enum event_result |
743 | parse_event_symbols(const char **str, struct perf_event_attr *attr) | 741 | parse_event_symbols(const struct option *opt, const char **str, |
742 | struct perf_event_attr *attr) | ||
744 | { | 743 | { |
745 | enum event_result ret; | 744 | enum event_result ret; |
746 | 745 | ||
747 | ret = parse_tracepoint_event(str, attr); | 746 | ret = parse_tracepoint_event(opt, str, attr); |
748 | if (ret != EVT_FAILED) | 747 | if (ret != EVT_FAILED) |
749 | goto modifier; | 748 | goto modifier; |
750 | 749 | ||
@@ -778,14 +777,15 @@ modifier: | |||
778 | return ret; | 777 | return ret; |
779 | } | 778 | } |
780 | 779 | ||
781 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 780 | int parse_events(const struct option *opt, const char *str, int unset __used) |
782 | { | 781 | { |
782 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
783 | struct perf_event_attr attr; | 783 | struct perf_event_attr attr; |
784 | enum event_result ret; | 784 | enum event_result ret; |
785 | 785 | ||
786 | for (;;) { | 786 | for (;;) { |
787 | memset(&attr, 0, sizeof(attr)); | 787 | memset(&attr, 0, sizeof(attr)); |
788 | ret = parse_event_symbols(&str, &attr); | 788 | ret = parse_event_symbols(opt, &str, &attr); |
789 | if (ret == EVT_FAILED) | 789 | if (ret == EVT_FAILED) |
790 | return -1; | 790 | return -1; |
791 | 791 | ||
@@ -794,12 +794,10 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
794 | 794 | ||
795 | if (ret != EVT_HANDLED_ALL) { | 795 | if (ret != EVT_HANDLED_ALL) { |
796 | struct perf_evsel *evsel; | 796 | struct perf_evsel *evsel; |
797 | evsel = perf_evsel__new(&attr, | 797 | evsel = perf_evsel__new(&attr, evlist->nr_entries); |
798 | nr_counters); | ||
799 | if (evsel == NULL) | 798 | if (evsel == NULL) |
800 | return -1; | 799 | return -1; |
801 | list_add_tail(&evsel->node, &evsel_list); | 800 | perf_evlist__add(evlist, evsel); |
802 | ++nr_counters; | ||
803 | } | 801 | } |
804 | 802 | ||
805 | if (*str == 0) | 803 | if (*str == 0) |
@@ -813,13 +811,14 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
813 | return 0; | 811 | return 0; |
814 | } | 812 | } |
815 | 813 | ||
816 | int parse_filter(const struct option *opt __used, const char *str, | 814 | int parse_filter(const struct option *opt, const char *str, |
817 | int unset __used) | 815 | int unset __used) |
818 | { | 816 | { |
817 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
819 | struct perf_evsel *last = NULL; | 818 | struct perf_evsel *last = NULL; |
820 | 819 | ||
821 | if (!list_empty(&evsel_list)) | 820 | if (evlist->nr_entries > 0) |
822 | last = list_entry(evsel_list.prev, struct perf_evsel, node); | 821 | last = list_entry(evlist->entries.prev, struct perf_evsel, node); |
823 | 822 | ||
824 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { | 823 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { |
825 | fprintf(stderr, | 824 | fprintf(stderr, |
@@ -981,33 +980,3 @@ void print_events(void) | |||
981 | 980 | ||
982 | exit(129); | 981 | exit(129); |
983 | } | 982 | } |
984 | |||
985 | int perf_evsel_list__create_default(void) | ||
986 | { | ||
987 | struct perf_evsel *evsel; | ||
988 | struct perf_event_attr attr; | ||
989 | |||
990 | memset(&attr, 0, sizeof(attr)); | ||
991 | attr.type = PERF_TYPE_HARDWARE; | ||
992 | attr.config = PERF_COUNT_HW_CPU_CYCLES; | ||
993 | |||
994 | evsel = perf_evsel__new(&attr, 0); | ||
995 | |||
996 | if (evsel == NULL) | ||
997 | return -ENOMEM; | ||
998 | |||
999 | list_add(&evsel->node, &evsel_list); | ||
1000 | ++nr_counters; | ||
1001 | return 0; | ||
1002 | } | ||
1003 | |||
1004 | void perf_evsel_list__delete(void) | ||
1005 | { | ||
1006 | struct perf_evsel *pos, *n; | ||
1007 | |||
1008 | list_for_each_entry_safe(pos, n, &evsel_list, node) { | ||
1009 | list_del_init(&pos->node); | ||
1010 | perf_evsel__delete(pos); | ||
1011 | } | ||
1012 | nr_counters = 0; | ||
1013 | } | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 458e3ecf17af..cf7e94abb676 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -9,11 +9,6 @@ | |||
9 | struct list_head; | 9 | struct list_head; |
10 | struct perf_evsel; | 10 | struct perf_evsel; |
11 | 11 | ||
12 | extern struct list_head evsel_list; | ||
13 | |||
14 | int perf_evsel_list__create_default(void); | ||
15 | void perf_evsel_list__delete(void); | ||
16 | |||
17 | struct option; | 12 | struct option; |
18 | 13 | ||
19 | struct tracepoint_path { | 14 | struct tracepoint_path { |
@@ -25,8 +20,6 @@ struct tracepoint_path { | |||
25 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); | 20 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); |
26 | extern bool have_tracepoints(struct list_head *evlist); | 21 | extern bool have_tracepoints(struct list_head *evlist); |
27 | 22 | ||
28 | extern int nr_counters; | ||
29 | |||
30 | const char *event_name(struct perf_evsel *event); | 23 | const char *event_name(struct perf_evsel *event); |
31 | extern const char *__event_name(int type, u64 config); | 24 | extern const char *__event_name(int type, u64 config); |
32 | 25 | ||
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 6e29d9c9dccc..859d377a3df3 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <string.h> | 31 | #include <string.h> |
32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
33 | #include <limits.h> | 33 | #include <limits.h> |
34 | #include <elf.h> | ||
34 | 35 | ||
35 | #undef _GNU_SOURCE | 36 | #undef _GNU_SOURCE |
36 | #include "util.h" | 37 | #include "util.h" |
@@ -111,7 +112,25 @@ static struct symbol *__find_kernel_function_by_name(const char *name, | |||
111 | NULL); | 112 | NULL); |
112 | } | 113 | } |
113 | 114 | ||
114 | const char *kernel_get_module_path(const char *module) | 115 | static struct map *kernel_get_module_map(const char *module) |
116 | { | ||
117 | struct rb_node *nd; | ||
118 | struct map_groups *grp = &machine.kmaps; | ||
119 | |||
120 | if (!module) | ||
121 | module = "kernel"; | ||
122 | |||
123 | for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { | ||
124 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
125 | if (strncmp(pos->dso->short_name + 1, module, | ||
126 | pos->dso->short_name_len - 2) == 0) { | ||
127 | return pos; | ||
128 | } | ||
129 | } | ||
130 | return NULL; | ||
131 | } | ||
132 | |||
133 | static struct dso *kernel_get_module_dso(const char *module) | ||
115 | { | 134 | { |
116 | struct dso *dso; | 135 | struct dso *dso; |
117 | struct map *map; | 136 | struct map *map; |
@@ -141,7 +160,13 @@ const char *kernel_get_module_path(const char *module) | |||
141 | } | 160 | } |
142 | } | 161 | } |
143 | found: | 162 | found: |
144 | return dso->long_name; | 163 | return dso; |
164 | } | ||
165 | |||
166 | const char *kernel_get_module_path(const char *module) | ||
167 | { | ||
168 | struct dso *dso = kernel_get_module_dso(module); | ||
169 | return (dso) ? dso->long_name : NULL; | ||
145 | } | 170 | } |
146 | 171 | ||
147 | #ifdef DWARF_SUPPORT | 172 | #ifdef DWARF_SUPPORT |
@@ -1913,3 +1938,42 @@ int del_perf_probe_events(struct strlist *dellist) | |||
1913 | return ret; | 1938 | return ret; |
1914 | } | 1939 | } |
1915 | 1940 | ||
1941 | /* | ||
1942 | * If a symbol corresponds to a function with global binding return 0. | ||
1943 | * For all others return 1. | ||
1944 | */ | ||
1945 | static int filter_non_global_functions(struct map *map __unused, | ||
1946 | struct symbol *sym) | ||
1947 | { | ||
1948 | if (sym->binding != STB_GLOBAL) | ||
1949 | return 1; | ||
1950 | |||
1951 | return 0; | ||
1952 | } | ||
1953 | |||
1954 | int show_available_funcs(const char *module) | ||
1955 | { | ||
1956 | struct map *map; | ||
1957 | int ret; | ||
1958 | |||
1959 | setup_pager(); | ||
1960 | |||
1961 | ret = init_vmlinux(); | ||
1962 | if (ret < 0) | ||
1963 | return ret; | ||
1964 | |||
1965 | map = kernel_get_module_map(module); | ||
1966 | if (!map) { | ||
1967 | pr_err("Failed to find %s map.\n", (module) ? : "kernel"); | ||
1968 | return -EINVAL; | ||
1969 | } | ||
1970 | if (map__load(map, filter_non_global_functions)) { | ||
1971 | pr_err("Failed to load map.\n"); | ||
1972 | return -EINVAL; | ||
1973 | } | ||
1974 | if (!dso__sorted_by_name(map->dso, map->type)) | ||
1975 | dso__sort_by_name(map->dso, map->type); | ||
1976 | |||
1977 | dso__fprintf_symbols_by_name(map->dso, map->type, stdout); | ||
1978 | return 0; | ||
1979 | } | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 5accbedfea37..1fb4f18337d3 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -127,6 +127,7 @@ extern int show_line_range(struct line_range *lr, const char *module); | |||
127 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, | 127 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, |
128 | int max_probe_points, const char *module, | 128 | int max_probe_points, const char *module, |
129 | bool externs); | 129 | bool externs); |
130 | extern int show_available_funcs(const char *module); | ||
130 | 131 | ||
131 | 132 | ||
132 | /* Maximum index number of event-name postfix */ | 133 | /* Maximum index number of event-name postfix */ |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ab83b6ac5d65..69215bff17e9 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -280,6 +280,19 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | |||
280 | return name ? (strcmp(tname, name) == 0) : false; | 280 | return name ? (strcmp(tname, name) == 0) : false; |
281 | } | 281 | } |
282 | 282 | ||
283 | /* Get callsite line number of inline-function instance */ | ||
284 | static int die_get_call_lineno(Dwarf_Die *in_die) | ||
285 | { | ||
286 | Dwarf_Attribute attr; | ||
287 | Dwarf_Word ret; | ||
288 | |||
289 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) | ||
290 | return -ENOENT; | ||
291 | |||
292 | dwarf_formudata(&attr, &ret); | ||
293 | return (int)ret; | ||
294 | } | ||
295 | |||
283 | /* Get type die */ | 296 | /* Get type die */ |
284 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | 297 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) |
285 | { | 298 | { |
@@ -458,6 +471,151 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | |||
458 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); | 471 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); |
459 | } | 472 | } |
460 | 473 | ||
474 | /* Walker on lines (Note: line number will not be sorted) */ | ||
475 | typedef int (* line_walk_handler_t) (const char *fname, int lineno, | ||
476 | Dwarf_Addr addr, void *data); | ||
477 | |||
478 | struct __line_walk_param { | ||
479 | const char *fname; | ||
480 | line_walk_handler_t handler; | ||
481 | void *data; | ||
482 | int retval; | ||
483 | }; | ||
484 | |||
485 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) | ||
486 | { | ||
487 | struct __line_walk_param *lw = data; | ||
488 | Dwarf_Addr addr; | ||
489 | int lineno; | ||
490 | |||
491 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { | ||
492 | lineno = die_get_call_lineno(in_die); | ||
493 | if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { | ||
494 | lw->retval = lw->handler(lw->fname, lineno, addr, | ||
495 | lw->data); | ||
496 | if (lw->retval != 0) | ||
497 | return DIE_FIND_CB_FOUND; | ||
498 | } | ||
499 | } | ||
500 | return DIE_FIND_CB_SIBLING; | ||
501 | } | ||
502 | |||
503 | /* Walk on lines of blocks included in given DIE */ | ||
504 | static int __die_walk_funclines(Dwarf_Die *sp_die, | ||
505 | line_walk_handler_t handler, void *data) | ||
506 | { | ||
507 | struct __line_walk_param lw = { | ||
508 | .handler = handler, | ||
509 | .data = data, | ||
510 | .retval = 0, | ||
511 | }; | ||
512 | Dwarf_Die die_mem; | ||
513 | Dwarf_Addr addr; | ||
514 | int lineno; | ||
515 | |||
516 | /* Handle function declaration line */ | ||
517 | lw.fname = dwarf_decl_file(sp_die); | ||
518 | if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
519 | dwarf_entrypc(sp_die, &addr) == 0) { | ||
520 | lw.retval = handler(lw.fname, lineno, addr, data); | ||
521 | if (lw.retval != 0) | ||
522 | goto done; | ||
523 | } | ||
524 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); | ||
525 | done: | ||
526 | return lw.retval; | ||
527 | } | ||
528 | |||
529 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | ||
530 | { | ||
531 | struct __line_walk_param *lw = data; | ||
532 | |||
533 | lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data); | ||
534 | if (lw->retval != 0) | ||
535 | return DWARF_CB_ABORT; | ||
536 | |||
537 | return DWARF_CB_OK; | ||
538 | } | ||
539 | |||
540 | /* | ||
541 | * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on | ||
542 | * the lines inside the subprogram, otherwise PDIE must be a CU DIE. | ||
543 | */ | ||
544 | static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler, | ||
545 | void *data) | ||
546 | { | ||
547 | Dwarf_Lines *lines; | ||
548 | Dwarf_Line *line; | ||
549 | Dwarf_Addr addr; | ||
550 | const char *fname; | ||
551 | int lineno, ret = 0; | ||
552 | Dwarf_Die die_mem, *cu_die; | ||
553 | size_t nlines, i; | ||
554 | |||
555 | /* Get the CU die */ | ||
556 | if (dwarf_tag(pdie) == DW_TAG_subprogram) | ||
557 | cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL); | ||
558 | else | ||
559 | cu_die = pdie; | ||
560 | if (!cu_die) { | ||
561 | pr_debug2("Failed to get CU from subprogram\n"); | ||
562 | return -EINVAL; | ||
563 | } | ||
564 | |||
565 | /* Get lines list in the CU */ | ||
566 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | ||
567 | pr_debug2("Failed to get source lines on this CU.\n"); | ||
568 | return -ENOENT; | ||
569 | } | ||
570 | pr_debug2("Get %zd lines from this CU\n", nlines); | ||
571 | |||
572 | /* Walk on the lines on lines list */ | ||
573 | for (i = 0; i < nlines; i++) { | ||
574 | line = dwarf_onesrcline(lines, i); | ||
575 | if (line == NULL || | ||
576 | dwarf_lineno(line, &lineno) != 0 || | ||
577 | dwarf_lineaddr(line, &addr) != 0) { | ||
578 | pr_debug2("Failed to get line info. " | ||
579 | "Possible error in debuginfo.\n"); | ||
580 | continue; | ||
581 | } | ||
582 | /* Filter lines based on address */ | ||
583 | if (pdie != cu_die) | ||
584 | /* | ||
585 | * Address filtering | ||
586 | * The line is included in given function, and | ||
587 | * no inline block includes it. | ||
588 | */ | ||
589 | if (!dwarf_haspc(pdie, addr) || | ||
590 | die_find_inlinefunc(pdie, addr, &die_mem)) | ||
591 | continue; | ||
592 | /* Get source line */ | ||
593 | fname = dwarf_linesrc(line, NULL, NULL); | ||
594 | |||
595 | ret = handler(fname, lineno, addr, data); | ||
596 | if (ret != 0) | ||
597 | return ret; | ||
598 | } | ||
599 | |||
600 | /* | ||
601 | * Dwarf lines doesn't include function declarations and inlined | ||
602 | * subroutines. We have to check functions list or given function. | ||
603 | */ | ||
604 | if (pdie != cu_die) | ||
605 | ret = __die_walk_funclines(pdie, handler, data); | ||
606 | else { | ||
607 | struct __line_walk_param param = { | ||
608 | .handler = handler, | ||
609 | .data = data, | ||
610 | .retval = 0, | ||
611 | }; | ||
612 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | ||
613 | ret = param.retval; | ||
614 | } | ||
615 | |||
616 | return ret; | ||
617 | } | ||
618 | |||
461 | struct __find_variable_param { | 619 | struct __find_variable_param { |
462 | const char *name; | 620 | const char *name; |
463 | Dwarf_Addr addr; | 621 | Dwarf_Addr addr; |
@@ -1050,43 +1208,26 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1050 | return ret; | 1208 | return ret; |
1051 | } | 1209 | } |
1052 | 1210 | ||
1053 | /* Find probe point from its line number */ | 1211 | static int probe_point_line_walker(const char *fname, int lineno, |
1054 | static int find_probe_point_by_line(struct probe_finder *pf) | 1212 | Dwarf_Addr addr, void *data) |
1055 | { | 1213 | { |
1056 | Dwarf_Lines *lines; | 1214 | struct probe_finder *pf = data; |
1057 | Dwarf_Line *line; | 1215 | int ret; |
1058 | size_t nlines, i; | ||
1059 | Dwarf_Addr addr; | ||
1060 | int lineno; | ||
1061 | int ret = 0; | ||
1062 | |||
1063 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | ||
1064 | pr_warning("No source lines found.\n"); | ||
1065 | return -ENOENT; | ||
1066 | } | ||
1067 | 1216 | ||
1068 | for (i = 0; i < nlines && ret == 0; i++) { | 1217 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) |
1069 | line = dwarf_onesrcline(lines, i); | 1218 | return 0; |
1070 | if (dwarf_lineno(line, &lineno) != 0 || | ||
1071 | lineno != pf->lno) | ||
1072 | continue; | ||
1073 | 1219 | ||
1074 | /* TODO: Get fileno from line, but how? */ | 1220 | pf->addr = addr; |
1075 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | 1221 | ret = call_probe_finder(NULL, pf); |
1076 | continue; | ||
1077 | 1222 | ||
1078 | if (dwarf_lineaddr(line, &addr) != 0) { | 1223 | /* Continue if no error, because the line will be in inline function */ |
1079 | pr_warning("Failed to get the address of the line.\n"); | 1224 | return ret < 0 ?: 0; |
1080 | return -ENOENT; | 1225 | } |
1081 | } | ||
1082 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", | ||
1083 | (int)i, lineno, (uintmax_t)addr); | ||
1084 | pf->addr = addr; | ||
1085 | 1226 | ||
1086 | ret = call_probe_finder(NULL, pf); | 1227 | /* Find probe point from its line number */ |
1087 | /* Continuing, because target line might be inlined. */ | 1228 | static int find_probe_point_by_line(struct probe_finder *pf) |
1088 | } | 1229 | { |
1089 | return ret; | 1230 | return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf); |
1090 | } | 1231 | } |
1091 | 1232 | ||
1092 | /* Find lines which match lazy pattern */ | 1233 | /* Find lines which match lazy pattern */ |
@@ -1140,15 +1281,31 @@ out_close: | |||
1140 | return nlines; | 1281 | return nlines; |
1141 | } | 1282 | } |
1142 | 1283 | ||
1284 | static int probe_point_lazy_walker(const char *fname, int lineno, | ||
1285 | Dwarf_Addr addr, void *data) | ||
1286 | { | ||
1287 | struct probe_finder *pf = data; | ||
1288 | int ret; | ||
1289 | |||
1290 | if (!line_list__has_line(&pf->lcache, lineno) || | ||
1291 | strtailcmp(fname, pf->fname) != 0) | ||
1292 | return 0; | ||
1293 | |||
1294 | pr_debug("Probe line found: line:%d addr:0x%llx\n", | ||
1295 | lineno, (unsigned long long)addr); | ||
1296 | pf->addr = addr; | ||
1297 | ret = call_probe_finder(NULL, pf); | ||
1298 | |||
1299 | /* | ||
1300 | * Continue if no error, because the lazy pattern will match | ||
1301 | * to other lines | ||
1302 | */ | ||
1303 | return ret < 0 ?: 0; | ||
1304 | } | ||
1305 | |||
1143 | /* Find probe points from lazy pattern */ | 1306 | /* Find probe points from lazy pattern */ |
1144 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | 1307 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) |
1145 | { | 1308 | { |
1146 | Dwarf_Lines *lines; | ||
1147 | Dwarf_Line *line; | ||
1148 | size_t nlines, i; | ||
1149 | Dwarf_Addr addr; | ||
1150 | Dwarf_Die die_mem; | ||
1151 | int lineno; | ||
1152 | int ret = 0; | 1309 | int ret = 0; |
1153 | 1310 | ||
1154 | if (list_empty(&pf->lcache)) { | 1311 | if (list_empty(&pf->lcache)) { |
@@ -1162,45 +1319,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1162 | return ret; | 1319 | return ret; |
1163 | } | 1320 | } |
1164 | 1321 | ||
1165 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | 1322 | return die_walk_lines(sp_die, probe_point_lazy_walker, pf); |
1166 | pr_warning("No source lines found.\n"); | ||
1167 | return -ENOENT; | ||
1168 | } | ||
1169 | |||
1170 | for (i = 0; i < nlines && ret >= 0; i++) { | ||
1171 | line = dwarf_onesrcline(lines, i); | ||
1172 | |||
1173 | if (dwarf_lineno(line, &lineno) != 0 || | ||
1174 | !line_list__has_line(&pf->lcache, lineno)) | ||
1175 | continue; | ||
1176 | |||
1177 | /* TODO: Get fileno from line, but how? */ | ||
1178 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
1179 | continue; | ||
1180 | |||
1181 | if (dwarf_lineaddr(line, &addr) != 0) { | ||
1182 | pr_debug("Failed to get the address of line %d.\n", | ||
1183 | lineno); | ||
1184 | continue; | ||
1185 | } | ||
1186 | if (sp_die) { | ||
1187 | /* Address filtering 1: does sp_die include addr? */ | ||
1188 | if (!dwarf_haspc(sp_die, addr)) | ||
1189 | continue; | ||
1190 | /* Address filtering 2: No child include addr? */ | ||
1191 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) | ||
1192 | continue; | ||
1193 | } | ||
1194 | |||
1195 | pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", | ||
1196 | (int)i, lineno, (unsigned long long)addr); | ||
1197 | pf->addr = addr; | ||
1198 | |||
1199 | ret = call_probe_finder(sp_die, pf); | ||
1200 | /* Continuing, because target line might be inlined. */ | ||
1201 | } | ||
1202 | /* TODO: deallocate lines, but how? */ | ||
1203 | return ret; | ||
1204 | } | 1323 | } |
1205 | 1324 | ||
1206 | /* Callback parameter with return value */ | 1325 | /* Callback parameter with return value */ |
@@ -1644,91 +1763,28 @@ static int line_range_add_line(const char *src, unsigned int lineno, | |||
1644 | return line_list__add_line(&lr->line_list, lineno); | 1763 | return line_list__add_line(&lr->line_list, lineno); |
1645 | } | 1764 | } |
1646 | 1765 | ||
1647 | /* Search function declaration lines */ | 1766 | static int line_range_walk_cb(const char *fname, int lineno, |
1648 | static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) | 1767 | Dwarf_Addr addr __used, |
1768 | void *data) | ||
1649 | { | 1769 | { |
1650 | struct dwarf_callback_param *param = data; | 1770 | struct line_finder *lf = data; |
1651 | struct line_finder *lf = param->data; | ||
1652 | const char *src; | ||
1653 | int lineno; | ||
1654 | 1771 | ||
1655 | src = dwarf_decl_file(sp_die); | 1772 | if ((strtailcmp(fname, lf->fname) != 0) || |
1656 | if (src && strtailcmp(src, lf->fname) != 0) | ||
1657 | return DWARF_CB_OK; | ||
1658 | |||
1659 | if (dwarf_decl_line(sp_die, &lineno) != 0 || | ||
1660 | (lf->lno_s > lineno || lf->lno_e < lineno)) | 1773 | (lf->lno_s > lineno || lf->lno_e < lineno)) |
1661 | return DWARF_CB_OK; | 1774 | return 0; |
1662 | 1775 | ||
1663 | param->retval = line_range_add_line(src, lineno, lf->lr); | 1776 | if (line_range_add_line(fname, lineno, lf->lr) < 0) |
1664 | if (param->retval < 0) | 1777 | return -EINVAL; |
1665 | return DWARF_CB_ABORT; | ||
1666 | return DWARF_CB_OK; | ||
1667 | } | ||
1668 | 1778 | ||
1669 | static int find_line_range_func_decl_lines(struct line_finder *lf) | 1779 | return 0; |
1670 | { | ||
1671 | struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; | ||
1672 | dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0); | ||
1673 | return param.retval; | ||
1674 | } | 1780 | } |
1675 | 1781 | ||
1676 | /* Find line range from its line number */ | 1782 | /* Find line range from its line number */ |
1677 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | 1783 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) |
1678 | { | 1784 | { |
1679 | Dwarf_Lines *lines; | 1785 | int ret; |
1680 | Dwarf_Line *line; | ||
1681 | size_t nlines, i; | ||
1682 | Dwarf_Addr addr; | ||
1683 | int lineno, ret = 0; | ||
1684 | const char *src; | ||
1685 | Dwarf_Die die_mem; | ||
1686 | |||
1687 | line_list__init(&lf->lr->line_list); | ||
1688 | if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { | ||
1689 | pr_warning("No source lines found.\n"); | ||
1690 | return -ENOENT; | ||
1691 | } | ||
1692 | |||
1693 | /* Search probable lines on lines list */ | ||
1694 | for (i = 0; i < nlines; i++) { | ||
1695 | line = dwarf_onesrcline(lines, i); | ||
1696 | if (dwarf_lineno(line, &lineno) != 0 || | ||
1697 | (lf->lno_s > lineno || lf->lno_e < lineno)) | ||
1698 | continue; | ||
1699 | |||
1700 | if (sp_die) { | ||
1701 | /* Address filtering 1: does sp_die include addr? */ | ||
1702 | if (dwarf_lineaddr(line, &addr) != 0 || | ||
1703 | !dwarf_haspc(sp_die, addr)) | ||
1704 | continue; | ||
1705 | |||
1706 | /* Address filtering 2: No child include addr? */ | ||
1707 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) | ||
1708 | continue; | ||
1709 | } | ||
1710 | |||
1711 | /* TODO: Get fileno from line, but how? */ | ||
1712 | src = dwarf_linesrc(line, NULL, NULL); | ||
1713 | if (strtailcmp(src, lf->fname) != 0) | ||
1714 | continue; | ||
1715 | |||
1716 | ret = line_range_add_line(src, lineno, lf->lr); | ||
1717 | if (ret < 0) | ||
1718 | return ret; | ||
1719 | } | ||
1720 | 1786 | ||
1721 | /* | 1787 | ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf); |
1722 | * Dwarf lines doesn't include function declarations. We have to | ||
1723 | * check functions list or given function. | ||
1724 | */ | ||
1725 | if (sp_die) { | ||
1726 | src = dwarf_decl_file(sp_die); | ||
1727 | if (src && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
1728 | (lf->lno_s <= lineno && lf->lno_e >= lineno)) | ||
1729 | ret = line_range_add_line(src, lineno, lf->lr); | ||
1730 | } else | ||
1731 | ret = find_line_range_func_decl_lines(lf); | ||
1732 | 1788 | ||
1733 | /* Update status */ | 1789 | /* Update status */ |
1734 | if (ret >= 0) | 1790 | if (ret >= 0) |
@@ -1758,9 +1814,6 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
1758 | struct line_finder *lf = param->data; | 1814 | struct line_finder *lf = param->data; |
1759 | struct line_range *lr = lf->lr; | 1815 | struct line_range *lr = lf->lr; |
1760 | 1816 | ||
1761 | pr_debug("find (%llx) %s\n", | ||
1762 | (unsigned long long)dwarf_dieoffset(sp_die), | ||
1763 | dwarf_diename(sp_die)); | ||
1764 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | 1817 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
1765 | die_compare_name(sp_die, lr->function)) { | 1818 | die_compare_name(sp_die, lr->function)) { |
1766 | lf->fname = dwarf_decl_file(sp_die); | 1819 | lf->fname = dwarf_decl_file(sp_die); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 105f00bfd555..e6a07408669e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -242,17 +242,16 @@ static bool symbol__match_parent_regex(struct symbol *sym) | |||
242 | return 0; | 242 | return 0; |
243 | } | 243 | } |
244 | 244 | ||
245 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | 245 | int perf_session__resolve_callchain(struct perf_session *self, |
246 | struct thread *thread, | 246 | struct thread *thread, |
247 | struct ip_callchain *chain, | 247 | struct ip_callchain *chain, |
248 | struct symbol **parent) | 248 | struct symbol **parent) |
249 | { | 249 | { |
250 | u8 cpumode = PERF_RECORD_MISC_USER; | 250 | u8 cpumode = PERF_RECORD_MISC_USER; |
251 | unsigned int i; | 251 | unsigned int i; |
252 | struct map_symbol *syms = calloc(chain->nr, sizeof(*syms)); | 252 | int err; |
253 | 253 | ||
254 | if (!syms) | 254 | callchain_cursor_reset(&self->callchain_cursor); |
255 | return NULL; | ||
256 | 255 | ||
257 | for (i = 0; i < chain->nr; i++) { | 256 | for (i = 0; i < chain->nr; i++) { |
258 | u64 ip = chain->ips[i]; | 257 | u64 ip = chain->ips[i]; |
@@ -281,12 +280,15 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | |||
281 | *parent = al.sym; | 280 | *parent = al.sym; |
282 | if (!symbol_conf.use_callchain) | 281 | if (!symbol_conf.use_callchain) |
283 | break; | 282 | break; |
284 | syms[i].map = al.map; | ||
285 | syms[i].sym = al.sym; | ||
286 | } | 283 | } |
284 | |||
285 | err = callchain_cursor_append(&self->callchain_cursor, | ||
286 | ip, al.map, al.sym); | ||
287 | if (err) | ||
288 | return err; | ||
287 | } | 289 | } |
288 | 290 | ||
289 | return syms; | 291 | return 0; |
290 | } | 292 | } |
291 | 293 | ||
292 | static int process_event_synth_stub(event_t *event __used, | 294 | static int process_event_synth_stub(event_t *event __used, |
@@ -494,7 +496,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
494 | if (iter->timestamp > limit) | 496 | if (iter->timestamp > limit) |
495 | break; | 497 | break; |
496 | 498 | ||
497 | event__parse_sample(iter->event, s, &sample); | 499 | perf_session__parse_sample(s, iter->event, &sample); |
498 | perf_session_deliver_event(s, iter->event, &sample, ops, | 500 | perf_session_deliver_event(s, iter->event, &sample, ops, |
499 | iter->file_offset); | 501 | iter->file_offset); |
500 | 502 | ||
@@ -804,7 +806,7 @@ static int perf_session__process_event(struct perf_session *session, | |||
804 | /* | 806 | /* |
805 | * For all kernel events we get the sample data | 807 | * For all kernel events we get the sample data |
806 | */ | 808 | */ |
807 | event__parse_sample(event, session, &sample); | 809 | perf_session__parse_sample(session, event, &sample); |
808 | 810 | ||
809 | /* Preprocess sample records - precheck callchains */ | 811 | /* Preprocess sample records - precheck callchains */ |
810 | if (perf_session__preprocess_sample(session, event, &sample)) | 812 | if (perf_session__preprocess_sample(session, event, &sample)) |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index decd83f274fd..78239767011e 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -51,7 +51,8 @@ struct perf_session { | |||
51 | int cwdlen; | 51 | int cwdlen; |
52 | char *cwd; | 52 | char *cwd; |
53 | struct ordered_samples ordered_samples; | 53 | struct ordered_samples ordered_samples; |
54 | char filename[0]; | 54 | struct callchain_cursor callchain_cursor; |
55 | char filename[0]; | ||
55 | }; | 56 | }; |
56 | 57 | ||
57 | struct perf_event_ops; | 58 | struct perf_event_ops; |
@@ -94,10 +95,10 @@ int __perf_session__process_events(struct perf_session *self, | |||
94 | int perf_session__process_events(struct perf_session *self, | 95 | int perf_session__process_events(struct perf_session *self, |
95 | struct perf_event_ops *event_ops); | 96 | struct perf_event_ops *event_ops); |
96 | 97 | ||
97 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | 98 | int perf_session__resolve_callchain(struct perf_session *self, |
98 | struct thread *thread, | 99 | struct thread *thread, |
99 | struct ip_callchain *chain, | 100 | struct ip_callchain *chain, |
100 | struct symbol **parent); | 101 | struct symbol **parent); |
101 | 102 | ||
102 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | 103 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
103 | 104 | ||
@@ -154,4 +155,13 @@ size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp) | |||
154 | { | 155 | { |
155 | return hists__fprintf_nr_events(&self->hists, fp); | 156 | return hists__fprintf_nr_events(&self->hists, fp); |
156 | } | 157 | } |
158 | |||
159 | static inline int perf_session__parse_sample(struct perf_session *session, | ||
160 | const event_t *event, | ||
161 | struct sample_data *sample) | ||
162 | { | ||
163 | return event__parse_sample(event, session->sample_type, | ||
164 | session->sample_id_all, sample); | ||
165 | } | ||
166 | |||
157 | #endif /* __PERF_SESSION_H */ | 167 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 00f4eade2e3e..d5d3b22250f3 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -7,61 +7,6 @@ | |||
7 | #include "util.h" | 7 | #include "util.h" |
8 | #include "debug.h" | 8 | #include "debug.h" |
9 | 9 | ||
10 | /* Skip "." and ".." directories */ | ||
11 | static int filter(const struct dirent *dir) | ||
12 | { | ||
13 | if (dir->d_name[0] == '.') | ||
14 | return 0; | ||
15 | else | ||
16 | return 1; | ||
17 | } | ||
18 | |||
19 | struct thread_map *thread_map__new_by_pid(pid_t pid) | ||
20 | { | ||
21 | struct thread_map *threads; | ||
22 | char name[256]; | ||
23 | int items; | ||
24 | struct dirent **namelist = NULL; | ||
25 | int i; | ||
26 | |||
27 | sprintf(name, "/proc/%d/task", pid); | ||
28 | items = scandir(name, &namelist, filter, NULL); | ||
29 | if (items <= 0) | ||
30 | return NULL; | ||
31 | |||
32 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); | ||
33 | if (threads != NULL) { | ||
34 | for (i = 0; i < items; i++) | ||
35 | threads->map[i] = atoi(namelist[i]->d_name); | ||
36 | threads->nr = items; | ||
37 | } | ||
38 | |||
39 | for (i=0; i<items; i++) | ||
40 | free(namelist[i]); | ||
41 | free(namelist); | ||
42 | |||
43 | return threads; | ||
44 | } | ||
45 | |||
46 | struct thread_map *thread_map__new_by_tid(pid_t tid) | ||
47 | { | ||
48 | struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
49 | |||
50 | if (threads != NULL) { | ||
51 | threads->map[0] = tid; | ||
52 | threads->nr = 1; | ||
53 | } | ||
54 | |||
55 | return threads; | ||
56 | } | ||
57 | |||
58 | struct thread_map *thread_map__new(pid_t pid, pid_t tid) | ||
59 | { | ||
60 | if (pid != -1) | ||
61 | return thread_map__new_by_pid(pid); | ||
62 | return thread_map__new_by_tid(tid); | ||
63 | } | ||
64 | |||
65 | static struct thread *thread__new(pid_t pid) | 10 | static struct thread *thread__new(pid_t pid) |
66 | { | 11 | { |
67 | struct thread *self = zalloc(sizeof(*self)); | 12 | struct thread *self = zalloc(sizeof(*self)); |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index d7574101054a..e5f2401c1b5e 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -18,24 +18,10 @@ struct thread { | |||
18 | int comm_len; | 18 | int comm_len; |
19 | }; | 19 | }; |
20 | 20 | ||
21 | struct thread_map { | ||
22 | int nr; | ||
23 | int map[]; | ||
24 | }; | ||
25 | |||
26 | struct perf_session; | 21 | struct perf_session; |
27 | 22 | ||
28 | void thread__delete(struct thread *self); | 23 | void thread__delete(struct thread *self); |
29 | 24 | ||
30 | struct thread_map *thread_map__new_by_pid(pid_t pid); | ||
31 | struct thread_map *thread_map__new_by_tid(pid_t tid); | ||
32 | struct thread_map *thread_map__new(pid_t pid, pid_t tid); | ||
33 | |||
34 | static inline void thread_map__delete(struct thread_map *threads) | ||
35 | { | ||
36 | free(threads); | ||
37 | } | ||
38 | |||
39 | int thread__set_comm(struct thread *self, const char *comm); | 25 | int thread__set_comm(struct thread *self, const char *comm); |
40 | int thread__comm_len(struct thread *self); | 26 | int thread__comm_len(struct thread *self); |
41 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); | 27 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c new file mode 100644 index 000000000000..a5df131b77c3 --- /dev/null +++ b/tools/perf/util/thread_map.c | |||
@@ -0,0 +1,64 @@ | |||
1 | #include <dirent.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <stdio.h> | ||
4 | #include "thread_map.h" | ||
5 | |||
6 | /* Skip "." and ".." directories */ | ||
7 | static int filter(const struct dirent *dir) | ||
8 | { | ||
9 | if (dir->d_name[0] == '.') | ||
10 | return 0; | ||
11 | else | ||
12 | return 1; | ||
13 | } | ||
14 | |||
15 | struct thread_map *thread_map__new_by_pid(pid_t pid) | ||
16 | { | ||
17 | struct thread_map *threads; | ||
18 | char name[256]; | ||
19 | int items; | ||
20 | struct dirent **namelist = NULL; | ||
21 | int i; | ||
22 | |||
23 | sprintf(name, "/proc/%d/task", pid); | ||
24 | items = scandir(name, &namelist, filter, NULL); | ||
25 | if (items <= 0) | ||
26 | return NULL; | ||
27 | |||
28 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); | ||
29 | if (threads != NULL) { | ||
30 | for (i = 0; i < items; i++) | ||
31 | threads->map[i] = atoi(namelist[i]->d_name); | ||
32 | threads->nr = items; | ||
33 | } | ||
34 | |||
35 | for (i=0; i<items; i++) | ||
36 | free(namelist[i]); | ||
37 | free(namelist); | ||
38 | |||
39 | return threads; | ||
40 | } | ||
41 | |||
42 | struct thread_map *thread_map__new_by_tid(pid_t tid) | ||
43 | { | ||
44 | struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
45 | |||
46 | if (threads != NULL) { | ||
47 | threads->map[0] = tid; | ||
48 | threads->nr = 1; | ||
49 | } | ||
50 | |||
51 | return threads; | ||
52 | } | ||
53 | |||
54 | struct thread_map *thread_map__new(pid_t pid, pid_t tid) | ||
55 | { | ||
56 | if (pid != -1) | ||
57 | return thread_map__new_by_pid(pid); | ||
58 | return thread_map__new_by_tid(tid); | ||
59 | } | ||
60 | |||
61 | void thread_map__delete(struct thread_map *threads) | ||
62 | { | ||
63 | free(threads); | ||
64 | } | ||
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h new file mode 100644 index 000000000000..3cb907311409 --- /dev/null +++ b/tools/perf/util/thread_map.h | |||
@@ -0,0 +1,15 @@ | |||
1 | #ifndef __PERF_THREAD_MAP_H | ||
2 | #define __PERF_THREAD_MAP_H | ||
3 | |||
4 | #include <sys/types.h> | ||
5 | |||
6 | struct thread_map { | ||
7 | int nr; | ||
8 | int map[]; | ||
9 | }; | ||
10 | |||
11 | struct thread_map *thread_map__new_by_pid(pid_t pid); | ||
12 | struct thread_map *thread_map__new_by_tid(pid_t tid); | ||
13 | struct thread_map *thread_map__new(pid_t pid, pid_t tid); | ||
14 | void thread_map__delete(struct thread_map *threads); | ||
15 | #endif /* __PERF_THREAD_MAP_H */ | ||
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 60c463c16028..86428239fa65 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -377,7 +377,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, | |||
377 | while (node) { | 377 | while (node) { |
378 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 378 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
379 | struct rb_node *next = rb_next(node); | 379 | struct rb_node *next = rb_next(node); |
380 | u64 cumul = cumul_hits(child); | 380 | u64 cumul = callchain_cumul_hits(child); |
381 | struct callchain_list *chain; | 381 | struct callchain_list *chain; |
382 | char folded_sign = ' '; | 382 | char folded_sign = ' '; |
383 | int first = true; | 383 | int first = true; |