aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/annotate.h2
-rw-r--r--tools/perf/util/build-id.c2
-rw-r--r--tools/perf/util/build-id.h2
-rw-r--r--tools/perf/util/callchain.c123
-rw-r--r--tools/perf/util/callchain.h19
-rw-r--r--tools/perf/util/config.c4
-rw-r--r--tools/perf/util/cpumap.c160
-rw-r--r--tools/perf/util/cpumap.h35
-rw-r--r--tools/perf/util/dso.h2
-rw-r--r--tools/perf/util/event.c4
-rw-r--r--tools/perf/util/event.h24
-rw-r--r--tools/perf/util/evsel.h9
-rw-r--r--tools/perf/util/header.h4
-rw-r--r--tools/perf/util/hist.c668
-rw-r--r--tools/perf/util/hist.h101
-rw-r--r--tools/perf/util/include/linux/bitmap.h3
-rw-r--r--tools/perf/util/include/linux/export.h6
-rw-r--r--tools/perf/util/include/linux/list.h1
-rw-r--r--tools/perf/util/include/linux/types.h29
-rw-r--r--tools/perf/util/machine.c11
-rw-r--r--tools/perf/util/map.c118
-rw-r--r--tools/perf/util/map.h14
-rw-r--r--tools/perf/util/pager.c12
-rw-r--r--tools/perf/util/parse-events.h3
-rw-r--r--tools/perf/util/parse-events.y14
-rw-r--r--tools/perf/util/perf_regs.h2
-rw-r--r--tools/perf/util/pmu.c6
-rw-r--r--tools/perf/util/pmu.h2
-rw-r--r--tools/perf/util/session.c5
-rw-r--r--tools/perf/util/sort.c523
-rw-r--r--tools/perf/util/sort.h26
-rw-r--r--tools/perf/util/stat.h2
-rw-r--r--tools/perf/util/svghelper.c2
-rw-r--r--tools/perf/util/svghelper.h2
-rw-r--r--tools/perf/util/symbol.c11
-rw-r--r--tools/perf/util/symbol.h5
-rw-r--r--tools/perf/util/thread.c52
-rw-r--r--tools/perf/util/thread.h3
-rw-r--r--tools/perf/util/top.h2
-rw-r--r--tools/perf/util/types.h24
-rw-r--r--tools/perf/util/unwind-libdw.c2
-rw-r--r--tools/perf/util/unwind.h2
-rw-r--r--tools/perf/util/util.c2
-rw-r--r--tools/perf/util/util.h2
-rw-r--r--tools/perf/util/values.h2
45 files changed, 1770 insertions, 277 deletions
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 56ad4f5287de..112d6e268150 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -3,7 +3,7 @@
3 3
4#include <stdbool.h> 4#include <stdbool.h>
5#include <stdint.h> 5#include <stdint.h>
6#include "types.h" 6#include <linux/types.h>
7#include "symbol.h" 7#include "symbol.h"
8#include "hist.h" 8#include "hist.h"
9#include "sort.h" 9#include "sort.h"
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 6baabe63182b..a904a4cfe7d3 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -25,7 +25,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
25 struct addr_location al; 25 struct addr_location al;
26 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 26 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
27 struct thread *thread = machine__findnew_thread(machine, sample->pid, 27 struct thread *thread = machine__findnew_thread(machine, sample->pid,
28 sample->pid); 28 sample->tid);
29 29
30 if (thread == NULL) { 30 if (thread == NULL) {
31 pr_err("problem processing %d event, skipping it.\n", 31 pr_err("problem processing %d event, skipping it.\n",
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index 845ef865eced..ae392561470b 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -4,7 +4,7 @@
4#define BUILD_ID_SIZE 20 4#define BUILD_ID_SIZE 20
5 5
6#include "tool.h" 6#include "tool.h"
7#include "types.h" 7#include <linux/types.h>
8 8
9extern struct perf_tool build_id__mark_dso_hit_ops; 9extern struct perf_tool build_id__mark_dso_hit_ops;
10struct dso; 10struct dso;
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 8d9db454f1a9..48b6d3f50012 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -25,6 +25,84 @@
25 25
26__thread struct callchain_cursor callchain_cursor; 26__thread struct callchain_cursor callchain_cursor;
27 27
28int
29parse_callchain_report_opt(const char *arg)
30{
31 char *tok, *tok2;
32 char *endptr;
33
34 symbol_conf.use_callchain = true;
35
36 if (!arg)
37 return 0;
38
39 tok = strtok((char *)arg, ",");
40 if (!tok)
41 return -1;
42
43 /* get the output mode */
44 if (!strncmp(tok, "graph", strlen(arg))) {
45 callchain_param.mode = CHAIN_GRAPH_ABS;
46
47 } else if (!strncmp(tok, "flat", strlen(arg))) {
48 callchain_param.mode = CHAIN_FLAT;
49 } else if (!strncmp(tok, "fractal", strlen(arg))) {
50 callchain_param.mode = CHAIN_GRAPH_REL;
51 } else if (!strncmp(tok, "none", strlen(arg))) {
52 callchain_param.mode = CHAIN_NONE;
53 symbol_conf.use_callchain = false;
54 return 0;
55 } else {
56 return -1;
57 }
58
59 /* get the min percentage */
60 tok = strtok(NULL, ",");
61 if (!tok)
62 goto setup;
63
64 callchain_param.min_percent = strtod(tok, &endptr);
65 if (tok == endptr)
66 return -1;
67
68 /* get the print limit */
69 tok2 = strtok(NULL, ",");
70 if (!tok2)
71 goto setup;
72
73 if (tok2[0] != 'c') {
74 callchain_param.print_limit = strtoul(tok2, &endptr, 0);
75 tok2 = strtok(NULL, ",");
76 if (!tok2)
77 goto setup;
78 }
79
80 /* get the call chain order */
81 if (!strncmp(tok2, "caller", strlen("caller")))
82 callchain_param.order = ORDER_CALLER;
83 else if (!strncmp(tok2, "callee", strlen("callee")))
84 callchain_param.order = ORDER_CALLEE;
85 else
86 return -1;
87
88 /* Get the sort key */
89 tok2 = strtok(NULL, ",");
90 if (!tok2)
91 goto setup;
92 if (!strncmp(tok2, "function", strlen("function")))
93 callchain_param.key = CCKEY_FUNCTION;
94 else if (!strncmp(tok2, "address", strlen("address")))
95 callchain_param.key = CCKEY_ADDRESS;
96 else
97 return -1;
98setup:
99 if (callchain_register_param(&callchain_param) < 0) {
100 pr_err("Can't register callchain params\n");
101 return -1;
102 }
103 return 0;
104}
105
28static void 106static void
29rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, 107rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
30 enum chain_mode mode) 108 enum chain_mode mode)
@@ -538,7 +616,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
538 if (sample->callchain == NULL) 616 if (sample->callchain == NULL)
539 return 0; 617 return 0;
540 618
541 if (symbol_conf.use_callchain || sort__has_parent) { 619 if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
620 sort__has_parent) {
542 return machine__resolve_callchain(al->machine, evsel, al->thread, 621 return machine__resolve_callchain(al->machine, evsel, al->thread,
543 sample, parent, al, max_stack); 622 sample, parent, al, max_stack);
544 } 623 }
@@ -551,3 +630,45 @@ int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *samp
551 return 0; 630 return 0;
552 return callchain_append(he->callchain, &callchain_cursor, sample->period); 631 return callchain_append(he->callchain, &callchain_cursor, sample->period);
553} 632}
633
634int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
635 bool hide_unresolved)
636{
637 al->map = node->map;
638 al->sym = node->sym;
639 if (node->map)
640 al->addr = node->map->map_ip(node->map, node->ip);
641 else
642 al->addr = node->ip;
643
644 if (al->sym == NULL) {
645 if (hide_unresolved)
646 return 0;
647 if (al->map == NULL)
648 goto out;
649 }
650
651 if (al->map->groups == &al->machine->kmaps) {
652 if (machine__is_host(al->machine)) {
653 al->cpumode = PERF_RECORD_MISC_KERNEL;
654 al->level = 'k';
655 } else {
656 al->cpumode = PERF_RECORD_MISC_GUEST_KERNEL;
657 al->level = 'g';
658 }
659 } else {
660 if (machine__is_host(al->machine)) {
661 al->cpumode = PERF_RECORD_MISC_USER;
662 al->level = '.';
663 } else if (perf_guest) {
664 al->cpumode = PERF_RECORD_MISC_GUEST_USER;
665 al->level = 'u';
666 } else {
667 al->cpumode = PERF_RECORD_MISC_HYPERVISOR;
668 al->level = 'H';
669 }
670 }
671
672out:
673 return 1;
674}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 8ad97e9b119f..8f84423a75da 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -7,6 +7,13 @@
7#include "event.h" 7#include "event.h"
8#include "symbol.h" 8#include "symbol.h"
9 9
10enum perf_call_graph_mode {
11 CALLCHAIN_NONE,
12 CALLCHAIN_FP,
13 CALLCHAIN_DWARF,
14 CALLCHAIN_MAX
15};
16
10enum chain_mode { 17enum chain_mode {
11 CHAIN_NONE, 18 CHAIN_NONE,
12 CHAIN_FLAT, 19 CHAIN_FLAT,
@@ -155,6 +162,18 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
155 struct perf_evsel *evsel, struct addr_location *al, 162 struct perf_evsel *evsel, struct addr_location *al,
156 int max_stack); 163 int max_stack);
157int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); 164int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample);
165int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
166 bool hide_unresolved);
158 167
159extern const char record_callchain_help[]; 168extern const char record_callchain_help[];
169int parse_callchain_report_opt(const char *arg);
170
171static inline void callchain_cursor_snapshot(struct callchain_cursor *dest,
172 struct callchain_cursor *src)
173{
174 *dest = *src;
175
176 dest->first = src->curr;
177 dest->nr -= src->pos;
178}
160#endif /* __PERF_CALLCHAIN_H */ 179#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 3e0fdd369ccb..24519e14ac56 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -11,6 +11,7 @@
11#include "util.h" 11#include "util.h"
12#include "cache.h" 12#include "cache.h"
13#include "exec_cmd.h" 13#include "exec_cmd.h"
14#include "util/hist.h" /* perf_hist_config */
14 15
15#define MAXNAME (256) 16#define MAXNAME (256)
16 17
@@ -355,6 +356,9 @@ int perf_default_config(const char *var, const char *value,
355 if (!prefixcmp(var, "core.")) 356 if (!prefixcmp(var, "core."))
356 return perf_default_core_config(var, value); 357 return perf_default_core_config(var, value);
357 358
359 if (!prefixcmp(var, "hist."))
360 return perf_hist_config(var, value);
361
358 /* Add other config variables here. */ 362 /* Add other config variables here. */
359 return 0; 363 return 0;
360} 364}
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 7fe4994eeb63..c4e55b71010c 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -317,3 +317,163 @@ int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep)
317{ 317{
318 return cpu_map__build_map(cpus, corep, cpu_map__get_core); 318 return cpu_map__build_map(cpus, corep, cpu_map__get_core);
319} 319}
320
321/* setup simple routines to easily access node numbers given a cpu number */
322static int get_max_num(char *path, int *max)
323{
324 size_t num;
325 char *buf;
326 int err = 0;
327
328 if (filename__read_str(path, &buf, &num))
329 return -1;
330
331 buf[num] = '\0';
332
333 /* start on the right, to find highest node num */
334 while (--num) {
335 if ((buf[num] == ',') || (buf[num] == '-')) {
336 num++;
337 break;
338 }
339 }
340 if (sscanf(&buf[num], "%d", max) < 1) {
341 err = -1;
342 goto out;
343 }
344
345 /* convert from 0-based to 1-based */
346 (*max)++;
347
348out:
349 free(buf);
350 return err;
351}
352
353/* Determine highest possible cpu in the system for sparse allocation */
354static void set_max_cpu_num(void)
355{
356 const char *mnt;
357 char path[PATH_MAX];
358 int ret = -1;
359
360 /* set up default */
361 max_cpu_num = 4096;
362
363 mnt = sysfs__mountpoint();
364 if (!mnt)
365 goto out;
366
367 /* get the highest possible cpu number for a sparse allocation */
368 ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/possible", mnt);
369 if (ret == PATH_MAX) {
370 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
371 goto out;
372 }
373
374 ret = get_max_num(path, &max_cpu_num);
375
376out:
377 if (ret)
378 pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num);
379}
380
381/* Determine highest possible node in the system for sparse allocation */
382static void set_max_node_num(void)
383{
384 const char *mnt;
385 char path[PATH_MAX];
386 int ret = -1;
387
388 /* set up default */
389 max_node_num = 8;
390
391 mnt = sysfs__mountpoint();
392 if (!mnt)
393 goto out;
394
395 /* get the highest possible cpu number for a sparse allocation */
396 ret = snprintf(path, PATH_MAX, "%s/devices/system/node/possible", mnt);
397 if (ret == PATH_MAX) {
398 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
399 goto out;
400 }
401
402 ret = get_max_num(path, &max_node_num);
403
404out:
405 if (ret)
406 pr_err("Failed to read max nodes, using default of %d\n", max_node_num);
407}
408
409static int init_cpunode_map(void)
410{
411 int i;
412
413 set_max_cpu_num();
414 set_max_node_num();
415
416 cpunode_map = calloc(max_cpu_num, sizeof(int));
417 if (!cpunode_map) {
418 pr_err("%s: calloc failed\n", __func__);
419 return -1;
420 }
421
422 for (i = 0; i < max_cpu_num; i++)
423 cpunode_map[i] = -1;
424
425 return 0;
426}
427
428int cpu__setup_cpunode_map(void)
429{
430 struct dirent *dent1, *dent2;
431 DIR *dir1, *dir2;
432 unsigned int cpu, mem;
433 char buf[PATH_MAX];
434 char path[PATH_MAX];
435 const char *mnt;
436 int n;
437
438 /* initialize globals */
439 if (init_cpunode_map())
440 return -1;
441
442 mnt = sysfs__mountpoint();
443 if (!mnt)
444 return 0;
445
446 n = snprintf(path, PATH_MAX, "%s/devices/system/node", mnt);
447 if (n == PATH_MAX) {
448 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
449 return -1;
450 }
451
452 dir1 = opendir(path);
453 if (!dir1)
454 return 0;
455
456 /* walk tree and setup map */
457 while ((dent1 = readdir(dir1)) != NULL) {
458 if (dent1->d_type != DT_DIR || sscanf(dent1->d_name, "node%u", &mem) < 1)
459 continue;
460
461 n = snprintf(buf, PATH_MAX, "%s/%s", path, dent1->d_name);
462 if (n == PATH_MAX) {
463 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
464 continue;
465 }
466
467 dir2 = opendir(buf);
468 if (!dir2)
469 continue;
470 while ((dent2 = readdir(dir2)) != NULL) {
471 if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1)
472 continue;
473 cpunode_map[cpu] = mem;
474 }
475 closedir(dir2);
476 }
477 closedir(dir1);
478 return 0;
479}
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index b123bb9d6f55..61a654849002 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -4,6 +4,9 @@
4#include <stdio.h> 4#include <stdio.h>
5#include <stdbool.h> 5#include <stdbool.h>
6 6
7#include "perf.h"
8#include "util/debug.h"
9
7struct cpu_map { 10struct cpu_map {
8 int nr; 11 int nr;
9 int map[]; 12 int map[];
@@ -46,4 +49,36 @@ static inline bool cpu_map__empty(const struct cpu_map *map)
46 return map ? map->map[0] == -1 : true; 49 return map ? map->map[0] == -1 : true;
47} 50}
48 51
52int max_cpu_num;
53int max_node_num;
54int *cpunode_map;
55
56int cpu__setup_cpunode_map(void);
57
58static inline int cpu__max_node(void)
59{
60 if (unlikely(!max_node_num))
61 pr_debug("cpu_map not initialized\n");
62
63 return max_node_num;
64}
65
66static inline int cpu__max_cpu(void)
67{
68 if (unlikely(!max_cpu_num))
69 pr_debug("cpu_map not initialized\n");
70
71 return max_cpu_num;
72}
73
74static inline int cpu__get_node(int cpu)
75{
76 if (unlikely(cpunode_map == NULL)) {
77 pr_debug("cpu_map not initialized\n");
78 return -1;
79 }
80
81 return cpunode_map[cpu];
82}
83
49#endif /* __PERF_CPUMAP_H */ 84#endif /* __PERF_CPUMAP_H */
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index ab06f1c03655..38efe95a7fdd 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -4,7 +4,7 @@
4#include <linux/types.h> 4#include <linux/types.h>
5#include <linux/rbtree.h> 5#include <linux/rbtree.h>
6#include <stdbool.h> 6#include <stdbool.h>
7#include "types.h" 7#include <linux/types.h>
8#include "map.h" 8#include "map.h"
9#include "build-id.h" 9#include "build-id.h"
10 10
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 9d12aa6dd485..65795b835b39 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -699,7 +699,7 @@ void thread__find_addr_map(struct thread *thread,
699 enum map_type type, u64 addr, 699 enum map_type type, u64 addr,
700 struct addr_location *al) 700 struct addr_location *al)
701{ 701{
702 struct map_groups *mg = &thread->mg; 702 struct map_groups *mg = thread->mg;
703 bool load_map = false; 703 bool load_map = false;
704 704
705 al->machine = machine; 705 al->machine = machine;
@@ -788,7 +788,7 @@ int perf_event__preprocess_sample(const union perf_event *event,
788{ 788{
789 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 789 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
790 struct thread *thread = machine__findnew_thread(machine, sample->pid, 790 struct thread *thread = machine__findnew_thread(machine, sample->pid,
791 sample->pid); 791 sample->tid);
792 792
793 if (thread == NULL) 793 if (thread == NULL)
794 return -1; 794 return -1;
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 38457d447a13..d970232cb270 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -112,6 +112,30 @@ struct sample_read {
112 }; 112 };
113}; 113};
114 114
115struct ip_callchain {
116 u64 nr;
117 u64 ips[0];
118};
119
120struct branch_flags {
121 u64 mispred:1;
122 u64 predicted:1;
123 u64 in_tx:1;
124 u64 abort:1;
125 u64 reserved:60;
126};
127
128struct branch_entry {
129 u64 from;
130 u64 to;
131 struct branch_flags flags;
132};
133
134struct branch_stack {
135 u64 nr;
136 struct branch_entry entries[0];
137};
138
115struct perf_sample { 139struct perf_sample {
116 u64 ip; 140 u64 ip;
117 u32 pid, tid; 141 u32 pid, tid;
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 0c9926cfb292..a52e9a5bb2d0 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -5,12 +5,12 @@
5#include <stdbool.h> 5#include <stdbool.h>
6#include <stddef.h> 6#include <stddef.h>
7#include <linux/perf_event.h> 7#include <linux/perf_event.h>
8#include "types.h" 8#include <linux/types.h>
9#include "xyarray.h" 9#include "xyarray.h"
10#include "cgroup.h" 10#include "cgroup.h"
11#include "hist.h" 11#include "hist.h"
12#include "symbol.h" 12#include "symbol.h"
13 13
14struct perf_counts_values { 14struct perf_counts_values {
15 union { 15 union {
16 struct { 16 struct {
@@ -91,6 +91,11 @@ struct perf_evsel {
91 char *group_name; 91 char *group_name;
92}; 92};
93 93
94union u64_swap {
95 u64 val64;
96 u32 val32[2];
97};
98
94#define hists_to_evsel(h) container_of(h, struct perf_evsel, hists) 99#define hists_to_evsel(h) container_of(h, struct perf_evsel, hists)
95 100
96struct cpu_map; 101struct cpu_map;
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index a2d047bdf4ef..d08cfe499404 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -4,10 +4,10 @@
4#include <linux/perf_event.h> 4#include <linux/perf_event.h>
5#include <sys/types.h> 5#include <sys/types.h>
6#include <stdbool.h> 6#include <stdbool.h>
7#include "types.h" 7#include <linux/bitmap.h>
8#include <linux/types.h>
8#include "event.h" 9#include "event.h"
9 10
10#include <linux/bitmap.h>
11 11
12enum { 12enum {
13 HEADER_RESERVED = 0, /* always cleared */ 13 HEADER_RESERVED = 0, /* always cleared */
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index f38590d7561b..5a0a4b2cadc4 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -4,6 +4,7 @@
4#include "session.h" 4#include "session.h"
5#include "sort.h" 5#include "sort.h"
6#include "evsel.h" 6#include "evsel.h"
7#include "annotate.h"
7#include <math.h> 8#include <math.h>
8 9
9static bool hists__filter_entry_by_dso(struct hists *hists, 10static bool hists__filter_entry_by_dso(struct hists *hists,
@@ -225,14 +226,20 @@ static void he_stat__decay(struct he_stat *he_stat)
225static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) 226static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
226{ 227{
227 u64 prev_period = he->stat.period; 228 u64 prev_period = he->stat.period;
229 u64 diff;
228 230
229 if (prev_period == 0) 231 if (prev_period == 0)
230 return true; 232 return true;
231 233
232 he_stat__decay(&he->stat); 234 he_stat__decay(&he->stat);
235 if (symbol_conf.cumulate_callchain)
236 he_stat__decay(he->stat_acc);
233 237
238 diff = prev_period - he->stat.period;
239
240 hists->stats.total_period -= diff;
234 if (!he->filtered) 241 if (!he->filtered)
235 hists->stats.total_period -= prev_period - he->stat.period; 242 hists->stats.total_non_filtered_period -= diff;
236 243
237 return he->stat.period == 0; 244 return he->stat.period == 0;
238} 245}
@@ -259,8 +266,11 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
259 if (sort__need_collapse) 266 if (sort__need_collapse)
260 rb_erase(&n->rb_node_in, &hists->entries_collapsed); 267 rb_erase(&n->rb_node_in, &hists->entries_collapsed);
261 268
262 hist_entry__free(n);
263 --hists->nr_entries; 269 --hists->nr_entries;
270 if (!n->filtered)
271 --hists->nr_non_filtered_entries;
272
273 hist_entry__free(n);
264 } 274 }
265 } 275 }
266} 276}
@@ -269,14 +279,31 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
269 * histogram, sorted on item, collects periods 279 * histogram, sorted on item, collects periods
270 */ 280 */
271 281
272static struct hist_entry *hist_entry__new(struct hist_entry *template) 282static struct hist_entry *hist_entry__new(struct hist_entry *template,
283 bool sample_self)
273{ 284{
274 size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; 285 size_t callchain_size = 0;
275 struct hist_entry *he = zalloc(sizeof(*he) + callchain_size); 286 struct hist_entry *he;
287
288 if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain)
289 callchain_size = sizeof(struct callchain_root);
290
291 he = zalloc(sizeof(*he) + callchain_size);
276 292
277 if (he != NULL) { 293 if (he != NULL) {
278 *he = *template; 294 *he = *template;
279 295
296 if (symbol_conf.cumulate_callchain) {
297 he->stat_acc = malloc(sizeof(he->stat));
298 if (he->stat_acc == NULL) {
299 free(he);
300 return NULL;
301 }
302 memcpy(he->stat_acc, &he->stat, sizeof(he->stat));
303 if (!sample_self)
304 memset(&he->stat, 0, sizeof(he->stat));
305 }
306
280 if (he->ms.map) 307 if (he->ms.map)
281 he->ms.map->referenced = true; 308 he->ms.map->referenced = true;
282 309
@@ -288,6 +315,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
288 */ 315 */
289 he->branch_info = malloc(sizeof(*he->branch_info)); 316 he->branch_info = malloc(sizeof(*he->branch_info));
290 if (he->branch_info == NULL) { 317 if (he->branch_info == NULL) {
318 free(he->stat_acc);
291 free(he); 319 free(he);
292 return NULL; 320 return NULL;
293 } 321 }
@@ -317,15 +345,6 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
317 return he; 345 return he;
318} 346}
319 347
320void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
321{
322 if (!h->filtered) {
323 hists__calc_col_len(hists, h);
324 ++hists->nr_entries;
325 hists->stats.total_period += h->stat.period;
326 }
327}
328
329static u8 symbol__parent_filter(const struct symbol *parent) 348static u8 symbol__parent_filter(const struct symbol *parent)
330{ 349{
331 if (symbol_conf.exclude_other && parent == NULL) 350 if (symbol_conf.exclude_other && parent == NULL)
@@ -335,7 +354,8 @@ static u8 symbol__parent_filter(const struct symbol *parent)
335 354
336static struct hist_entry *add_hist_entry(struct hists *hists, 355static struct hist_entry *add_hist_entry(struct hists *hists,
337 struct hist_entry *entry, 356 struct hist_entry *entry,
338 struct addr_location *al) 357 struct addr_location *al,
358 bool sample_self)
339{ 359{
340 struct rb_node **p; 360 struct rb_node **p;
341 struct rb_node *parent = NULL; 361 struct rb_node *parent = NULL;
@@ -359,7 +379,10 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
359 cmp = hist_entry__cmp(he, entry); 379 cmp = hist_entry__cmp(he, entry);
360 380
361 if (!cmp) { 381 if (!cmp) {
362 he_stat__add_period(&he->stat, period, weight); 382 if (sample_self)
383 he_stat__add_period(&he->stat, period, weight);
384 if (symbol_conf.cumulate_callchain)
385 he_stat__add_period(he->stat_acc, period, weight);
363 386
364 /* 387 /*
365 * This mem info was allocated from sample__resolve_mem 388 * This mem info was allocated from sample__resolve_mem
@@ -387,15 +410,17 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
387 p = &(*p)->rb_right; 410 p = &(*p)->rb_right;
388 } 411 }
389 412
390 he = hist_entry__new(entry); 413 he = hist_entry__new(entry, sample_self);
391 if (!he) 414 if (!he)
392 return NULL; 415 return NULL;
393 416
394 hists->nr_entries++;
395 rb_link_node(&he->rb_node_in, parent, p); 417 rb_link_node(&he->rb_node_in, parent, p);
396 rb_insert_color(&he->rb_node_in, hists->entries_in); 418 rb_insert_color(&he->rb_node_in, hists->entries_in);
397out: 419out:
398 he_stat__add_cpumode_period(&he->stat, al->cpumode, period); 420 if (sample_self)
421 he_stat__add_cpumode_period(&he->stat, al->cpumode, period);
422 if (symbol_conf.cumulate_callchain)
423 he_stat__add_cpumode_period(he->stat_acc, al->cpumode, period);
399 return he; 424 return he;
400} 425}
401 426
@@ -404,7 +429,8 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
404 struct symbol *sym_parent, 429 struct symbol *sym_parent,
405 struct branch_info *bi, 430 struct branch_info *bi,
406 struct mem_info *mi, 431 struct mem_info *mi,
407 u64 period, u64 weight, u64 transaction) 432 u64 period, u64 weight, u64 transaction,
433 bool sample_self)
408{ 434{
409 struct hist_entry entry = { 435 struct hist_entry entry = {
410 .thread = al->thread, 436 .thread = al->thread,
@@ -429,17 +455,442 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
429 .transaction = transaction, 455 .transaction = transaction,
430 }; 456 };
431 457
432 return add_hist_entry(hists, &entry, al); 458 return add_hist_entry(hists, &entry, al, sample_self);
459}
460
461static int
462iter_next_nop_entry(struct hist_entry_iter *iter __maybe_unused,
463 struct addr_location *al __maybe_unused)
464{
465 return 0;
466}
467
468static int
469iter_add_next_nop_entry(struct hist_entry_iter *iter __maybe_unused,
470 struct addr_location *al __maybe_unused)
471{
472 return 0;
473}
474
475static int
476iter_prepare_mem_entry(struct hist_entry_iter *iter, struct addr_location *al)
477{
478 struct perf_sample *sample = iter->sample;
479 struct mem_info *mi;
480
481 mi = sample__resolve_mem(sample, al);
482 if (mi == NULL)
483 return -ENOMEM;
484
485 iter->priv = mi;
486 return 0;
487}
488
489static int
490iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al)
491{
492 u64 cost;
493 struct mem_info *mi = iter->priv;
494 struct hist_entry *he;
495
496 if (mi == NULL)
497 return -EINVAL;
498
499 cost = iter->sample->weight;
500 if (!cost)
501 cost = 1;
502
503 /*
504 * must pass period=weight in order to get the correct
505 * sorting from hists__collapse_resort() which is solely
506 * based on periods. We want sorting be done on nr_events * weight
507 * and this is indirectly achieved by passing period=weight here
508 * and the he_stat__add_period() function.
509 */
510 he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi,
511 cost, cost, 0, true);
512 if (!he)
513 return -ENOMEM;
514
515 iter->he = he;
516 return 0;
517}
518
519static int
520iter_finish_mem_entry(struct hist_entry_iter *iter,
521 struct addr_location *al __maybe_unused)
522{
523 struct perf_evsel *evsel = iter->evsel;
524 struct hist_entry *he = iter->he;
525 int err = -EINVAL;
526
527 if (he == NULL)
528 goto out;
529
530 hists__inc_nr_samples(&evsel->hists, he->filtered);
531
532 err = hist_entry__append_callchain(he, iter->sample);
533
534out:
535 /*
536 * We don't need to free iter->priv (mem_info) here since
537 * the mem info was either already freed in add_hist_entry() or
538 * passed to a new hist entry by hist_entry__new().
539 */
540 iter->priv = NULL;
541
542 iter->he = NULL;
543 return err;
544}
545
546static int
547iter_prepare_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
548{
549 struct branch_info *bi;
550 struct perf_sample *sample = iter->sample;
551
552 bi = sample__resolve_bstack(sample, al);
553 if (!bi)
554 return -ENOMEM;
555
556 iter->curr = 0;
557 iter->total = sample->branch_stack->nr;
558
559 iter->priv = bi;
560 return 0;
561}
562
563static int
564iter_add_single_branch_entry(struct hist_entry_iter *iter __maybe_unused,
565 struct addr_location *al __maybe_unused)
566{
567 /* to avoid calling callback function */
568 iter->he = NULL;
569
570 return 0;
571}
572
573static int
574iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
575{
576 struct branch_info *bi = iter->priv;
577 int i = iter->curr;
578
579 if (bi == NULL)
580 return 0;
581
582 if (iter->curr >= iter->total)
583 return 0;
584
585 al->map = bi[i].to.map;
586 al->sym = bi[i].to.sym;
587 al->addr = bi[i].to.addr;
588 return 1;
589}
590
591static int
592iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
593{
594 struct branch_info *bi;
595 struct perf_evsel *evsel = iter->evsel;
596 struct hist_entry *he = NULL;
597 int i = iter->curr;
598 int err = 0;
599
600 bi = iter->priv;
601
602 if (iter->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
603 goto out;
604
605 /*
606 * The report shows the percentage of total branches captured
607 * and not events sampled. Thus we use a pseudo period of 1.
608 */
609 he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL,
610 1, 1, 0, true);
611 if (he == NULL)
612 return -ENOMEM;
613
614 hists__inc_nr_samples(&evsel->hists, he->filtered);
615
616out:
617 iter->he = he;
618 iter->curr++;
619 return err;
620}
621
622static int
623iter_finish_branch_entry(struct hist_entry_iter *iter,
624 struct addr_location *al __maybe_unused)
625{
626 zfree(&iter->priv);
627 iter->he = NULL;
628
629 return iter->curr >= iter->total ? 0 : -1;
630}
631
632static int
633iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused,
634 struct addr_location *al __maybe_unused)
635{
636 return 0;
637}
638
639static int
640iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location *al)
641{
642 struct perf_evsel *evsel = iter->evsel;
643 struct perf_sample *sample = iter->sample;
644 struct hist_entry *he;
645
646 he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
647 sample->period, sample->weight,
648 sample->transaction, true);
649 if (he == NULL)
650 return -ENOMEM;
651
652 iter->he = he;
653 return 0;
654}
655
656static int
657iter_finish_normal_entry(struct hist_entry_iter *iter,
658 struct addr_location *al __maybe_unused)
659{
660 struct hist_entry *he = iter->he;
661 struct perf_evsel *evsel = iter->evsel;
662 struct perf_sample *sample = iter->sample;
663
664 if (he == NULL)
665 return 0;
666
667 iter->he = NULL;
668
669 hists__inc_nr_samples(&evsel->hists, he->filtered);
670
671 return hist_entry__append_callchain(he, sample);
672}
673
674static int
675iter_prepare_cumulative_entry(struct hist_entry_iter *iter __maybe_unused,
676 struct addr_location *al __maybe_unused)
677{
678 struct hist_entry **he_cache;
679
680 callchain_cursor_commit(&callchain_cursor);
681
682 /*
683 * This is for detecting cycles or recursions so that they're
684 * cumulated only one time to prevent entries more than 100%
685 * overhead.
686 */
687 he_cache = malloc(sizeof(*he_cache) * (PERF_MAX_STACK_DEPTH + 1));
688 if (he_cache == NULL)
689 return -ENOMEM;
690
691 iter->priv = he_cache;
692 iter->curr = 0;
693
694 return 0;
695}
696
697static int
698iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
699 struct addr_location *al)
700{
701 struct perf_evsel *evsel = iter->evsel;
702 struct perf_sample *sample = iter->sample;
703 struct hist_entry **he_cache = iter->priv;
704 struct hist_entry *he;
705 int err = 0;
706
707 he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
708 sample->period, sample->weight,
709 sample->transaction, true);
710 if (he == NULL)
711 return -ENOMEM;
712
713 iter->he = he;
714 he_cache[iter->curr++] = he;
715
716 callchain_append(he->callchain, &callchain_cursor, sample->period);
717
718 /*
719 * We need to re-initialize the cursor since callchain_append()
720 * advanced the cursor to the end.
721 */
722 callchain_cursor_commit(&callchain_cursor);
723
724 hists__inc_nr_samples(&evsel->hists, he->filtered);
725
726 return err;
727}
728
729static int
730iter_next_cumulative_entry(struct hist_entry_iter *iter,
731 struct addr_location *al)
732{
733 struct callchain_cursor_node *node;
734
735 node = callchain_cursor_current(&callchain_cursor);
736 if (node == NULL)
737 return 0;
738
739 return fill_callchain_info(al, node, iter->hide_unresolved);
740}
741
742static int
743iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
744 struct addr_location *al)
745{
746 struct perf_evsel *evsel = iter->evsel;
747 struct perf_sample *sample = iter->sample;
748 struct hist_entry **he_cache = iter->priv;
749 struct hist_entry *he;
750 struct hist_entry he_tmp = {
751 .cpu = al->cpu,
752 .thread = al->thread,
753 .comm = thread__comm(al->thread),
754 .ip = al->addr,
755 .ms = {
756 .map = al->map,
757 .sym = al->sym,
758 },
759 .parent = iter->parent,
760 };
761 int i;
762 struct callchain_cursor cursor;
763
764 callchain_cursor_snapshot(&cursor, &callchain_cursor);
765
766 callchain_cursor_advance(&callchain_cursor);
767
768 /*
769 * Check if there's duplicate entries in the callchain.
770 * It's possible that it has cycles or recursive calls.
771 */
772 for (i = 0; i < iter->curr; i++) {
773 if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) {
774 /* to avoid calling callback function */
775 iter->he = NULL;
776 return 0;
777 }
778 }
779
780 he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
781 sample->period, sample->weight,
782 sample->transaction, false);
783 if (he == NULL)
784 return -ENOMEM;
785
786 iter->he = he;
787 he_cache[iter->curr++] = he;
788
789 callchain_append(he->callchain, &cursor, sample->period);
790 return 0;
791}
792
793static int
794iter_finish_cumulative_entry(struct hist_entry_iter *iter,
795 struct addr_location *al __maybe_unused)
796{
797 zfree(&iter->priv);
798 iter->he = NULL;
799
800 return 0;
801}
802
803const struct hist_iter_ops hist_iter_mem = {
804 .prepare_entry = iter_prepare_mem_entry,
805 .add_single_entry = iter_add_single_mem_entry,
806 .next_entry = iter_next_nop_entry,
807 .add_next_entry = iter_add_next_nop_entry,
808 .finish_entry = iter_finish_mem_entry,
809};
810
811const struct hist_iter_ops hist_iter_branch = {
812 .prepare_entry = iter_prepare_branch_entry,
813 .add_single_entry = iter_add_single_branch_entry,
814 .next_entry = iter_next_branch_entry,
815 .add_next_entry = iter_add_next_branch_entry,
816 .finish_entry = iter_finish_branch_entry,
817};
818
819const struct hist_iter_ops hist_iter_normal = {
820 .prepare_entry = iter_prepare_normal_entry,
821 .add_single_entry = iter_add_single_normal_entry,
822 .next_entry = iter_next_nop_entry,
823 .add_next_entry = iter_add_next_nop_entry,
824 .finish_entry = iter_finish_normal_entry,
825};
826
827const struct hist_iter_ops hist_iter_cumulative = {
828 .prepare_entry = iter_prepare_cumulative_entry,
829 .add_single_entry = iter_add_single_cumulative_entry,
830 .next_entry = iter_next_cumulative_entry,
831 .add_next_entry = iter_add_next_cumulative_entry,
832 .finish_entry = iter_finish_cumulative_entry,
833};
834
835int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
836 struct perf_evsel *evsel, struct perf_sample *sample,
837 int max_stack_depth, void *arg)
838{
839 int err, err2;
840
841 err = sample__resolve_callchain(sample, &iter->parent, evsel, al,
842 max_stack_depth);
843 if (err)
844 return err;
845
846 iter->evsel = evsel;
847 iter->sample = sample;
848
849 err = iter->ops->prepare_entry(iter, al);
850 if (err)
851 goto out;
852
853 err = iter->ops->add_single_entry(iter, al);
854 if (err)
855 goto out;
856
857 if (iter->he && iter->add_entry_cb) {
858 err = iter->add_entry_cb(iter, al, true, arg);
859 if (err)
860 goto out;
861 }
862
863 while (iter->ops->next_entry(iter, al)) {
864 err = iter->ops->add_next_entry(iter, al);
865 if (err)
866 break;
867
868 if (iter->he && iter->add_entry_cb) {
869 err = iter->add_entry_cb(iter, al, false, arg);
870 if (err)
871 goto out;
872 }
873 }
874
875out:
876 err2 = iter->ops->finish_entry(iter, al);
877 if (!err)
878 err = err2;
879
880 return err;
433} 881}
434 882
435int64_t 883int64_t
436hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) 884hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
437{ 885{
438 struct sort_entry *se; 886 struct perf_hpp_fmt *fmt;
439 int64_t cmp = 0; 887 int64_t cmp = 0;
440 888
441 list_for_each_entry(se, &hist_entry__sort_list, list) { 889 perf_hpp__for_each_sort_list(fmt) {
442 cmp = se->se_cmp(left, right); 890 if (perf_hpp__should_skip(fmt))
891 continue;
892
893 cmp = fmt->cmp(left, right);
443 if (cmp) 894 if (cmp)
444 break; 895 break;
445 } 896 }
@@ -450,15 +901,14 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
450int64_t 901int64_t
451hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) 902hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
452{ 903{
453 struct sort_entry *se; 904 struct perf_hpp_fmt *fmt;
454 int64_t cmp = 0; 905 int64_t cmp = 0;
455 906
456 list_for_each_entry(se, &hist_entry__sort_list, list) { 907 perf_hpp__for_each_sort_list(fmt) {
457 int64_t (*f)(struct hist_entry *, struct hist_entry *); 908 if (perf_hpp__should_skip(fmt))
458 909 continue;
459 f = se->se_collapse ?: se->se_cmp;
460 910
461 cmp = f(left, right); 911 cmp = fmt->collapse(left, right);
462 if (cmp) 912 if (cmp)
463 break; 913 break;
464 } 914 }
@@ -470,6 +920,7 @@ void hist_entry__free(struct hist_entry *he)
470{ 920{
471 zfree(&he->branch_info); 921 zfree(&he->branch_info);
472 zfree(&he->mem_info); 922 zfree(&he->mem_info);
923 zfree(&he->stat_acc);
473 free_srcline(he->srcline); 924 free_srcline(he->srcline);
474 free(he); 925 free(he);
475} 926}
@@ -495,6 +946,8 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
495 946
496 if (!cmp) { 947 if (!cmp) {
497 he_stat__add_stat(&iter->stat, &he->stat); 948 he_stat__add_stat(&iter->stat, &he->stat);
949 if (symbol_conf.cumulate_callchain)
950 he_stat__add_stat(iter->stat_acc, he->stat_acc);
498 951
499 if (symbol_conf.use_callchain) { 952 if (symbol_conf.use_callchain) {
500 callchain_cursor_reset(&callchain_cursor); 953 callchain_cursor_reset(&callchain_cursor);
@@ -571,64 +1024,50 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
571 } 1024 }
572} 1025}
573 1026
574/* 1027static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
575 * reverse the map, sort on period.
576 */
577
578static int period_cmp(u64 period_a, u64 period_b)
579{
580 if (period_a > period_b)
581 return 1;
582 if (period_a < period_b)
583 return -1;
584 return 0;
585}
586
587static int hist_entry__sort_on_period(struct hist_entry *a,
588 struct hist_entry *b)
589{ 1028{
590 int ret; 1029 struct perf_hpp_fmt *fmt;
591 int i, nr_members; 1030 int64_t cmp = 0;
592 struct perf_evsel *evsel;
593 struct hist_entry *pair;
594 u64 *periods_a, *periods_b;
595 1031
596 ret = period_cmp(a->stat.period, b->stat.period); 1032 perf_hpp__for_each_sort_list(fmt) {
597 if (ret || !symbol_conf.event_group) 1033 if (perf_hpp__should_skip(fmt))
598 return ret; 1034 continue;
599 1035
600 evsel = hists_to_evsel(a->hists); 1036 cmp = fmt->sort(a, b);
601 nr_members = evsel->nr_members; 1037 if (cmp)
602 if (nr_members <= 1) 1038 break;
603 return ret; 1039 }
604 1040
605 periods_a = zalloc(sizeof(periods_a) * nr_members); 1041 return cmp;
606 periods_b = zalloc(sizeof(periods_b) * nr_members); 1042}
607 1043
608 if (!periods_a || !periods_b) 1044static void hists__reset_filter_stats(struct hists *hists)
609 goto out; 1045{
1046 hists->nr_non_filtered_entries = 0;
1047 hists->stats.total_non_filtered_period = 0;
1048}
610 1049
611 list_for_each_entry(pair, &a->pairs.head, pairs.node) { 1050void hists__reset_stats(struct hists *hists)
612 evsel = hists_to_evsel(pair->hists); 1051{
613 periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period; 1052 hists->nr_entries = 0;
614 } 1053 hists->stats.total_period = 0;
615 1054
616 list_for_each_entry(pair, &b->pairs.head, pairs.node) { 1055 hists__reset_filter_stats(hists);
617 evsel = hists_to_evsel(pair->hists); 1056}
618 periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period;
619 }
620 1057
621 for (i = 1; i < nr_members; i++) { 1058static void hists__inc_filter_stats(struct hists *hists, struct hist_entry *h)
622 ret = period_cmp(periods_a[i], periods_b[i]); 1059{
623 if (ret) 1060 hists->nr_non_filtered_entries++;
624 break; 1061 hists->stats.total_non_filtered_period += h->stat.period;
625 } 1062}
626 1063
627out: 1064void hists__inc_stats(struct hists *hists, struct hist_entry *h)
628 free(periods_a); 1065{
629 free(periods_b); 1066 if (!h->filtered)
1067 hists__inc_filter_stats(hists, h);
630 1068
631 return ret; 1069 hists->nr_entries++;
1070 hists->stats.total_period += h->stat.period;
632} 1071}
633 1072
634static void __hists__insert_output_entry(struct rb_root *entries, 1073static void __hists__insert_output_entry(struct rb_root *entries,
@@ -647,7 +1086,7 @@ static void __hists__insert_output_entry(struct rb_root *entries,
647 parent = *p; 1086 parent = *p;
648 iter = rb_entry(parent, struct hist_entry, rb_node); 1087 iter = rb_entry(parent, struct hist_entry, rb_node);
649 1088
650 if (hist_entry__sort_on_period(he, iter) > 0) 1089 if (hist_entry__sort(he, iter) > 0)
651 p = &(*p)->rb_left; 1090 p = &(*p)->rb_left;
652 else 1091 else
653 p = &(*p)->rb_right; 1092 p = &(*p)->rb_right;
@@ -674,8 +1113,7 @@ void hists__output_resort(struct hists *hists)
674 next = rb_first(root); 1113 next = rb_first(root);
675 hists->entries = RB_ROOT; 1114 hists->entries = RB_ROOT;
676 1115
677 hists->nr_entries = 0; 1116 hists__reset_stats(hists);
678 hists->stats.total_period = 0;
679 hists__reset_col_len(hists); 1117 hists__reset_col_len(hists);
680 1118
681 while (next) { 1119 while (next) {
@@ -683,7 +1121,10 @@ void hists__output_resort(struct hists *hists)
683 next = rb_next(&n->rb_node_in); 1121 next = rb_next(&n->rb_node_in);
684 1122
685 __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); 1123 __hists__insert_output_entry(&hists->entries, n, min_callchain_hits);
686 hists__inc_nr_entries(hists, n); 1124 hists__inc_stats(hists, n);
1125
1126 if (!n->filtered)
1127 hists__calc_col_len(hists, n);
687 } 1128 }
688} 1129}
689 1130
@@ -694,13 +1135,13 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h
694 if (h->filtered) 1135 if (h->filtered)
695 return; 1136 return;
696 1137
697 ++hists->nr_entries; 1138 /* force fold unfiltered entry for simplicity */
698 if (h->ms.unfolded) 1139 h->ms.unfolded = false;
699 hists->nr_entries += h->nr_rows;
700 h->row_offset = 0; 1140 h->row_offset = 0;
701 hists->stats.total_period += h->stat.period;
702 hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events;
703 1141
1142 hists->stats.nr_non_filtered_samples += h->stat.nr_events;
1143
1144 hists__inc_filter_stats(hists, h);
704 hists__calc_col_len(hists, h); 1145 hists__calc_col_len(hists, h);
705} 1146}
706 1147
@@ -721,8 +1162,9 @@ void hists__filter_by_dso(struct hists *hists)
721{ 1162{
722 struct rb_node *nd; 1163 struct rb_node *nd;
723 1164
724 hists->nr_entries = hists->stats.total_period = 0; 1165 hists->stats.nr_non_filtered_samples = 0;
725 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 1166
1167 hists__reset_filter_stats(hists);
726 hists__reset_col_len(hists); 1168 hists__reset_col_len(hists);
727 1169
728 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 1170 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
@@ -754,8 +1196,9 @@ void hists__filter_by_thread(struct hists *hists)
754{ 1196{
755 struct rb_node *nd; 1197 struct rb_node *nd;
756 1198
757 hists->nr_entries = hists->stats.total_period = 0; 1199 hists->stats.nr_non_filtered_samples = 0;
758 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 1200
1201 hists__reset_filter_stats(hists);
759 hists__reset_col_len(hists); 1202 hists__reset_col_len(hists);
760 1203
761 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 1204 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
@@ -785,8 +1228,9 @@ void hists__filter_by_symbol(struct hists *hists)
785{ 1228{
786 struct rb_node *nd; 1229 struct rb_node *nd;
787 1230
788 hists->nr_entries = hists->stats.total_period = 0; 1231 hists->stats.nr_non_filtered_samples = 0;
789 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 1232
1233 hists__reset_filter_stats(hists);
790 hists__reset_col_len(hists); 1234 hists__reset_col_len(hists);
791 1235
792 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 1236 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
@@ -810,6 +1254,13 @@ void hists__inc_nr_events(struct hists *hists, u32 type)
810 events_stats__inc(&hists->stats, type); 1254 events_stats__inc(&hists->stats, type);
811} 1255}
812 1256
1257void hists__inc_nr_samples(struct hists *hists, bool filtered)
1258{
1259 events_stats__inc(&hists->stats, PERF_RECORD_SAMPLE);
1260 if (!filtered)
1261 hists->stats.nr_non_filtered_samples++;
1262}
1263
813static struct hist_entry *hists__add_dummy_entry(struct hists *hists, 1264static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
814 struct hist_entry *pair) 1265 struct hist_entry *pair)
815{ 1266{
@@ -841,13 +1292,13 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
841 p = &(*p)->rb_right; 1292 p = &(*p)->rb_right;
842 } 1293 }
843 1294
844 he = hist_entry__new(pair); 1295 he = hist_entry__new(pair, true);
845 if (he) { 1296 if (he) {
846 memset(&he->stat, 0, sizeof(he->stat)); 1297 memset(&he->stat, 0, sizeof(he->stat));
847 he->hists = hists; 1298 he->hists = hists;
848 rb_link_node(&he->rb_node_in, parent, p); 1299 rb_link_node(&he->rb_node_in, parent, p);
849 rb_insert_color(&he->rb_node_in, root); 1300 rb_insert_color(&he->rb_node_in, root);
850 hists__inc_nr_entries(hists, he); 1301 hists__inc_stats(hists, he);
851 he->dummy = true; 1302 he->dummy = true;
852 } 1303 }
853out: 1304out:
@@ -931,3 +1382,30 @@ int hists__link(struct hists *leader, struct hists *other)
931 1382
932 return 0; 1383 return 0;
933} 1384}
1385
1386u64 hists__total_period(struct hists *hists)
1387{
1388 return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period :
1389 hists->stats.total_period;
1390}
1391
1392int parse_filter_percentage(const struct option *opt __maybe_unused,
1393 const char *arg, int unset __maybe_unused)
1394{
1395 if (!strcmp(arg, "relative"))
1396 symbol_conf.filter_relative = true;
1397 else if (!strcmp(arg, "absolute"))
1398 symbol_conf.filter_relative = false;
1399 else
1400 return -1;
1401
1402 return 0;
1403}
1404
1405int perf_hist_config(const char *var, const char *value)
1406{
1407 if (!strcmp(var, "hist.percentage"))
1408 return parse_filter_percentage(NULL, value, 0);
1409
1410 return 0;
1411}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 1f1f513dfe7f..d2bf03575d5f 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -37,9 +37,11 @@ enum hist_filter {
37 */ 37 */
38struct events_stats { 38struct events_stats {
39 u64 total_period; 39 u64 total_period;
40 u64 total_non_filtered_period;
40 u64 total_lost; 41 u64 total_lost;
41 u64 total_invalid_chains; 42 u64 total_invalid_chains;
42 u32 nr_events[PERF_RECORD_HEADER_MAX]; 43 u32 nr_events[PERF_RECORD_HEADER_MAX];
44 u32 nr_non_filtered_samples;
43 u32 nr_lost_warned; 45 u32 nr_lost_warned;
44 u32 nr_unknown_events; 46 u32 nr_unknown_events;
45 u32 nr_invalid_chains; 47 u32 nr_invalid_chains;
@@ -83,6 +85,7 @@ struct hists {
83 struct rb_root entries; 85 struct rb_root entries;
84 struct rb_root entries_collapsed; 86 struct rb_root entries_collapsed;
85 u64 nr_entries; 87 u64 nr_entries;
88 u64 nr_non_filtered_entries;
86 const struct thread *thread_filter; 89 const struct thread *thread_filter;
87 const struct dso *dso_filter; 90 const struct dso *dso_filter;
88 const char *uid_filter_str; 91 const char *uid_filter_str;
@@ -93,12 +96,50 @@ struct hists {
93 u16 col_len[HISTC_NR_COLS]; 96 u16 col_len[HISTC_NR_COLS];
94}; 97};
95 98
99struct hist_entry_iter;
100
101struct hist_iter_ops {
102 int (*prepare_entry)(struct hist_entry_iter *, struct addr_location *);
103 int (*add_single_entry)(struct hist_entry_iter *, struct addr_location *);
104 int (*next_entry)(struct hist_entry_iter *, struct addr_location *);
105 int (*add_next_entry)(struct hist_entry_iter *, struct addr_location *);
106 int (*finish_entry)(struct hist_entry_iter *, struct addr_location *);
107};
108
109struct hist_entry_iter {
110 int total;
111 int curr;
112
113 bool hide_unresolved;
114
115 struct perf_evsel *evsel;
116 struct perf_sample *sample;
117 struct hist_entry *he;
118 struct symbol *parent;
119 void *priv;
120
121 const struct hist_iter_ops *ops;
122 /* user-defined callback function (optional) */
123 int (*add_entry_cb)(struct hist_entry_iter *iter,
124 struct addr_location *al, bool single, void *arg);
125};
126
127extern const struct hist_iter_ops hist_iter_normal;
128extern const struct hist_iter_ops hist_iter_branch;
129extern const struct hist_iter_ops hist_iter_mem;
130extern const struct hist_iter_ops hist_iter_cumulative;
131
96struct hist_entry *__hists__add_entry(struct hists *hists, 132struct hist_entry *__hists__add_entry(struct hists *hists,
97 struct addr_location *al, 133 struct addr_location *al,
98 struct symbol *parent, 134 struct symbol *parent,
99 struct branch_info *bi, 135 struct branch_info *bi,
100 struct mem_info *mi, u64 period, 136 struct mem_info *mi, u64 period,
101 u64 weight, u64 transaction); 137 u64 weight, u64 transaction,
138 bool sample_self);
139int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
140 struct perf_evsel *evsel, struct perf_sample *sample,
141 int max_stack_depth, void *arg);
142
102int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); 143int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right);
103int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); 144int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right);
104int hist_entry__transaction_len(void); 145int hist_entry__transaction_len(void);
@@ -112,8 +153,11 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog);
112void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); 153void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel);
113void hists__output_recalc_col_len(struct hists *hists, int max_rows); 154void hists__output_recalc_col_len(struct hists *hists, int max_rows);
114 155
115void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h); 156u64 hists__total_period(struct hists *hists);
157void hists__reset_stats(struct hists *hists);
158void hists__inc_stats(struct hists *hists, struct hist_entry *h);
116void hists__inc_nr_events(struct hists *hists, u32 type); 159void hists__inc_nr_events(struct hists *hists, u32 type);
160void hists__inc_nr_samples(struct hists *hists, bool filtered);
117void events_stats__inc(struct events_stats *stats, u32 type); 161void events_stats__inc(struct events_stats *stats, u32 type);
118size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); 162size_t events_stats__fprintf(struct events_stats *stats, FILE *fp);
119 163
@@ -124,6 +168,12 @@ void hists__filter_by_dso(struct hists *hists);
124void hists__filter_by_thread(struct hists *hists); 168void hists__filter_by_thread(struct hists *hists);
125void hists__filter_by_symbol(struct hists *hists); 169void hists__filter_by_symbol(struct hists *hists);
126 170
171static inline bool hists__has_filter(struct hists *hists)
172{
173 return hists->thread_filter || hists->dso_filter ||
174 hists->symbol_filter_str;
175}
176
127u16 hists__col_len(struct hists *hists, enum hist_column col); 177u16 hists__col_len(struct hists *hists, enum hist_column col);
128void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len); 178void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len);
129bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len); 179bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len);
@@ -149,15 +199,30 @@ struct perf_hpp_fmt {
149 struct hist_entry *he); 199 struct hist_entry *he);
150 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 200 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
151 struct hist_entry *he); 201 struct hist_entry *he);
202 int64_t (*cmp)(struct hist_entry *a, struct hist_entry *b);
203 int64_t (*collapse)(struct hist_entry *a, struct hist_entry *b);
204 int64_t (*sort)(struct hist_entry *a, struct hist_entry *b);
152 205
153 struct list_head list; 206 struct list_head list;
207 struct list_head sort_list;
208 bool elide;
154}; 209};
155 210
156extern struct list_head perf_hpp__list; 211extern struct list_head perf_hpp__list;
212extern struct list_head perf_hpp__sort_list;
157 213
158#define perf_hpp__for_each_format(format) \ 214#define perf_hpp__for_each_format(format) \
159 list_for_each_entry(format, &perf_hpp__list, list) 215 list_for_each_entry(format, &perf_hpp__list, list)
160 216
217#define perf_hpp__for_each_format_safe(format, tmp) \
218 list_for_each_entry_safe(format, tmp, &perf_hpp__list, list)
219
220#define perf_hpp__for_each_sort_list(format) \
221 list_for_each_entry(format, &perf_hpp__sort_list, sort_list)
222
223#define perf_hpp__for_each_sort_list_safe(format, tmp) \
224 list_for_each_entry_safe(format, tmp, &perf_hpp__sort_list, sort_list)
225
161extern struct perf_hpp_fmt perf_hpp__format[]; 226extern struct perf_hpp_fmt perf_hpp__format[];
162 227
163enum { 228enum {
@@ -167,6 +232,7 @@ enum {
167 PERF_HPP__OVERHEAD_US, 232 PERF_HPP__OVERHEAD_US,
168 PERF_HPP__OVERHEAD_GUEST_SYS, 233 PERF_HPP__OVERHEAD_GUEST_SYS,
169 PERF_HPP__OVERHEAD_GUEST_US, 234 PERF_HPP__OVERHEAD_GUEST_US,
235 PERF_HPP__OVERHEAD_ACC,
170 PERF_HPP__SAMPLES, 236 PERF_HPP__SAMPLES,
171 PERF_HPP__PERIOD, 237 PERF_HPP__PERIOD,
172 238
@@ -175,15 +241,36 @@ enum {
175 241
176void perf_hpp__init(void); 242void perf_hpp__init(void);
177void perf_hpp__column_register(struct perf_hpp_fmt *format); 243void perf_hpp__column_register(struct perf_hpp_fmt *format);
244void perf_hpp__column_unregister(struct perf_hpp_fmt *format);
178void perf_hpp__column_enable(unsigned col); 245void perf_hpp__column_enable(unsigned col);
246void perf_hpp__column_disable(unsigned col);
247void perf_hpp__cancel_cumulate(void);
248
249void perf_hpp__register_sort_field(struct perf_hpp_fmt *format);
250void perf_hpp__setup_output_field(void);
251void perf_hpp__reset_output_field(void);
252void perf_hpp__append_sort_keys(void);
253
254bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format);
255bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b);
256
257static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format)
258{
259 return format->elide;
260}
261
262void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists);
179 263
180typedef u64 (*hpp_field_fn)(struct hist_entry *he); 264typedef u64 (*hpp_field_fn)(struct hist_entry *he);
181typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); 265typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front);
182typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); 266typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...);
183 267
184int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, 268int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
185 hpp_field_fn get_field, hpp_callback_fn callback, 269 hpp_field_fn get_field, const char *fmt,
186 const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent); 270 hpp_snprint_fn print_fn, bool fmt_percent);
271int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he,
272 hpp_field_fn get_field, const char *fmt,
273 hpp_snprint_fn print_fn, bool fmt_percent);
187 274
188static inline void advance_hpp(struct perf_hpp *hpp, int inc) 275static inline void advance_hpp(struct perf_hpp *hpp, int inc)
189{ 276{
@@ -250,4 +337,10 @@ static inline int script_browse(const char *script_opt __maybe_unused)
250#endif 337#endif
251 338
252unsigned int hists__sort_list_width(struct hists *hists); 339unsigned int hists__sort_list_width(struct hists *hists);
340
341struct option;
342int parse_filter_percentage(const struct option *opt __maybe_unused,
343 const char *arg, int unset __maybe_unused);
344int perf_hist_config(const char *var, const char *value);
345
253#endif /* __PERF_HIST_H */ 346#endif /* __PERF_HIST_H */
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h
index bb162e40c76c..01ffd12dc791 100644
--- a/tools/perf/util/include/linux/bitmap.h
+++ b/tools/perf/util/include/linux/bitmap.h
@@ -4,6 +4,9 @@
4#include <string.h> 4#include <string.h>
5#include <linux/bitops.h> 5#include <linux/bitops.h>
6 6
7#define DECLARE_BITMAP(name,bits) \
8 unsigned long name[BITS_TO_LONGS(bits)]
9
7int __bitmap_weight(const unsigned long *bitmap, int bits); 10int __bitmap_weight(const unsigned long *bitmap, int bits);
8void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, 11void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
9 const unsigned long *bitmap2, int bits); 12 const unsigned long *bitmap2, int bits);
diff --git a/tools/perf/util/include/linux/export.h b/tools/perf/util/include/linux/export.h
deleted file mode 100644
index b43e2dc21e04..000000000000
--- a/tools/perf/util/include/linux/export.h
+++ /dev/null
@@ -1,6 +0,0 @@
1#ifndef PERF_LINUX_MODULE_H
2#define PERF_LINUX_MODULE_H
3
4#define EXPORT_SYMBOL(name)
5
6#endif
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h
index bfe0a2afd0d2..76ddbc726343 100644
--- a/tools/perf/util/include/linux/list.h
+++ b/tools/perf/util/include/linux/list.h
@@ -1,4 +1,5 @@
1#include <linux/kernel.h> 1#include <linux/kernel.h>
2#include <linux/types.h>
2 3
3#include "../../../../include/linux/list.h" 4#include "../../../../include/linux/list.h"
4 5
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h
deleted file mode 100644
index eb464786c084..000000000000
--- a/tools/perf/util/include/linux/types.h
+++ /dev/null
@@ -1,29 +0,0 @@
1#ifndef _PERF_LINUX_TYPES_H_
2#define _PERF_LINUX_TYPES_H_
3
4#include <asm/types.h>
5
6#ifndef __bitwise
7#define __bitwise
8#endif
9
10#ifndef __le32
11typedef __u32 __bitwise __le32;
12#endif
13
14#define DECLARE_BITMAP(name,bits) \
15 unsigned long name[BITS_TO_LONGS(bits)]
16
17struct list_head {
18 struct list_head *next, *prev;
19};
20
21struct hlist_head {
22 struct hlist_node *first;
23};
24
25struct hlist_node {
26 struct hlist_node *next, **pprev;
27};
28
29#endif
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 27c2a5efe450..7409ac8de51c 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -316,6 +316,17 @@ static struct thread *__machine__findnew_thread(struct machine *machine,
316 rb_link_node(&th->rb_node, parent, p); 316 rb_link_node(&th->rb_node, parent, p);
317 rb_insert_color(&th->rb_node, &machine->threads); 317 rb_insert_color(&th->rb_node, &machine->threads);
318 machine->last_match = th; 318 machine->last_match = th;
319
320 /*
321 * We have to initialize map_groups separately
322 * after rb tree is updated.
323 *
324 * The reason is that we call machine__findnew_thread
325 * within thread__init_map_groups to find the thread
326 * leader and that would screwed the rb tree.
327 */
328 if (thread__init_map_groups(th, machine))
329 return NULL;
319 } 330 }
320 331
321 return th; 332 return th;
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 39cd2d0faff6..8ccbb32eda25 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -32,6 +32,93 @@ static inline int is_no_dso_memory(const char *filename)
32 !strcmp(filename, "[heap]"); 32 !strcmp(filename, "[heap]");
33} 33}
34 34
35static inline int is_android_lib(const char *filename)
36{
37 return !strncmp(filename, "/data/app-lib", 13) ||
38 !strncmp(filename, "/system/lib", 11);
39}
40
41static inline bool replace_android_lib(const char *filename, char *newfilename)
42{
43 const char *libname;
44 char *app_abi;
45 size_t app_abi_length, new_length;
46 size_t lib_length = 0;
47
48 libname = strrchr(filename, '/');
49 if (libname)
50 lib_length = strlen(libname);
51
52 app_abi = getenv("APP_ABI");
53 if (!app_abi)
54 return false;
55
56 app_abi_length = strlen(app_abi);
57
58 if (!strncmp(filename, "/data/app-lib", 13)) {
59 char *apk_path;
60
61 if (!app_abi_length)
62 return false;
63
64 new_length = 7 + app_abi_length + lib_length;
65
66 apk_path = getenv("APK_PATH");
67 if (apk_path) {
68 new_length += strlen(apk_path) + 1;
69 if (new_length > PATH_MAX)
70 return false;
71 snprintf(newfilename, new_length,
72 "%s/libs/%s/%s", apk_path, app_abi, libname);
73 } else {
74 if (new_length > PATH_MAX)
75 return false;
76 snprintf(newfilename, new_length,
77 "libs/%s/%s", app_abi, libname);
78 }
79 return true;
80 }
81
82 if (!strncmp(filename, "/system/lib/", 11)) {
83 char *ndk, *app;
84 const char *arch;
85 size_t ndk_length;
86 size_t app_length;
87
88 ndk = getenv("NDK_ROOT");
89 app = getenv("APP_PLATFORM");
90
91 if (!(ndk && app))
92 return false;
93
94 ndk_length = strlen(ndk);
95 app_length = strlen(app);
96
97 if (!(ndk_length && app_length && app_abi_length))
98 return false;
99
100 arch = !strncmp(app_abi, "arm", 3) ? "arm" :
101 !strncmp(app_abi, "mips", 4) ? "mips" :
102 !strncmp(app_abi, "x86", 3) ? "x86" : NULL;
103
104 if (!arch)
105 return false;
106
107 new_length = 27 + ndk_length +
108 app_length + lib_length
109 + strlen(arch);
110
111 if (new_length > PATH_MAX)
112 return false;
113 snprintf(newfilename, new_length,
114 "%s/platforms/%s/arch-%s/usr/lib/%s",
115 ndk, app, arch, libname);
116
117 return true;
118 }
119 return false;
120}
121
35void map__init(struct map *map, enum map_type type, 122void map__init(struct map *map, enum map_type type,
36 u64 start, u64 end, u64 pgoff, struct dso *dso) 123 u64 start, u64 end, u64 pgoff, struct dso *dso)
37{ 124{
@@ -59,8 +146,9 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
59 if (map != NULL) { 146 if (map != NULL) {
60 char newfilename[PATH_MAX]; 147 char newfilename[PATH_MAX];
61 struct dso *dso; 148 struct dso *dso;
62 int anon, no_dso, vdso; 149 int anon, no_dso, vdso, android;
63 150
151 android = is_android_lib(filename);
64 anon = is_anon_memory(filename); 152 anon = is_anon_memory(filename);
65 vdso = is_vdso_map(filename); 153 vdso = is_vdso_map(filename);
66 no_dso = is_no_dso_memory(filename); 154 no_dso = is_no_dso_memory(filename);
@@ -75,6 +163,11 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
75 filename = newfilename; 163 filename = newfilename;
76 } 164 }
77 165
166 if (android) {
167 if (replace_android_lib(filename, newfilename))
168 filename = newfilename;
169 }
170
78 if (vdso) { 171 if (vdso) {
79 pgoff = 0; 172 pgoff = 0;
80 dso = vdso__dso_findnew(dsos__list); 173 dso = vdso__dso_findnew(dsos__list);
@@ -323,6 +416,7 @@ void map_groups__init(struct map_groups *mg)
323 INIT_LIST_HEAD(&mg->removed_maps[i]); 416 INIT_LIST_HEAD(&mg->removed_maps[i]);
324 } 417 }
325 mg->machine = NULL; 418 mg->machine = NULL;
419 mg->refcnt = 1;
326} 420}
327 421
328static void maps__delete(struct rb_root *maps) 422static void maps__delete(struct rb_root *maps)
@@ -358,6 +452,28 @@ void map_groups__exit(struct map_groups *mg)
358 } 452 }
359} 453}
360 454
455struct map_groups *map_groups__new(void)
456{
457 struct map_groups *mg = malloc(sizeof(*mg));
458
459 if (mg != NULL)
460 map_groups__init(mg);
461
462 return mg;
463}
464
465void map_groups__delete(struct map_groups *mg)
466{
467 map_groups__exit(mg);
468 free(mg);
469}
470
471void map_groups__put(struct map_groups *mg)
472{
473 if (--mg->refcnt == 0)
474 map_groups__delete(mg);
475}
476
361void map_groups__flush(struct map_groups *mg) 477void map_groups__flush(struct map_groups *mg)
362{ 478{
363 int type; 479 int type;
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index f00f058afb3b..ae2d45110588 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -6,7 +6,7 @@
6#include <linux/rbtree.h> 6#include <linux/rbtree.h>
7#include <stdio.h> 7#include <stdio.h>
8#include <stdbool.h> 8#include <stdbool.h>
9#include "types.h" 9#include <linux/types.h>
10 10
11enum map_type { 11enum map_type {
12 MAP__FUNCTION = 0, 12 MAP__FUNCTION = 0,
@@ -59,8 +59,20 @@ struct map_groups {
59 struct rb_root maps[MAP__NR_TYPES]; 59 struct rb_root maps[MAP__NR_TYPES];
60 struct list_head removed_maps[MAP__NR_TYPES]; 60 struct list_head removed_maps[MAP__NR_TYPES];
61 struct machine *machine; 61 struct machine *machine;
62 int refcnt;
62}; 63};
63 64
65struct map_groups *map_groups__new(void);
66void map_groups__delete(struct map_groups *mg);
67
68static inline struct map_groups *map_groups__get(struct map_groups *mg)
69{
70 ++mg->refcnt;
71 return mg;
72}
73
74void map_groups__put(struct map_groups *mg);
75
64static inline struct kmap *map__kmap(struct map *map) 76static inline struct kmap *map__kmap(struct map *map)
65{ 77{
66 return (struct kmap *)(map + 1); 78 return (struct kmap *)(map + 1);
diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c
index 3322b8446e89..31ee02d4e988 100644
--- a/tools/perf/util/pager.c
+++ b/tools/perf/util/pager.c
@@ -57,13 +57,13 @@ void setup_pager(void)
57 } 57 }
58 if (!pager) 58 if (!pager)
59 pager = getenv("PAGER"); 59 pager = getenv("PAGER");
60 if (!pager) { 60 if (!(pager || access("/usr/bin/pager", X_OK)))
61 if (!access("/usr/bin/pager", X_OK)) 61 pager = "/usr/bin/pager";
62 pager = "/usr/bin/pager"; 62 if (!(pager || access("/usr/bin/less", X_OK)))
63 } 63 pager = "/usr/bin/less";
64 if (!pager) 64 if (!pager)
65 pager = "less"; 65 pager = "cat";
66 else if (!*pager || !strcmp(pager, "cat")) 66 if (!*pager || !strcmp(pager, "cat"))
67 return; 67 return;
68 68
69 spawned_pager = 1; /* means we are emitting to terminal */ 69 spawned_pager = 1; /* means we are emitting to terminal */
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index f1cb4c4b3c70..df094b4ed5ed 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -6,9 +6,8 @@
6 6
7#include <linux/list.h> 7#include <linux/list.h>
8#include <stdbool.h> 8#include <stdbool.h>
9#include "types.h" 9#include <linux/types.h>
10#include <linux/perf_event.h> 10#include <linux/perf_event.h>
11#include "types.h"
12 11
13struct list_head; 12struct list_head;
14struct perf_evsel; 13struct perf_evsel;
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 4eb67ec333f1..0bc87ba46bf3 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -9,7 +9,7 @@
9 9
10#include <linux/compiler.h> 10#include <linux/compiler.h>
11#include <linux/list.h> 11#include <linux/list.h>
12#include "types.h" 12#include <linux/types.h>
13#include "util.h" 13#include "util.h"
14#include "parse-events.h" 14#include "parse-events.h"
15#include "parse-events-bison.h" 15#include "parse-events-bison.h"
@@ -299,6 +299,18 @@ PE_PREFIX_MEM PE_VALUE sep_dc
299} 299}
300 300
301event_legacy_tracepoint: 301event_legacy_tracepoint:
302PE_NAME '-' PE_NAME ':' PE_NAME
303{
304 struct parse_events_evlist *data = _data;
305 struct list_head *list;
306 char sys_name[128];
307 snprintf(&sys_name, 128, "%s-%s", $1, $3);
308
309 ALLOC_LIST(list);
310 ABORT_ON(parse_events_add_tracepoint(list, &data->idx, &sys_name, $5));
311 $$ = list;
312}
313|
302PE_NAME ':' PE_NAME 314PE_NAME ':' PE_NAME
303{ 315{
304 struct parse_events_evlist *data = _data; 316 struct parse_events_evlist *data = _data;
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h
index d6e8b6a8d7f3..79c78f74e0cf 100644
--- a/tools/perf/util/perf_regs.h
+++ b/tools/perf/util/perf_regs.h
@@ -1,7 +1,7 @@
1#ifndef __PERF_REGS_H 1#ifndef __PERF_REGS_H
2#define __PERF_REGS_H 2#define __PERF_REGS_H
3 3
4#include "types.h" 4#include <linux/types.h>
5#include "event.h" 5#include "event.h"
6 6
7#ifdef HAVE_PERF_REGS_SUPPORT 7#ifdef HAVE_PERF_REGS_SUPPORT
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 00a7dcb2f55c..7a811eb61f75 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -284,17 +284,17 @@ static int pmu_aliases(const char *name, struct list_head *head)
284static int pmu_alias_terms(struct perf_pmu_alias *alias, 284static int pmu_alias_terms(struct perf_pmu_alias *alias,
285 struct list_head *terms) 285 struct list_head *terms)
286{ 286{
287 struct parse_events_term *term, *clone; 287 struct parse_events_term *term, *cloned;
288 LIST_HEAD(list); 288 LIST_HEAD(list);
289 int ret; 289 int ret;
290 290
291 list_for_each_entry(term, &alias->terms, list) { 291 list_for_each_entry(term, &alias->terms, list) {
292 ret = parse_events_term__clone(&clone, term); 292 ret = parse_events_term__clone(&cloned, term);
293 if (ret) { 293 if (ret) {
294 parse_events__free_terms(&list); 294 parse_events__free_terms(&list);
295 return ret; 295 return ret;
296 } 296 }
297 list_add_tail(&clone->list, &list); 297 list_add_tail(&cloned->list, &list);
298 } 298 }
299 list_splice(&list, terms); 299 list_splice(&list, terms);
300 return 0; 300 return 0;
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 8b64125a9281..c14a543ce1f3 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -1,7 +1,7 @@
1#ifndef __PMU_H 1#ifndef __PMU_H
2#define __PMU_H 2#define __PMU_H
3 3
4#include <linux/bitops.h> 4#include <linux/bitmap.h>
5#include <linux/perf_event.h> 5#include <linux/perf_event.h>
6#include <stdbool.h> 6#include <stdbool.h>
7 7
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 55960f22233c..64a186edc7be 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1625,13 +1625,14 @@ out_delete_map:
1625void perf_session__fprintf_info(struct perf_session *session, FILE *fp, 1625void perf_session__fprintf_info(struct perf_session *session, FILE *fp,
1626 bool full) 1626 bool full)
1627{ 1627{
1628 int fd = perf_data_file__fd(session->file);
1629 struct stat st; 1628 struct stat st;
1630 int ret; 1629 int fd, ret;
1631 1630
1632 if (session == NULL || fp == NULL) 1631 if (session == NULL || fp == NULL)
1633 return; 1632 return;
1634 1633
1634 fd = perf_data_file__fd(session->file);
1635
1635 ret = fstat(fd, &st); 1636 ret = fstat(fd, &st);
1636 if (ret == -1) 1637 if (ret == -1)
1637 return; 1638 return;
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 635cd8f8b22e..45512baaab67 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -2,12 +2,18 @@
2#include "hist.h" 2#include "hist.h"
3#include "comm.h" 3#include "comm.h"
4#include "symbol.h" 4#include "symbol.h"
5#include "evsel.h"
5 6
6regex_t parent_regex; 7regex_t parent_regex;
7const char default_parent_pattern[] = "^sys_|^do_page_fault"; 8const char default_parent_pattern[] = "^sys_|^do_page_fault";
8const char *parent_pattern = default_parent_pattern; 9const char *parent_pattern = default_parent_pattern;
9const char default_sort_order[] = "comm,dso,symbol"; 10const char default_sort_order[] = "comm,dso,symbol";
10const char *sort_order = default_sort_order; 11const char default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to";
12const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
13const char default_top_sort_order[] = "dso,symbol";
14const char default_diff_sort_order[] = "dso,symbol";
15const char *sort_order;
16const char *field_order;
11regex_t ignore_callees_regex; 17regex_t ignore_callees_regex;
12int have_ignore_callees = 0; 18int have_ignore_callees = 0;
13int sort__need_collapse = 0; 19int sort__need_collapse = 0;
@@ -16,9 +22,6 @@ int sort__has_sym = 0;
16int sort__has_dso = 0; 22int sort__has_dso = 0;
17enum sort_mode sort__mode = SORT_MODE__NORMAL; 23enum sort_mode sort__mode = SORT_MODE__NORMAL;
18 24
19enum sort_type sort__first_dimension;
20
21LIST_HEAD(hist_entry__sort_list);
22 25
23static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) 26static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
24{ 27{
@@ -93,6 +96,12 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
93 return comm__str(right->comm) - comm__str(left->comm); 96 return comm__str(right->comm) - comm__str(left->comm);
94} 97}
95 98
99static int64_t
100sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
101{
102 return strcmp(comm__str(right->comm), comm__str(left->comm));
103}
104
96static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, 105static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
97 size_t size, unsigned int width) 106 size_t size, unsigned int width)
98{ 107{
@@ -103,6 +112,7 @@ struct sort_entry sort_comm = {
103 .se_header = "Command", 112 .se_header = "Command",
104 .se_cmp = sort__comm_cmp, 113 .se_cmp = sort__comm_cmp,
105 .se_collapse = sort__comm_collapse, 114 .se_collapse = sort__comm_collapse,
115 .se_sort = sort__comm_sort,
106 .se_snprintf = hist_entry__comm_snprintf, 116 .se_snprintf = hist_entry__comm_snprintf,
107 .se_width_idx = HISTC_COMM, 117 .se_width_idx = HISTC_COMM,
108}; 118};
@@ -116,7 +126,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
116 const char *dso_name_l, *dso_name_r; 126 const char *dso_name_l, *dso_name_r;
117 127
118 if (!dso_l || !dso_r) 128 if (!dso_l || !dso_r)
119 return cmp_null(dso_l, dso_r); 129 return cmp_null(dso_r, dso_l);
120 130
121 if (verbose) { 131 if (verbose) {
122 dso_name_l = dso_l->long_name; 132 dso_name_l = dso_l->long_name;
@@ -132,7 +142,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
132static int64_t 142static int64_t
133sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) 143sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
134{ 144{
135 return _sort__dso_cmp(left->ms.map, right->ms.map); 145 return _sort__dso_cmp(right->ms.map, left->ms.map);
136} 146}
137 147
138static int _hist_entry__dso_snprintf(struct map *map, char *bf, 148static int _hist_entry__dso_snprintf(struct map *map, char *bf,
@@ -204,6 +214,15 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
204 return _sort__sym_cmp(left->ms.sym, right->ms.sym); 214 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
205} 215}
206 216
217static int64_t
218sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
219{
220 if (!left->ms.sym || !right->ms.sym)
221 return cmp_null(left->ms.sym, right->ms.sym);
222
223 return strcmp(right->ms.sym->name, left->ms.sym->name);
224}
225
207static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, 226static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
208 u64 ip, char level, char *bf, size_t size, 227 u64 ip, char level, char *bf, size_t size,
209 unsigned int width) 228 unsigned int width)
@@ -250,6 +269,7 @@ static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
250struct sort_entry sort_sym = { 269struct sort_entry sort_sym = {
251 .se_header = "Symbol", 270 .se_header = "Symbol",
252 .se_cmp = sort__sym_cmp, 271 .se_cmp = sort__sym_cmp,
272 .se_sort = sort__sym_sort,
253 .se_snprintf = hist_entry__sym_snprintf, 273 .se_snprintf = hist_entry__sym_snprintf,
254 .se_width_idx = HISTC_SYMBOL, 274 .se_width_idx = HISTC_SYMBOL,
255}; 275};
@@ -277,7 +297,7 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
277 map__rip_2objdump(map, right->ip)); 297 map__rip_2objdump(map, right->ip));
278 } 298 }
279 } 299 }
280 return strcmp(left->srcline, right->srcline); 300 return strcmp(right->srcline, left->srcline);
281} 301}
282 302
283static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, 303static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
@@ -305,7 +325,7 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
305 if (!sym_l || !sym_r) 325 if (!sym_l || !sym_r)
306 return cmp_null(sym_l, sym_r); 326 return cmp_null(sym_l, sym_r);
307 327
308 return strcmp(sym_l->name, sym_r->name); 328 return strcmp(sym_r->name, sym_l->name);
309} 329}
310 330
311static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, 331static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
@@ -1027,19 +1047,194 @@ static struct sort_dimension memory_sort_dimensions[] = {
1027 1047
1028#undef DIM 1048#undef DIM
1029 1049
1030static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) 1050struct hpp_dimension {
1051 const char *name;
1052 struct perf_hpp_fmt *fmt;
1053 int taken;
1054};
1055
1056#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1057
1058static struct hpp_dimension hpp_sort_dimensions[] = {
1059 DIM(PERF_HPP__OVERHEAD, "overhead"),
1060 DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
1061 DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
1062 DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
1063 DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
1064 DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
1065 DIM(PERF_HPP__SAMPLES, "sample"),
1066 DIM(PERF_HPP__PERIOD, "period"),
1067};
1068
1069#undef DIM
1070
1071struct hpp_sort_entry {
1072 struct perf_hpp_fmt hpp;
1073 struct sort_entry *se;
1074};
1075
1076bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1031{ 1077{
1032 if (sd->taken) 1078 struct hpp_sort_entry *hse_a;
1079 struct hpp_sort_entry *hse_b;
1080
1081 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1082 return false;
1083
1084 hse_a = container_of(a, struct hpp_sort_entry, hpp);
1085 hse_b = container_of(b, struct hpp_sort_entry, hpp);
1086
1087 return hse_a->se == hse_b->se;
1088}
1089
1090void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
1091{
1092 struct hpp_sort_entry *hse;
1093
1094 if (!perf_hpp__is_sort_entry(fmt))
1033 return; 1095 return;
1034 1096
1097 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1098 hists__new_col_len(hists, hse->se->se_width_idx,
1099 strlen(hse->se->se_header));
1100}
1101
1102static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1103 struct perf_evsel *evsel)
1104{
1105 struct hpp_sort_entry *hse;
1106 size_t len;
1107
1108 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1109 len = hists__col_len(&evsel->hists, hse->se->se_width_idx);
1110
1111 return scnprintf(hpp->buf, hpp->size, "%*s", len, hse->se->se_header);
1112}
1113
1114static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
1115 struct perf_hpp *hpp __maybe_unused,
1116 struct perf_evsel *evsel)
1117{
1118 struct hpp_sort_entry *hse;
1119
1120 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1121
1122 return hists__col_len(&evsel->hists, hse->se->se_width_idx);
1123}
1124
1125static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1126 struct hist_entry *he)
1127{
1128 struct hpp_sort_entry *hse;
1129 size_t len;
1130
1131 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1132 len = hists__col_len(he->hists, hse->se->se_width_idx);
1133
1134 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1135}
1136
1137static struct hpp_sort_entry *
1138__sort_dimension__alloc_hpp(struct sort_dimension *sd)
1139{
1140 struct hpp_sort_entry *hse;
1141
1142 hse = malloc(sizeof(*hse));
1143 if (hse == NULL) {
1144 pr_err("Memory allocation failed\n");
1145 return NULL;
1146 }
1147
1148 hse->se = sd->entry;
1149 hse->hpp.header = __sort__hpp_header;
1150 hse->hpp.width = __sort__hpp_width;
1151 hse->hpp.entry = __sort__hpp_entry;
1152 hse->hpp.color = NULL;
1153
1154 hse->hpp.cmp = sd->entry->se_cmp;
1155 hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp;
1156 hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse;
1157
1158 INIT_LIST_HEAD(&hse->hpp.list);
1159 INIT_LIST_HEAD(&hse->hpp.sort_list);
1160 hse->hpp.elide = false;
1161
1162 return hse;
1163}
1164
1165bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1166{
1167 return format->header == __sort__hpp_header;
1168}
1169
1170static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
1171{
1172 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1173
1174 if (hse == NULL)
1175 return -1;
1176
1177 perf_hpp__register_sort_field(&hse->hpp);
1178 return 0;
1179}
1180
1181static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
1182{
1183 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1184
1185 if (hse == NULL)
1186 return -1;
1187
1188 perf_hpp__column_register(&hse->hpp);
1189 return 0;
1190}
1191
1192static int __sort_dimension__add(struct sort_dimension *sd)
1193{
1194 if (sd->taken)
1195 return 0;
1196
1197 if (__sort_dimension__add_hpp_sort(sd) < 0)
1198 return -1;
1199
1035 if (sd->entry->se_collapse) 1200 if (sd->entry->se_collapse)
1036 sort__need_collapse = 1; 1201 sort__need_collapse = 1;
1037 1202
1038 if (list_empty(&hist_entry__sort_list)) 1203 sd->taken = 1;
1039 sort__first_dimension = idx; 1204
1205 return 0;
1206}
1207
1208static int __hpp_dimension__add(struct hpp_dimension *hd)
1209{
1210 if (!hd->taken) {
1211 hd->taken = 1;
1212
1213 perf_hpp__register_sort_field(hd->fmt);
1214 }
1215 return 0;
1216}
1217
1218static int __sort_dimension__add_output(struct sort_dimension *sd)
1219{
1220 if (sd->taken)
1221 return 0;
1222
1223 if (__sort_dimension__add_hpp_output(sd) < 0)
1224 return -1;
1040 1225
1041 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
1042 sd->taken = 1; 1226 sd->taken = 1;
1227 return 0;
1228}
1229
1230static int __hpp_dimension__add_output(struct hpp_dimension *hd)
1231{
1232 if (!hd->taken) {
1233 hd->taken = 1;
1234
1235 perf_hpp__column_register(hd->fmt);
1236 }
1237 return 0;
1043} 1238}
1044 1239
1045int sort_dimension__add(const char *tok) 1240int sort_dimension__add(const char *tok)
@@ -1068,8 +1263,16 @@ int sort_dimension__add(const char *tok)
1068 sort__has_dso = 1; 1263 sort__has_dso = 1;
1069 } 1264 }
1070 1265
1071 __sort_dimension__add(sd, i); 1266 return __sort_dimension__add(sd);
1072 return 0; 1267 }
1268
1269 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1270 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1271
1272 if (strncasecmp(tok, hd->name, strlen(tok)))
1273 continue;
1274
1275 return __hpp_dimension__add(hd);
1073 } 1276 }
1074 1277
1075 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { 1278 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
@@ -1084,7 +1287,7 @@ int sort_dimension__add(const char *tok)
1084 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) 1287 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
1085 sort__has_sym = 1; 1288 sort__has_sym = 1;
1086 1289
1087 __sort_dimension__add(sd, i + __SORT_BRANCH_STACK); 1290 __sort_dimension__add(sd);
1088 return 0; 1291 return 0;
1089 } 1292 }
1090 1293
@@ -1100,18 +1303,47 @@ int sort_dimension__add(const char *tok)
1100 if (sd->entry == &sort_mem_daddr_sym) 1303 if (sd->entry == &sort_mem_daddr_sym)
1101 sort__has_sym = 1; 1304 sort__has_sym = 1;
1102 1305
1103 __sort_dimension__add(sd, i + __SORT_MEMORY_MODE); 1306 __sort_dimension__add(sd);
1104 return 0; 1307 return 0;
1105 } 1308 }
1106 1309
1107 return -ESRCH; 1310 return -ESRCH;
1108} 1311}
1109 1312
1110int setup_sorting(void) 1313static const char *get_default_sort_order(void)
1111{ 1314{
1112 char *tmp, *tok, *str = strdup(sort_order); 1315 const char *default_sort_orders[] = {
1316 default_sort_order,
1317 default_branch_sort_order,
1318 default_mem_sort_order,
1319 default_top_sort_order,
1320 default_diff_sort_order,
1321 };
1322
1323 BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
1324
1325 return default_sort_orders[sort__mode];
1326}
1327
1328static int __setup_sorting(void)
1329{
1330 char *tmp, *tok, *str;
1331 const char *sort_keys = sort_order;
1113 int ret = 0; 1332 int ret = 0;
1114 1333
1334 if (sort_keys == NULL) {
1335 if (field_order) {
1336 /*
1337 * If user specified field order but no sort order,
1338 * we'll honor it and not add default sort orders.
1339 */
1340 return 0;
1341 }
1342
1343 sort_keys = get_default_sort_order();
1344 }
1345
1346 str = strdup(sort_keys);
1115 if (str == NULL) { 1347 if (str == NULL) {
1116 error("Not enough memory to setup sort keys"); 1348 error("Not enough memory to setup sort keys");
1117 return -ENOMEM; 1349 return -ENOMEM;
@@ -1133,66 +1365,235 @@ int setup_sorting(void)
1133 return ret; 1365 return ret;
1134} 1366}
1135 1367
1136static void sort_entry__setup_elide(struct sort_entry *se, 1368void perf_hpp__set_elide(int idx, bool elide)
1137 struct strlist *list, 1369{
1138 const char *list_name, FILE *fp) 1370 struct perf_hpp_fmt *fmt;
1371 struct hpp_sort_entry *hse;
1372
1373 perf_hpp__for_each_format(fmt) {
1374 if (!perf_hpp__is_sort_entry(fmt))
1375 continue;
1376
1377 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1378 if (hse->se->se_width_idx == idx) {
1379 fmt->elide = elide;
1380 break;
1381 }
1382 }
1383}
1384
1385static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
1139{ 1386{
1140 if (list && strlist__nr_entries(list) == 1) { 1387 if (list && strlist__nr_entries(list) == 1) {
1141 if (fp != NULL) 1388 if (fp != NULL)
1142 fprintf(fp, "# %s: %s\n", list_name, 1389 fprintf(fp, "# %s: %s\n", list_name,
1143 strlist__entry(list, 0)->s); 1390 strlist__entry(list, 0)->s);
1144 se->elide = true; 1391 return true;
1392 }
1393 return false;
1394}
1395
1396static bool get_elide(int idx, FILE *output)
1397{
1398 switch (idx) {
1399 case HISTC_SYMBOL:
1400 return __get_elide(symbol_conf.sym_list, "symbol", output);
1401 case HISTC_DSO:
1402 return __get_elide(symbol_conf.dso_list, "dso", output);
1403 case HISTC_COMM:
1404 return __get_elide(symbol_conf.comm_list, "comm", output);
1405 default:
1406 break;
1145 } 1407 }
1408
1409 if (sort__mode != SORT_MODE__BRANCH)
1410 return false;
1411
1412 switch (idx) {
1413 case HISTC_SYMBOL_FROM:
1414 return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
1415 case HISTC_SYMBOL_TO:
1416 return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
1417 case HISTC_DSO_FROM:
1418 return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
1419 case HISTC_DSO_TO:
1420 return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
1421 default:
1422 break;
1423 }
1424
1425 return false;
1146} 1426}
1147 1427
1148void sort__setup_elide(FILE *output) 1428void sort__setup_elide(FILE *output)
1149{ 1429{
1150 struct sort_entry *se; 1430 struct perf_hpp_fmt *fmt;
1431 struct hpp_sort_entry *hse;
1151 1432
1152 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, 1433 perf_hpp__for_each_format(fmt) {
1153 "dso", output); 1434 if (!perf_hpp__is_sort_entry(fmt))
1154 sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, 1435 continue;
1155 "comm", output); 1436
1156 sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, 1437 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1157 "symbol", output); 1438 fmt->elide = get_elide(hse->se->se_width_idx, output);
1158
1159 if (sort__mode == SORT_MODE__BRANCH) {
1160 sort_entry__setup_elide(&sort_dso_from,
1161 symbol_conf.dso_from_list,
1162 "dso_from", output);
1163 sort_entry__setup_elide(&sort_dso_to,
1164 symbol_conf.dso_to_list,
1165 "dso_to", output);
1166 sort_entry__setup_elide(&sort_sym_from,
1167 symbol_conf.sym_from_list,
1168 "sym_from", output);
1169 sort_entry__setup_elide(&sort_sym_to,
1170 symbol_conf.sym_to_list,
1171 "sym_to", output);
1172 } else if (sort__mode == SORT_MODE__MEMORY) {
1173 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1174 "symbol_daddr", output);
1175 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1176 "dso_daddr", output);
1177 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1178 "mem", output);
1179 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1180 "local_weight", output);
1181 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1182 "tlb", output);
1183 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1184 "snoop", output);
1185 } 1439 }
1186 1440
1187 /* 1441 /*
1188 * It makes no sense to elide all of sort entries. 1442 * It makes no sense to elide all of sort entries.
1189 * Just revert them to show up again. 1443 * Just revert them to show up again.
1190 */ 1444 */
1191 list_for_each_entry(se, &hist_entry__sort_list, list) { 1445 perf_hpp__for_each_format(fmt) {
1192 if (!se->elide) 1446 if (!perf_hpp__is_sort_entry(fmt))
1447 continue;
1448
1449 if (!fmt->elide)
1193 return; 1450 return;
1194 } 1451 }
1195 1452
1196 list_for_each_entry(se, &hist_entry__sort_list, list) 1453 perf_hpp__for_each_format(fmt) {
1197 se->elide = false; 1454 if (!perf_hpp__is_sort_entry(fmt))
1455 continue;
1456
1457 fmt->elide = false;
1458 }
1459}
1460
1461static int output_field_add(char *tok)
1462{
1463 unsigned int i;
1464
1465 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1466 struct sort_dimension *sd = &common_sort_dimensions[i];
1467
1468 if (strncasecmp(tok, sd->name, strlen(tok)))
1469 continue;
1470
1471 return __sort_dimension__add_output(sd);
1472 }
1473
1474 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1475 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1476
1477 if (strncasecmp(tok, hd->name, strlen(tok)))
1478 continue;
1479
1480 return __hpp_dimension__add_output(hd);
1481 }
1482
1483 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1484 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1485
1486 if (strncasecmp(tok, sd->name, strlen(tok)))
1487 continue;
1488
1489 return __sort_dimension__add_output(sd);
1490 }
1491
1492 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1493 struct sort_dimension *sd = &memory_sort_dimensions[i];
1494
1495 if (strncasecmp(tok, sd->name, strlen(tok)))
1496 continue;
1497
1498 return __sort_dimension__add_output(sd);
1499 }
1500
1501 return -ESRCH;
1502}
1503
1504static void reset_dimensions(void)
1505{
1506 unsigned int i;
1507
1508 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
1509 common_sort_dimensions[i].taken = 0;
1510
1511 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
1512 hpp_sort_dimensions[i].taken = 0;
1513
1514 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
1515 bstack_sort_dimensions[i].taken = 0;
1516
1517 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
1518 memory_sort_dimensions[i].taken = 0;
1519}
1520
1521static int __setup_output_field(void)
1522{
1523 char *tmp, *tok, *str;
1524 int ret = 0;
1525
1526 if (field_order == NULL)
1527 return 0;
1528
1529 reset_dimensions();
1530
1531 str = strdup(field_order);
1532 if (str == NULL) {
1533 error("Not enough memory to setup output fields");
1534 return -ENOMEM;
1535 }
1536
1537 for (tok = strtok_r(str, ", ", &tmp);
1538 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1539 ret = output_field_add(tok);
1540 if (ret == -EINVAL) {
1541 error("Invalid --fields key: `%s'", tok);
1542 break;
1543 } else if (ret == -ESRCH) {
1544 error("Unknown --fields key: `%s'", tok);
1545 break;
1546 }
1547 }
1548
1549 free(str);
1550 return ret;
1551}
1552
1553int setup_sorting(void)
1554{
1555 int err;
1556
1557 err = __setup_sorting();
1558 if (err < 0)
1559 return err;
1560
1561 if (parent_pattern != default_parent_pattern) {
1562 err = sort_dimension__add("parent");
1563 if (err < 0)
1564 return err;
1565 }
1566
1567 reset_dimensions();
1568
1569 /*
1570 * perf diff doesn't use default hpp output fields.
1571 */
1572 if (sort__mode != SORT_MODE__DIFF)
1573 perf_hpp__init();
1574
1575 err = __setup_output_field();
1576 if (err < 0)
1577 return err;
1578
1579 /* copy sort keys to output fields */
1580 perf_hpp__setup_output_field();
1581 /* and then copy output fields to sort keys */
1582 perf_hpp__append_sort_keys();
1583
1584 return 0;
1585}
1586
1587void reset_output_field(void)
1588{
1589 sort__need_collapse = 0;
1590 sort__has_parent = 0;
1591 sort__has_sym = 0;
1592 sort__has_dso = 0;
1593
1594 field_order = NULL;
1595 sort_order = NULL;
1596
1597 reset_dimensions();
1598 perf_hpp__reset_output_field();
1198} 1599}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 43e5ff42a609..5bf0098d6b06 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -20,11 +20,12 @@
20 20
21#include "parse-options.h" 21#include "parse-options.h"
22#include "parse-events.h" 22#include "parse-events.h"
23 23#include "hist.h"
24#include "thread.h" 24#include "thread.h"
25 25
26extern regex_t parent_regex; 26extern regex_t parent_regex;
27extern const char *sort_order; 27extern const char *sort_order;
28extern const char *field_order;
28extern const char default_parent_pattern[]; 29extern const char default_parent_pattern[];
29extern const char *parent_pattern; 30extern const char *parent_pattern;
30extern const char default_sort_order[]; 31extern const char default_sort_order[];
@@ -81,6 +82,7 @@ struct hist_entry {
81 struct list_head head; 82 struct list_head head;
82 } pairs; 83 } pairs;
83 struct he_stat stat; 84 struct he_stat stat;
85 struct he_stat *stat_acc;
84 struct map_symbol ms; 86 struct map_symbol ms;
85 struct thread *thread; 87 struct thread *thread;
86 struct comm *comm; 88 struct comm *comm;
@@ -129,10 +131,27 @@ static inline void hist_entry__add_pair(struct hist_entry *pair,
129 list_add_tail(&pair->pairs.node, &he->pairs.head); 131 list_add_tail(&pair->pairs.node, &he->pairs.head);
130} 132}
131 133
134static inline float hist_entry__get_percent_limit(struct hist_entry *he)
135{
136 u64 period = he->stat.period;
137 u64 total_period = hists__total_period(he->hists);
138
139 if (unlikely(total_period == 0))
140 return 0;
141
142 if (symbol_conf.cumulate_callchain)
143 period = he->stat_acc->period;
144
145 return period * 100.0 / total_period;
146}
147
148
132enum sort_mode { 149enum sort_mode {
133 SORT_MODE__NORMAL, 150 SORT_MODE__NORMAL,
134 SORT_MODE__BRANCH, 151 SORT_MODE__BRANCH,
135 SORT_MODE__MEMORY, 152 SORT_MODE__MEMORY,
153 SORT_MODE__TOP,
154 SORT_MODE__DIFF,
136}; 155};
137 156
138enum sort_type { 157enum sort_type {
@@ -179,18 +198,21 @@ struct sort_entry {
179 198
180 int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); 199 int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);
181 int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); 200 int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *);
201 int64_t (*se_sort)(struct hist_entry *, struct hist_entry *);
182 int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size, 202 int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size,
183 unsigned int width); 203 unsigned int width);
184 u8 se_width_idx; 204 u8 se_width_idx;
185 bool elide;
186}; 205};
187 206
188extern struct sort_entry sort_thread; 207extern struct sort_entry sort_thread;
189extern struct list_head hist_entry__sort_list; 208extern struct list_head hist_entry__sort_list;
190 209
191int setup_sorting(void); 210int setup_sorting(void);
211int setup_output_field(void);
212void reset_output_field(void);
192extern int sort_dimension__add(const char *); 213extern int sort_dimension__add(const char *);
193void sort__setup_elide(FILE *fp); 214void sort__setup_elide(FILE *fp);
215void perf_hpp__set_elide(int idx, bool elide);
194 216
195int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); 217int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset);
196 218
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index ae8ccd7227cf..5667fc3e39cf 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -1,7 +1,7 @@
1#ifndef __PERF_STATS_H 1#ifndef __PERF_STATS_H
2#define __PERF_STATS_H 2#define __PERF_STATS_H
3 3
4#include "types.h" 4#include <linux/types.h>
5 5
6struct stats 6struct stats
7{ 7{
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c
index 43262b83c541..6a0a13d07a28 100644
--- a/tools/perf/util/svghelper.c
+++ b/tools/perf/util/svghelper.c
@@ -17,7 +17,7 @@
17#include <stdlib.h> 17#include <stdlib.h>
18#include <unistd.h> 18#include <unistd.h>
19#include <string.h> 19#include <string.h>
20#include <linux/bitops.h> 20#include <linux/bitmap.h>
21 21
22#include "perf.h" 22#include "perf.h"
23#include "svghelper.h" 23#include "svghelper.h"
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h
index f7b4d6e699ea..e3aff5332e30 100644
--- a/tools/perf/util/svghelper.h
+++ b/tools/perf/util/svghelper.h
@@ -1,7 +1,7 @@
1#ifndef __PERF_SVGHELPER_H 1#ifndef __PERF_SVGHELPER_H
2#define __PERF_SVGHELPER_H 2#define __PERF_SVGHELPER_H
3 3
4#include "types.h" 4#include <linux/types.h>
5 5
6extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end); 6extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end);
7extern void svg_box(int Yslot, u64 start, u64 end, const char *type); 7extern void svg_box(int Yslot, u64 start, u64 end, const char *type);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 95e249779931..7b9096f29cdb 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -29,11 +29,12 @@ int vmlinux_path__nr_entries;
29char **vmlinux_path; 29char **vmlinux_path;
30 30
31struct symbol_conf symbol_conf = { 31struct symbol_conf symbol_conf = {
32 .use_modules = true, 32 .use_modules = true,
33 .try_vmlinux_path = true, 33 .try_vmlinux_path = true,
34 .annotate_src = true, 34 .annotate_src = true,
35 .demangle = true, 35 .demangle = true,
36 .symfs = "", 36 .cumulate_callchain = true,
37 .symfs = "",
37}; 38};
38 39
39static enum dso_binary_type binary_type_symtab[] = { 40static enum dso_binary_type binary_type_symtab[] = {
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 501e4e722e8e..615c752dd767 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -12,6 +12,7 @@
12#include <byteswap.h> 12#include <byteswap.h>
13#include <libgen.h> 13#include <libgen.h>
14#include "build-id.h" 14#include "build-id.h"
15#include "event.h"
15 16
16#ifdef HAVE_LIBELF_SUPPORT 17#ifdef HAVE_LIBELF_SUPPORT
17#include <libelf.h> 18#include <libelf.h>
@@ -108,6 +109,7 @@ struct symbol_conf {
108 show_nr_samples, 109 show_nr_samples,
109 show_total_period, 110 show_total_period,
110 use_callchain, 111 use_callchain,
112 cumulate_callchain,
111 exclude_other, 113 exclude_other,
112 show_cpu_utilization, 114 show_cpu_utilization,
113 initialized, 115 initialized,
@@ -115,7 +117,8 @@ struct symbol_conf {
115 annotate_asm_raw, 117 annotate_asm_raw,
116 annotate_src, 118 annotate_src,
117 event_group, 119 event_group,
118 demangle; 120 demangle,
121 filter_relative;
119 const char *vmlinux_name, 122 const char *vmlinux_name,
120 *kallsyms_name, 123 *kallsyms_name,
121 *source_prefix, 124 *source_prefix,
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 3ce0498bdae6..2fde0d5e40b5 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -8,6 +8,22 @@
8#include "debug.h" 8#include "debug.h"
9#include "comm.h" 9#include "comm.h"
10 10
11int thread__init_map_groups(struct thread *thread, struct machine *machine)
12{
13 struct thread *leader;
14 pid_t pid = thread->pid_;
15
16 if (pid == thread->tid) {
17 thread->mg = map_groups__new();
18 } else {
19 leader = machine__findnew_thread(machine, pid, pid);
20 if (leader)
21 thread->mg = map_groups__get(leader->mg);
22 }
23
24 return thread->mg ? 0 : -1;
25}
26
11struct thread *thread__new(pid_t pid, pid_t tid) 27struct thread *thread__new(pid_t pid, pid_t tid)
12{ 28{
13 char *comm_str; 29 char *comm_str;
@@ -15,7 +31,6 @@ struct thread *thread__new(pid_t pid, pid_t tid)
15 struct thread *thread = zalloc(sizeof(*thread)); 31 struct thread *thread = zalloc(sizeof(*thread));
16 32
17 if (thread != NULL) { 33 if (thread != NULL) {
18 map_groups__init(&thread->mg);
19 thread->pid_ = pid; 34 thread->pid_ = pid;
20 thread->tid = tid; 35 thread->tid = tid;
21 thread->ppid = -1; 36 thread->ppid = -1;
@@ -45,7 +60,8 @@ void thread__delete(struct thread *thread)
45{ 60{
46 struct comm *comm, *tmp; 61 struct comm *comm, *tmp;
47 62
48 map_groups__exit(&thread->mg); 63 map_groups__put(thread->mg);
64 thread->mg = NULL;
49 list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { 65 list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) {
50 list_del(&comm->list); 66 list_del(&comm->list);
51 comm__free(comm); 67 comm__free(comm);
@@ -111,18 +127,35 @@ int thread__comm_len(struct thread *thread)
111size_t thread__fprintf(struct thread *thread, FILE *fp) 127size_t thread__fprintf(struct thread *thread, FILE *fp)
112{ 128{
113 return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + 129 return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) +
114 map_groups__fprintf(&thread->mg, verbose, fp); 130 map_groups__fprintf(thread->mg, verbose, fp);
115} 131}
116 132
117void thread__insert_map(struct thread *thread, struct map *map) 133void thread__insert_map(struct thread *thread, struct map *map)
118{ 134{
119 map_groups__fixup_overlappings(&thread->mg, map, verbose, stderr); 135 map_groups__fixup_overlappings(thread->mg, map, verbose, stderr);
120 map_groups__insert(&thread->mg, map); 136 map_groups__insert(thread->mg, map);
137}
138
139static int thread__clone_map_groups(struct thread *thread,
140 struct thread *parent)
141{
142 int i;
143
144 /* This is new thread, we share map groups for process. */
145 if (thread->pid_ == parent->pid_)
146 return 0;
147
148 /* But this one is new process, copy maps. */
149 for (i = 0; i < MAP__NR_TYPES; ++i)
150 if (map_groups__clone(thread->mg, parent->mg, i) < 0)
151 return -ENOMEM;
152
153 return 0;
121} 154}
122 155
123int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) 156int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp)
124{ 157{
125 int i, err; 158 int err;
126 159
127 if (parent->comm_set) { 160 if (parent->comm_set) {
128 const char *comm = thread__comm_str(parent); 161 const char *comm = thread__comm_str(parent);
@@ -134,13 +167,8 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp)
134 thread->comm_set = true; 167 thread->comm_set = true;
135 } 168 }
136 169
137 for (i = 0; i < MAP__NR_TYPES; ++i)
138 if (map_groups__clone(&thread->mg, &parent->mg, i) < 0)
139 return -ENOMEM;
140
141 thread->ppid = parent->tid; 170 thread->ppid = parent->tid;
142 171 return thread__clone_map_groups(thread, parent);
143 return 0;
144} 172}
145 173
146void thread__find_cpumode_addr_location(struct thread *thread, 174void thread__find_cpumode_addr_location(struct thread *thread,
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 9b29f085aede..3c0c2724f82c 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -13,7 +13,7 @@ struct thread {
13 struct rb_node rb_node; 13 struct rb_node rb_node;
14 struct list_head node; 14 struct list_head node;
15 }; 15 };
16 struct map_groups mg; 16 struct map_groups *mg;
17 pid_t pid_; /* Not all tools update this */ 17 pid_t pid_; /* Not all tools update this */
18 pid_t tid; 18 pid_t tid;
19 pid_t ppid; 19 pid_t ppid;
@@ -30,6 +30,7 @@ struct machine;
30struct comm; 30struct comm;
31 31
32struct thread *thread__new(pid_t pid, pid_t tid); 32struct thread *thread__new(pid_t pid, pid_t tid);
33int thread__init_map_groups(struct thread *thread, struct machine *machine);
33void thread__delete(struct thread *thread); 34void thread__delete(struct thread *thread);
34static inline void thread__exited(struct thread *thread) 35static inline void thread__exited(struct thread *thread)
35{ 36{
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h
index dab14d0ad3d0..f92c37abb0a8 100644
--- a/tools/perf/util/top.h
+++ b/tools/perf/util/top.h
@@ -2,7 +2,7 @@
2#define __PERF_TOP_H 1 2#define __PERF_TOP_H 1
3 3
4#include "tool.h" 4#include "tool.h"
5#include "types.h" 5#include <linux/types.h>
6#include <stddef.h> 6#include <stddef.h>
7#include <stdbool.h> 7#include <stdbool.h>
8#include <termios.h> 8#include <termios.h>
diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h
deleted file mode 100644
index c51fa6b70a28..000000000000
--- a/tools/perf/util/types.h
+++ /dev/null
@@ -1,24 +0,0 @@
1#ifndef __PERF_TYPES_H
2#define __PERF_TYPES_H
3
4#include <stdint.h>
5
6/*
7 * We define u64 as uint64_t for every architecture
8 * so that we can print it with "%"PRIx64 without getting warnings.
9 */
10typedef uint64_t u64;
11typedef int64_t s64;
12typedef unsigned int u32;
13typedef signed int s32;
14typedef unsigned short u16;
15typedef signed short s16;
16typedef unsigned char u8;
17typedef signed char s8;
18
19union u64_swap {
20 u64 val64;
21 u32 val32[2];
22};
23
24#endif /* __PERF_TYPES_H */
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 67db73ec3dab..5ec80a575b50 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -7,7 +7,7 @@
7#include "unwind-libdw.h" 7#include "unwind-libdw.h"
8#include "machine.h" 8#include "machine.h"
9#include "thread.h" 9#include "thread.h"
10#include "types.h" 10#include <linux/types.h>
11#include "event.h" 11#include "event.h"
12#include "perf_regs.h" 12#include "perf_regs.h"
13 13
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index b031316f221a..f03061260b4e 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -1,7 +1,7 @@
1#ifndef __UNWIND_H 1#ifndef __UNWIND_H
2#define __UNWIND_H 2#define __UNWIND_H
3 3
4#include "types.h" 4#include <linux/types.h>
5#include "event.h" 5#include "event.h"
6#include "symbol.h" 6#include "symbol.h"
7 7
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 9f66549562bd..7fff6be07f07 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -166,6 +166,8 @@ static ssize_t ion(bool is_read, int fd, void *buf, size_t n)
166 ssize_t ret = is_read ? read(fd, buf, left) : 166 ssize_t ret = is_read ? read(fd, buf, left) :
167 write(fd, buf, left); 167 write(fd, buf, left);
168 168
169 if (ret < 0 && errno == EINTR)
170 continue;
169 if (ret <= 0) 171 if (ret <= 0)
170 return ret; 172 return ret;
171 173
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 6995d66f225c..b03da44e94e4 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -69,7 +69,7 @@
69#include <sys/ioctl.h> 69#include <sys/ioctl.h>
70#include <inttypes.h> 70#include <inttypes.h>
71#include <linux/magic.h> 71#include <linux/magic.h>
72#include "types.h" 72#include <linux/types.h>
73#include <sys/ttydefaults.h> 73#include <sys/ttydefaults.h>
74#include <api/fs/debugfs.h> 74#include <api/fs/debugfs.h>
75#include <termios.h> 75#include <termios.h>
diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h
index 2fa967e1a88a..b21a80c6cf8d 100644
--- a/tools/perf/util/values.h
+++ b/tools/perf/util/values.h
@@ -1,7 +1,7 @@
1#ifndef __PERF_VALUES_H 1#ifndef __PERF_VALUES_H
2#define __PERF_VALUES_H 2#define __PERF_VALUES_H
3 3
4#include "types.h" 4#include <linux/types.h>
5 5
6struct perf_read_values { 6struct perf_read_values {
7 int threads; 7 int threads;