aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2019-02-28 02:27:30 -0500
committerIngo Molnar <mingo@kernel.org>2019-02-28 02:27:30 -0500
commit0a1571243d3f150fa99c6f41f1b8e17a307a2b8b (patch)
treea38a137f96eace69709bf4b44cd7d7b548d0d9cf
parent9ed8f1a6e7670aadd5aef30456a90b456ed1b185 (diff)
parentb4409ae112caa6315f6ee678e953b9fc93e6919c (diff)
Merge tag 'perf-core-for-mingo-5.1-20190220' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: perf report: He Kuang: - Don't shadow inlined symbol with different addr range. perf script: Jiri Olsa: - Allow +- operator to ask for -F to add/remove fields to the default set, for instance to ask for the removal of the 'cpu' field in tracepoint events, adding 'period' to that kind of events, etc. perf test: Thomas Richter: - Fix scheduler tracepoint signedness of COMM fields failure of 'evsel-tp-sched' test on s390 and other arches. Tommi Rantala: - Skip trace+probe_vfs_getname.sh when 'perf trace' is not built. perf trace: Arnaldo Carvalho de Melo: - Add initial BPF map dumper, initially just for the current, minimal needs of the augmented_raw_syscalls BPF example used to collect pointer args payloads that uses BPF maps for pid and syscall filtering, but will in time have features similar to 'perf stat' --interval-print, --interval-clear, ways to signal from a BPF event that a specific map (or range of that map) should be printed, optionally as a histogram, etc. General: Jiri Olsa: - Add CPU and NUMA topologies classes for further reuse, fixing some issues in the process. - Fixup some warnings and debug levels. - Make rm_rf() remove single file, not just directories. Documentation: Jonas Rabenstein: - Fix HEADER_CMDLINE description in perf.data documentation. - Fix documentation of the Flags section in perf.data. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--tools/perf/Documentation/perf-script.txt6
-rw-r--r--tools/perf/Documentation/perf-trace.txt8
-rw-r--r--tools/perf/Documentation/perf.data-file-format.txt11
-rw-r--r--tools/perf/builtin-script.c8
-rw-r--r--tools/perf/builtin-trace.c19
-rw-r--r--tools/perf/tests/evsel-tp-sched.c6
-rw-r--r--tools/perf/tests/shell/lib/probe.sh5
-rwxr-xr-xtools/perf/tests/shell/trace+probe_vfs_getname.sh1
-rw-r--r--tools/perf/util/Build2
-rw-r--r--tools/perf/util/bpf-event.c2
-rw-r--r--tools/perf/util/bpf_map.c72
-rw-r--r--tools/perf/util/bpf_map.h22
-rw-r--r--tools/perf/util/cpumap.c2
-rw-r--r--tools/perf/util/cputopo.c277
-rw-r--r--tools/perf/util/cputopo.h33
-rw-r--r--tools/perf/util/evsel.c8
-rw-r--r--tools/perf/util/header.c269
-rw-r--r--tools/perf/util/session.c7
-rw-r--r--tools/perf/util/sort.c10
-rw-r--r--tools/perf/util/srcline.c2
-rw-r--r--tools/perf/util/util.c16
21 files changed, 530 insertions, 256 deletions
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
index 9e4def08d569..2e19fd7ffe35 100644
--- a/tools/perf/Documentation/perf-script.txt
+++ b/tools/perf/Documentation/perf-script.txt
@@ -159,6 +159,12 @@ OPTIONS
159 the override, and the result of the above is that only S/W and H/W 159 the override, and the result of the above is that only S/W and H/W
160 events are displayed with the given fields. 160 events are displayed with the given fields.
161 161
162 It's possible tp add/remove fields only for specific event type:
163
164 -Fsw:-cpu,-period
165
166 removes cpu and period from software events.
167
162 For the 'wildcard' option if a user selected field is invalid for an 168 For the 'wildcard' option if a user selected field is invalid for an
163 event type, a message is displayed to the user that the option is 169 event type, a message is displayed to the user that the option is
164 ignored for that type. For example: 170 ignored for that type. For example:
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt
index 631e687be4eb..fc6e43262c41 100644
--- a/tools/perf/Documentation/perf-trace.txt
+++ b/tools/perf/Documentation/perf-trace.txt
@@ -210,6 +210,14 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
210 may happen, for instance, when a thread gets migrated to a different CPU 210 may happen, for instance, when a thread gets migrated to a different CPU
211 while processing a syscall. 211 while processing a syscall.
212 212
213--map-dump::
214 Dump BPF maps setup by events passed via -e, for instance the augmented_raw_syscalls
215 living in tools/perf/examples/bpf/augmented_raw_syscalls.c. For now this
216 dumps just boolean map values and integer keys, in time this will print in hex
217 by default and use BTF when available, as well as use functions to do pretty
218 printing using the existing 'perf trace' syscall arg beautifiers to map integer
219 arguments to strings (pid to comm, syscall id to syscall name, etc).
220
213 221
214PAGEFAULTS 222PAGEFAULTS
215---------- 223----------
diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt
index dfb218feaad9..593ef49b273c 100644
--- a/tools/perf/Documentation/perf.data-file-format.txt
+++ b/tools/perf/Documentation/perf.data-file-format.txt
@@ -43,11 +43,10 @@ struct perf_file_section {
43 43
44Flags section: 44Flags section:
45 45
46The header is followed by different optional headers, described by the bits set 46For each of the optional features a perf_file_section it placed after the data
47in flags. Only headers for which the bit is set are included. Each header 47section if the feature bit is set in the perf_header flags bitset. The
48consists of a perf_file_section located after the initial header. 48respective perf_file_section points to the data of the additional header and
49The respective perf_file_section points to the data of the additional 49defines its size.
50header and defines its size.
51 50
52Some headers consist of strings, which are defined like this: 51Some headers consist of strings, which are defined like this:
53 52
@@ -131,7 +130,7 @@ An uint64_t with the total memory in bytes.
131 130
132 HEADER_CMDLINE = 11, 131 HEADER_CMDLINE = 11,
133 132
134A perf_header_string with the perf command line used to collect the data. 133A perf_header_string_list with the perf arg-vector used to collect the data.
135 134
136 HEADER_EVENT_DESC = 12, 135 HEADER_EVENT_DESC = 12,
137 136
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 8d5fe092525c..373ea151dc60 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -2560,6 +2560,10 @@ static int parse_output_fields(const struct option *opt __maybe_unused,
2560 pr_warning("Overriding previous field request for %s events.\n", 2560 pr_warning("Overriding previous field request for %s events.\n",
2561 event_type(type)); 2561 event_type(type));
2562 2562
2563 /* Don't override defaults for +- */
2564 if (strchr(tok, '+') || strchr(tok, '-'))
2565 goto parse;
2566
2563 output[type].fields = 0; 2567 output[type].fields = 0;
2564 output[type].user_set = true; 2568 output[type].user_set = true;
2565 output[type].wildcard_set = false; 2569 output[type].wildcard_set = false;
@@ -2644,6 +2648,10 @@ parse:
2644 rc = -EINVAL; 2648 rc = -EINVAL;
2645 goto out; 2649 goto out;
2646 } 2650 }
2651 if (change == REMOVE)
2652 output[type].fields &= ~all_output_options[i].field;
2653 else
2654 output[type].fields |= all_output_options[i].field;
2647 output[type].user_set = true; 2655 output[type].user_set = true;
2648 output[type].wildcard_set = true; 2656 output[type].wildcard_set = true;
2649 } 2657 }
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 68a01e624ad3..1a11fe656afc 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -19,6 +19,7 @@
19#include <traceevent/event-parse.h> 19#include <traceevent/event-parse.h>
20#include <api/fs/tracing_path.h> 20#include <api/fs/tracing_path.h>
21#include <bpf/bpf.h> 21#include <bpf/bpf.h>
22#include "util/bpf_map.h"
22#include "builtin.h" 23#include "builtin.h"
23#include "util/cgroup.h" 24#include "util/cgroup.h"
24#include "util/color.h" 25#include "util/color.h"
@@ -87,6 +88,9 @@ struct trace {
87 *augmented; 88 *augmented;
88 } events; 89 } events;
89 } syscalls; 90 } syscalls;
91 struct {
92 struct bpf_map *map;
93 } dump;
90 struct record_opts opts; 94 struct record_opts opts;
91 struct perf_evlist *evlist; 95 struct perf_evlist *evlist;
92 struct machine *host; 96 struct machine *host;
@@ -2997,6 +3001,9 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
2997 if (err < 0) 3001 if (err < 0)
2998 goto out_error_apply_filters; 3002 goto out_error_apply_filters;
2999 3003
3004 if (trace->dump.map)
3005 bpf_map__fprintf(trace->dump.map, trace->output);
3006
3000 err = perf_evlist__mmap(evlist, trace->opts.mmap_pages); 3007 err = perf_evlist__mmap(evlist, trace->opts.mmap_pages);
3001 if (err < 0) 3008 if (err < 0)
3002 goto out_error_mmap; 3009 goto out_error_mmap;
@@ -3686,6 +3693,7 @@ int cmd_trace(int argc, const char **argv)
3686 .max_stack = UINT_MAX, 3693 .max_stack = UINT_MAX,
3687 .max_events = ULONG_MAX, 3694 .max_events = ULONG_MAX,
3688 }; 3695 };
3696 const char *map_dump_str = NULL;
3689 const char *output_name = NULL; 3697 const char *output_name = NULL;
3690 const struct option trace_options[] = { 3698 const struct option trace_options[] = {
3691 OPT_CALLBACK('e', "event", &trace, "event", 3699 OPT_CALLBACK('e', "event", &trace, "event",
@@ -3718,6 +3726,9 @@ int cmd_trace(int argc, const char **argv)
3718 OPT_CALLBACK(0, "duration", &trace, "float", 3726 OPT_CALLBACK(0, "duration", &trace, "float",
3719 "show only events with duration > N.M ms", 3727 "show only events with duration > N.M ms",
3720 trace__set_duration), 3728 trace__set_duration),
3729#ifdef HAVE_LIBBPF_SUPPORT
3730 OPT_STRING(0, "map-dump", &map_dump_str, "BPF map", "BPF map to periodically dump"),
3731#endif
3721 OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), 3732 OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"),
3722 OPT_INCR('v', "verbose", &verbose, "be more verbose"), 3733 OPT_INCR('v', "verbose", &verbose, "be more verbose"),
3723 OPT_BOOLEAN('T', "time", &trace.full_time, 3734 OPT_BOOLEAN('T', "time", &trace.full_time,
@@ -3812,6 +3823,14 @@ int cmd_trace(int argc, const char **argv)
3812 3823
3813 err = -1; 3824 err = -1;
3814 3825
3826 if (map_dump_str) {
3827 trace.dump.map = bpf__find_map_by_name(map_dump_str);
3828 if (trace.dump.map == NULL) {
3829 pr_err("ERROR: BPF map \"%s\" not found\n", map_dump_str);
3830 goto out;
3831 }
3832 }
3833
3815 if (trace.trace_pgfaults) { 3834 if (trace.trace_pgfaults) {
3816 trace.opts.sample_address = true; 3835 trace.opts.sample_address = true;
3817 trace.opts.sample_time = true; 3836 trace.opts.sample_time = true;
diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c
index 5cbba70bcdd0..ea7acf403727 100644
--- a/tools/perf/tests/evsel-tp-sched.c
+++ b/tools/perf/tests/evsel-tp-sched.c
@@ -43,7 +43,7 @@ int test__perf_evsel__tp_sched_test(struct test *test __maybe_unused, int subtes
43 return -1; 43 return -1;
44 } 44 }
45 45
46 if (perf_evsel__test_field(evsel, "prev_comm", 16, true)) 46 if (perf_evsel__test_field(evsel, "prev_comm", 16, false))
47 ret = -1; 47 ret = -1;
48 48
49 if (perf_evsel__test_field(evsel, "prev_pid", 4, true)) 49 if (perf_evsel__test_field(evsel, "prev_pid", 4, true))
@@ -55,7 +55,7 @@ int test__perf_evsel__tp_sched_test(struct test *test __maybe_unused, int subtes
55 if (perf_evsel__test_field(evsel, "prev_state", sizeof(long), true)) 55 if (perf_evsel__test_field(evsel, "prev_state", sizeof(long), true))
56 ret = -1; 56 ret = -1;
57 57
58 if (perf_evsel__test_field(evsel, "next_comm", 16, true)) 58 if (perf_evsel__test_field(evsel, "next_comm", 16, false))
59 ret = -1; 59 ret = -1;
60 60
61 if (perf_evsel__test_field(evsel, "next_pid", 4, true)) 61 if (perf_evsel__test_field(evsel, "next_pid", 4, true))
@@ -73,7 +73,7 @@ int test__perf_evsel__tp_sched_test(struct test *test __maybe_unused, int subtes
73 return -1; 73 return -1;
74 } 74 }
75 75
76 if (perf_evsel__test_field(evsel, "comm", 16, true)) 76 if (perf_evsel__test_field(evsel, "comm", 16, false))
77 ret = -1; 77 ret = -1;
78 78
79 if (perf_evsel__test_field(evsel, "pid", 4, true)) 79 if (perf_evsel__test_field(evsel, "pid", 4, true))
diff --git a/tools/perf/tests/shell/lib/probe.sh b/tools/perf/tests/shell/lib/probe.sh
index 6293cc660947..e37787be672b 100644
--- a/tools/perf/tests/shell/lib/probe.sh
+++ b/tools/perf/tests/shell/lib/probe.sh
@@ -4,3 +4,8 @@ skip_if_no_perf_probe() {
4 perf probe 2>&1 | grep -q 'is not a perf-command' && return 2 4 perf probe 2>&1 | grep -q 'is not a perf-command' && return 2
5 return 0 5 return 0
6} 6}
7
8skip_if_no_perf_trace() {
9 perf trace -h 2>&1 | grep -q -e 'is not a perf-command' -e 'trace command not available' && return 2
10 return 0
11}
diff --git a/tools/perf/tests/shell/trace+probe_vfs_getname.sh b/tools/perf/tests/shell/trace+probe_vfs_getname.sh
index 50109f27ca07..147efeb6b195 100755
--- a/tools/perf/tests/shell/trace+probe_vfs_getname.sh
+++ b/tools/perf/tests/shell/trace+probe_vfs_getname.sh
@@ -12,6 +12,7 @@
12. $(dirname $0)/lib/probe.sh 12. $(dirname $0)/lib/probe.sh
13 13
14skip_if_no_perf_probe || exit 2 14skip_if_no_perf_probe || exit 2
15skip_if_no_perf_trace || exit 2
15 16
16. $(dirname $0)/lib/probe_vfs_getname.sh 17. $(dirname $0)/lib/probe_vfs_getname.sh
17 18
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index ca0741c91903..8dd3102301ea 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -69,6 +69,7 @@ perf-y += hist.o
69perf-y += util.o 69perf-y += util.o
70perf-y += xyarray.o 70perf-y += xyarray.o
71perf-y += cpumap.o 71perf-y += cpumap.o
72perf-y += cputopo.o
72perf-y += cgroup.o 73perf-y += cgroup.o
73perf-y += target.o 74perf-y += target.o
74perf-y += rblist.o 75perf-y += rblist.o
@@ -114,6 +115,7 @@ perf-y += branch.o
114perf-y += mem2node.o 115perf-y += mem2node.o
115 116
116perf-$(CONFIG_LIBBPF) += bpf-loader.o 117perf-$(CONFIG_LIBBPF) += bpf-loader.o
118perf-$(CONFIG_LIBBPF) += bpf_map.o
117perf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o 119perf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o
118perf-$(CONFIG_LIBELF) += symbol-elf.o 120perf-$(CONFIG_LIBELF) += symbol-elf.o
119perf-$(CONFIG_LIBELF) += probe-file.o 121perf-$(CONFIG_LIBELF) += probe-file.o
diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
index 62dda96b0096..028c8ec1f62a 100644
--- a/tools/perf/util/bpf-event.c
+++ b/tools/perf/util/bpf-event.c
@@ -233,7 +233,7 @@ int perf_event__synthesize_bpf_events(struct perf_tool *tool,
233 err = 0; 233 err = 0;
234 break; 234 break;
235 } 235 }
236 pr_debug("%s: can't get next program: %s%s", 236 pr_debug("%s: can't get next program: %s%s\n",
237 __func__, strerror(errno), 237 __func__, strerror(errno),
238 errno == EINVAL ? " -- kernel too old?" : ""); 238 errno == EINVAL ? " -- kernel too old?" : "");
239 /* don't report error on old kernel or EPERM */ 239 /* don't report error on old kernel or EPERM */
diff --git a/tools/perf/util/bpf_map.c b/tools/perf/util/bpf_map.c
new file mode 100644
index 000000000000..eb853ca67cf4
--- /dev/null
+++ b/tools/perf/util/bpf_map.c
@@ -0,0 +1,72 @@
1// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2
3#include "util/bpf_map.h"
4#include <bpf/bpf.h>
5#include <bpf/libbpf.h>
6#include <linux/err.h>
7#include <linux/kernel.h>
8#include <stdbool.h>
9#include <stdlib.h>
10#include <unistd.h>
11
12static bool bpf_map_def__is_per_cpu(const struct bpf_map_def *def)
13{
14 return def->type == BPF_MAP_TYPE_PERCPU_HASH ||
15 def->type == BPF_MAP_TYPE_PERCPU_ARRAY ||
16 def->type == BPF_MAP_TYPE_LRU_PERCPU_HASH ||
17 def->type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE;
18}
19
20static void *bpf_map_def__alloc_value(const struct bpf_map_def *def)
21{
22 if (bpf_map_def__is_per_cpu(def))
23 return malloc(round_up(def->value_size, 8) * sysconf(_SC_NPROCESSORS_CONF));
24
25 return malloc(def->value_size);
26}
27
28int bpf_map__fprintf(struct bpf_map *map, FILE *fp)
29{
30 const struct bpf_map_def *def = bpf_map__def(map);
31 void *prev_key = NULL, *key, *value;
32 int fd = bpf_map__fd(map), err;
33 int printed = 0;
34
35 if (fd < 0)
36 return fd;
37
38 if (IS_ERR(def))
39 return PTR_ERR(def);
40
41 err = -ENOMEM;
42 key = malloc(def->key_size);
43 if (key == NULL)
44 goto out;
45
46 value = bpf_map_def__alloc_value(def);
47 if (value == NULL)
48 goto out_free_key;
49
50 while ((err = bpf_map_get_next_key(fd, prev_key, key) == 0)) {
51 int intkey = *(int *)key;
52
53 if (!bpf_map_lookup_elem(fd, key, value)) {
54 bool boolval = *(bool *)value;
55 if (boolval)
56 printed += fprintf(fp, "[%d] = %d,\n", intkey, boolval);
57 } else {
58 printed += fprintf(fp, "[%d] = ERROR,\n", intkey);
59 }
60
61 prev_key = key;
62 }
63
64 if (err == ENOENT)
65 err = printed;
66
67 free(value);
68out_free_key:
69 free(key);
70out:
71 return err;
72}
diff --git a/tools/perf/util/bpf_map.h b/tools/perf/util/bpf_map.h
new file mode 100644
index 000000000000..d6abd5e47af8
--- /dev/null
+++ b/tools/perf/util/bpf_map.h
@@ -0,0 +1,22 @@
1// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2#ifndef __PERF_BPF_MAP_H
3#define __PERF_BPF_MAP_H 1
4
5#include <stdio.h>
6#include <linux/compiler.h>
7struct bpf_map;
8
9#ifdef HAVE_LIBBPF_SUPPORT
10
11int bpf_map__fprintf(struct bpf_map *map, FILE *fp);
12
13#else
14
15static inline int bpf_map__fprintf(struct bpf_map *map __maybe_unused, FILE *fp __maybe_unused)
16{
17 return 0;
18}
19
20#endif // HAVE_LIBBPF_SUPPORT
21
22#endif // __PERF_BPF_MAP_H
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 0bbc3feb0894..0b599229bc7e 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -681,7 +681,7 @@ size_t cpu_map__snprint(struct cpu_map *map, char *buf, size_t size)
681 681
682#undef COMMA 682#undef COMMA
683 683
684 pr_debug("cpumask list: %s\n", buf); 684 pr_debug2("cpumask list: %s\n", buf);
685 return ret; 685 return ret;
686} 686}
687 687
diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c
new file mode 100644
index 000000000000..ece0710249d4
--- /dev/null
+++ b/tools/perf/util/cputopo.c
@@ -0,0 +1,277 @@
1// SPDX-License-Identifier: GPL-2.0
2#include <sys/param.h>
3#include <inttypes.h>
4#include <api/fs/fs.h>
5
6#include "cputopo.h"
7#include "cpumap.h"
8#include "util.h"
9#include "env.h"
10
11
12#define CORE_SIB_FMT \
13 "%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
14#define THRD_SIB_FMT \
15 "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
16#define NODE_ONLINE_FMT \
17 "%s/devices/system/node/online"
18#define NODE_MEMINFO_FMT \
19 "%s/devices/system/node/node%d/meminfo"
20#define NODE_CPULIST_FMT \
21 "%s/devices/system/node/node%d/cpulist"
22
23static int build_cpu_topology(struct cpu_topology *tp, int cpu)
24{
25 FILE *fp;
26 char filename[MAXPATHLEN];
27 char *buf = NULL, *p;
28 size_t len = 0;
29 ssize_t sret;
30 u32 i = 0;
31 int ret = -1;
32
33 scnprintf(filename, MAXPATHLEN, CORE_SIB_FMT,
34 sysfs__mountpoint(), cpu);
35 fp = fopen(filename, "r");
36 if (!fp)
37 goto try_threads;
38
39 sret = getline(&buf, &len, fp);
40 fclose(fp);
41 if (sret <= 0)
42 goto try_threads;
43
44 p = strchr(buf, '\n');
45 if (p)
46 *p = '\0';
47
48 for (i = 0; i < tp->core_sib; i++) {
49 if (!strcmp(buf, tp->core_siblings[i]))
50 break;
51 }
52 if (i == tp->core_sib) {
53 tp->core_siblings[i] = buf;
54 tp->core_sib++;
55 buf = NULL;
56 len = 0;
57 }
58 ret = 0;
59
60try_threads:
61 scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT,
62 sysfs__mountpoint(), cpu);
63 fp = fopen(filename, "r");
64 if (!fp)
65 goto done;
66
67 if (getline(&buf, &len, fp) <= 0)
68 goto done;
69
70 p = strchr(buf, '\n');
71 if (p)
72 *p = '\0';
73
74 for (i = 0; i < tp->thread_sib; i++) {
75 if (!strcmp(buf, tp->thread_siblings[i]))
76 break;
77 }
78 if (i == tp->thread_sib) {
79 tp->thread_siblings[i] = buf;
80 tp->thread_sib++;
81 buf = NULL;
82 }
83 ret = 0;
84done:
85 if (fp)
86 fclose(fp);
87 free(buf);
88 return ret;
89}
90
91void cpu_topology__delete(struct cpu_topology *tp)
92{
93 u32 i;
94
95 if (!tp)
96 return;
97
98 for (i = 0 ; i < tp->core_sib; i++)
99 zfree(&tp->core_siblings[i]);
100
101 for (i = 0 ; i < tp->thread_sib; i++)
102 zfree(&tp->thread_siblings[i]);
103
104 free(tp);
105}
106
107struct cpu_topology *cpu_topology__new(void)
108{
109 struct cpu_topology *tp = NULL;
110 void *addr;
111 u32 nr, i;
112 size_t sz;
113 long ncpus;
114 int ret = -1;
115 struct cpu_map *map;
116
117 ncpus = cpu__max_present_cpu();
118
119 /* build online CPU map */
120 map = cpu_map__new(NULL);
121 if (map == NULL) {
122 pr_debug("failed to get system cpumap\n");
123 return NULL;
124 }
125
126 nr = (u32)(ncpus & UINT_MAX);
127
128 sz = nr * sizeof(char *);
129 addr = calloc(1, sizeof(*tp) + 2 * sz);
130 if (!addr)
131 goto out_free;
132
133 tp = addr;
134 addr += sizeof(*tp);
135 tp->core_siblings = addr;
136 addr += sz;
137 tp->thread_siblings = addr;
138
139 for (i = 0; i < nr; i++) {
140 if (!cpu_map__has(map, i))
141 continue;
142
143 ret = build_cpu_topology(tp, i);
144 if (ret < 0)
145 break;
146 }
147
148out_free:
149 cpu_map__put(map);
150 if (ret) {
151 cpu_topology__delete(tp);
152 tp = NULL;
153 }
154 return tp;
155}
156
157static int load_numa_node(struct numa_topology_node *node, int nr)
158{
159 char str[MAXPATHLEN];
160 char field[32];
161 char *buf = NULL, *p;
162 size_t len = 0;
163 int ret = -1;
164 FILE *fp;
165 u64 mem;
166
167 node->node = (u32) nr;
168
169 scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
170 sysfs__mountpoint(), nr);
171 fp = fopen(str, "r");
172 if (!fp)
173 return -1;
174
175 while (getline(&buf, &len, fp) > 0) {
176 /* skip over invalid lines */
177 if (!strchr(buf, ':'))
178 continue;
179 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
180 goto err;
181 if (!strcmp(field, "MemTotal:"))
182 node->mem_total = mem;
183 if (!strcmp(field, "MemFree:"))
184 node->mem_free = mem;
185 if (node->mem_total && node->mem_free)
186 break;
187 }
188
189 fclose(fp);
190 fp = NULL;
191
192 scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
193 sysfs__mountpoint(), nr);
194
195 fp = fopen(str, "r");
196 if (!fp)
197 return -1;
198
199 if (getline(&buf, &len, fp) <= 0)
200 goto err;
201
202 p = strchr(buf, '\n');
203 if (p)
204 *p = '\0';
205
206 node->cpus = buf;
207 fclose(fp);
208 return 0;
209
210err:
211 free(buf);
212 if (fp)
213 fclose(fp);
214 return ret;
215}
216
217struct numa_topology *numa_topology__new(void)
218{
219 struct cpu_map *node_map = NULL;
220 struct numa_topology *tp = NULL;
221 char path[MAXPATHLEN];
222 char *buf = NULL;
223 size_t len = 0;
224 u32 nr, i;
225 FILE *fp;
226 char *c;
227
228 scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
229 sysfs__mountpoint());
230
231 fp = fopen(path, "r");
232 if (!fp)
233 return NULL;
234
235 if (getline(&buf, &len, fp) <= 0)
236 goto out;
237
238 c = strchr(buf, '\n');
239 if (c)
240 *c = '\0';
241
242 node_map = cpu_map__new(buf);
243 if (!node_map)
244 goto out;
245
246 nr = (u32) node_map->nr;
247
248 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
249 if (!tp)
250 goto out;
251
252 tp->nr = nr;
253
254 for (i = 0; i < nr; i++) {
255 if (load_numa_node(&tp->nodes[i], node_map->map[i])) {
256 numa_topology__delete(tp);
257 tp = NULL;
258 break;
259 }
260 }
261
262out:
263 free(buf);
264 fclose(fp);
265 cpu_map__put(node_map);
266 return tp;
267}
268
269void numa_topology__delete(struct numa_topology *tp)
270{
271 u32 i;
272
273 for (i = 0; i < tp->nr; i++)
274 free(tp->nodes[i].cpus);
275
276 free(tp);
277}
diff --git a/tools/perf/util/cputopo.h b/tools/perf/util/cputopo.h
new file mode 100644
index 000000000000..47a97e71acdf
--- /dev/null
+++ b/tools/perf/util/cputopo.h
@@ -0,0 +1,33 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef __PERF_CPUTOPO_H
3#define __PERF_CPUTOPO_H
4
5#include <linux/types.h>
6#include "env.h"
7
8struct cpu_topology {
9 u32 core_sib;
10 u32 thread_sib;
11 char **core_siblings;
12 char **thread_siblings;
13};
14
15struct numa_topology_node {
16 char *cpus;
17 u32 node;
18 u64 mem_total;
19 u64 mem_free;
20};
21
22struct numa_topology {
23 u32 nr;
24 struct numa_topology_node nodes[0];
25};
26
27struct cpu_topology *cpu_topology__new(void);
28void cpu_topology__delete(struct cpu_topology *tp);
29
30struct numa_topology *numa_topology__new(void);
31void numa_topology__delete(struct numa_topology *tp);
32
33#endif /* __PERF_CPUTOPO_H */
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 684c893ca6bc..dfe2958e6287 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -956,6 +956,14 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
956 attr->sample_freq = 0; 956 attr->sample_freq = 0;
957 attr->sample_period = 0; 957 attr->sample_period = 0;
958 attr->write_backward = 0; 958 attr->write_backward = 0;
959
960 /*
961 * We don't get sample for slave events, we make them
962 * when delivering group leader sample. Set the slave
963 * event to follow the master sample_type to ease up
964 * report.
965 */
966 attr->sample_type = leader->attr.sample_type;
959 } 967 }
960 968
961 if (opts->no_samples) 969 if (opts->no_samples)
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 61ce197c5362..a2323d777dae 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -39,6 +39,7 @@
39#include "tool.h" 39#include "tool.h"
40#include "time-utils.h" 40#include "time-utils.h"
41#include "units.h" 41#include "units.h"
42#include "cputopo.h"
42 43
43#include "sane_ctype.h" 44#include "sane_ctype.h"
44 45
@@ -557,158 +558,15 @@ static int write_cmdline(struct feat_fd *ff,
557 return 0; 558 return 0;
558} 559}
559 560
560#define CORE_SIB_FMT \
561 "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list"
562#define THRD_SIB_FMT \
563 "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list"
564
565struct cpu_topo {
566 u32 core_sib;
567 u32 thread_sib;
568 char **core_siblings;
569 char **thread_siblings;
570};
571
572static int build_cpu_topo(struct cpu_topo *tp, int cpu)
573{
574 FILE *fp;
575 char filename[MAXPATHLEN];
576 char *buf = NULL, *p;
577 size_t len = 0;
578 ssize_t sret;
579 u32 i = 0;
580 int ret = -1;
581
582 sprintf(filename, CORE_SIB_FMT, cpu);
583 fp = fopen(filename, "r");
584 if (!fp)
585 goto try_threads;
586
587 sret = getline(&buf, &len, fp);
588 fclose(fp);
589 if (sret <= 0)
590 goto try_threads;
591
592 p = strchr(buf, '\n');
593 if (p)
594 *p = '\0';
595
596 for (i = 0; i < tp->core_sib; i++) {
597 if (!strcmp(buf, tp->core_siblings[i]))
598 break;
599 }
600 if (i == tp->core_sib) {
601 tp->core_siblings[i] = buf;
602 tp->core_sib++;
603 buf = NULL;
604 len = 0;
605 }
606 ret = 0;
607
608try_threads:
609 sprintf(filename, THRD_SIB_FMT, cpu);
610 fp = fopen(filename, "r");
611 if (!fp)
612 goto done;
613
614 if (getline(&buf, &len, fp) <= 0)
615 goto done;
616
617 p = strchr(buf, '\n');
618 if (p)
619 *p = '\0';
620
621 for (i = 0; i < tp->thread_sib; i++) {
622 if (!strcmp(buf, tp->thread_siblings[i]))
623 break;
624 }
625 if (i == tp->thread_sib) {
626 tp->thread_siblings[i] = buf;
627 tp->thread_sib++;
628 buf = NULL;
629 }
630 ret = 0;
631done:
632 if(fp)
633 fclose(fp);
634 free(buf);
635 return ret;
636}
637
638static void free_cpu_topo(struct cpu_topo *tp)
639{
640 u32 i;
641
642 if (!tp)
643 return;
644
645 for (i = 0 ; i < tp->core_sib; i++)
646 zfree(&tp->core_siblings[i]);
647
648 for (i = 0 ; i < tp->thread_sib; i++)
649 zfree(&tp->thread_siblings[i]);
650
651 free(tp);
652}
653
654static struct cpu_topo *build_cpu_topology(void)
655{
656 struct cpu_topo *tp = NULL;
657 void *addr;
658 u32 nr, i;
659 size_t sz;
660 long ncpus;
661 int ret = -1;
662 struct cpu_map *map;
663
664 ncpus = cpu__max_present_cpu();
665
666 /* build online CPU map */
667 map = cpu_map__new(NULL);
668 if (map == NULL) {
669 pr_debug("failed to get system cpumap\n");
670 return NULL;
671 }
672
673 nr = (u32)(ncpus & UINT_MAX);
674
675 sz = nr * sizeof(char *);
676 addr = calloc(1, sizeof(*tp) + 2 * sz);
677 if (!addr)
678 goto out_free;
679
680 tp = addr;
681 addr += sizeof(*tp);
682 tp->core_siblings = addr;
683 addr += sz;
684 tp->thread_siblings = addr;
685
686 for (i = 0; i < nr; i++) {
687 if (!cpu_map__has(map, i))
688 continue;
689
690 ret = build_cpu_topo(tp, i);
691 if (ret < 0)
692 break;
693 }
694
695out_free:
696 cpu_map__put(map);
697 if (ret) {
698 free_cpu_topo(tp);
699 tp = NULL;
700 }
701 return tp;
702}
703 561
704static int write_cpu_topology(struct feat_fd *ff, 562static int write_cpu_topology(struct feat_fd *ff,
705 struct perf_evlist *evlist __maybe_unused) 563 struct perf_evlist *evlist __maybe_unused)
706{ 564{
707 struct cpu_topo *tp; 565 struct cpu_topology *tp;
708 u32 i; 566 u32 i;
709 int ret, j; 567 int ret, j;
710 568
711 tp = build_cpu_topology(); 569 tp = cpu_topology__new();
712 if (!tp) 570 if (!tp)
713 return -1; 571 return -1;
714 572
@@ -746,7 +604,7 @@ static int write_cpu_topology(struct feat_fd *ff,
746 return ret; 604 return ret;
747 } 605 }
748done: 606done:
749 free_cpu_topo(tp); 607 cpu_topology__delete(tp);
750 return ret; 608 return ret;
751} 609}
752 610
@@ -781,112 +639,45 @@ static int write_total_mem(struct feat_fd *ff,
781 return ret; 639 return ret;
782} 640}
783 641
784static int write_topo_node(struct feat_fd *ff, int node)
785{
786 char str[MAXPATHLEN];
787 char field[32];
788 char *buf = NULL, *p;
789 size_t len = 0;
790 FILE *fp;
791 u64 mem_total, mem_free, mem;
792 int ret = -1;
793
794 sprintf(str, "/sys/devices/system/node/node%d/meminfo", node);
795 fp = fopen(str, "r");
796 if (!fp)
797 return -1;
798
799 while (getline(&buf, &len, fp) > 0) {
800 /* skip over invalid lines */
801 if (!strchr(buf, ':'))
802 continue;
803 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
804 goto done;
805 if (!strcmp(field, "MemTotal:"))
806 mem_total = mem;
807 if (!strcmp(field, "MemFree:"))
808 mem_free = mem;
809 }
810
811 fclose(fp);
812 fp = NULL;
813
814 ret = do_write(ff, &mem_total, sizeof(u64));
815 if (ret)
816 goto done;
817
818 ret = do_write(ff, &mem_free, sizeof(u64));
819 if (ret)
820 goto done;
821
822 ret = -1;
823 sprintf(str, "/sys/devices/system/node/node%d/cpulist", node);
824
825 fp = fopen(str, "r");
826 if (!fp)
827 goto done;
828
829 if (getline(&buf, &len, fp) <= 0)
830 goto done;
831
832 p = strchr(buf, '\n');
833 if (p)
834 *p = '\0';
835
836 ret = do_write_string(ff, buf);
837done:
838 free(buf);
839 if (fp)
840 fclose(fp);
841 return ret;
842}
843
844static int write_numa_topology(struct feat_fd *ff, 642static int write_numa_topology(struct feat_fd *ff,
845 struct perf_evlist *evlist __maybe_unused) 643 struct perf_evlist *evlist __maybe_unused)
846{ 644{
847 char *buf = NULL; 645 struct numa_topology *tp;
848 size_t len = 0;
849 FILE *fp;
850 struct cpu_map *node_map = NULL;
851 char *c;
852 u32 nr, i, j;
853 int ret = -1; 646 int ret = -1;
647 u32 i;
854 648
855 fp = fopen("/sys/devices/system/node/online", "r"); 649 tp = numa_topology__new();
856 if (!fp) 650 if (!tp)
857 return -1; 651 return -ENOMEM;
858
859 if (getline(&buf, &len, fp) <= 0)
860 goto done;
861 652
862 c = strchr(buf, '\n'); 653 ret = do_write(ff, &tp->nr, sizeof(u32));
863 if (c) 654 if (ret < 0)
864 *c = '\0'; 655 goto err;
865 656
866 node_map = cpu_map__new(buf); 657 for (i = 0; i < tp->nr; i++) {
867 if (!node_map) 658 struct numa_topology_node *n = &tp->nodes[i];
868 goto done;
869 659
870 nr = (u32)node_map->nr; 660 ret = do_write(ff, &n->node, sizeof(u32));
661 if (ret < 0)
662 goto err;
871 663
872 ret = do_write(ff, &nr, sizeof(nr)); 664 ret = do_write(ff, &n->mem_total, sizeof(u64));
873 if (ret < 0) 665 if (ret)
874 goto done; 666 goto err;
875 667
876 for (i = 0; i < nr; i++) { 668 ret = do_write(ff, &n->mem_free, sizeof(u64));
877 j = (u32)node_map->map[i]; 669 if (ret)
878 ret = do_write(ff, &j, sizeof(j)); 670 goto err;
879 if (ret < 0)
880 break;
881 671
882 ret = write_topo_node(ff, i); 672 ret = do_write_string(ff, n->cpus);
883 if (ret < 0) 673 if (ret < 0)
884 break; 674 goto err;
885 } 675 }
886done: 676
887 free(buf); 677 ret = 0;
888 fclose(fp); 678
889 cpu_map__put(node_map); 679err:
680 numa_topology__delete(tp);
890 return ret; 681 return ret;
891} 682}
892 683
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 18fb9c8cbf9c..c764bbc91009 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1202,6 +1202,13 @@ static int deliver_sample_value(struct perf_evlist *evlist,
1202 return 0; 1202 return 0;
1203 } 1203 }
1204 1204
1205 /*
1206 * There's no reason to deliver sample
1207 * for zero period, bail out.
1208 */
1209 if (!sample->period)
1210 return 0;
1211
1205 return tool->sample(tool, event, sample, sid->evsel, machine); 1212 return tool->sample(tool, event, sample, sid->evsel, machine);
1206} 1213}
1207 1214
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 2b6c1ccb878c..d2299e912e59 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -231,8 +231,14 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
231 if (sym_l == sym_r) 231 if (sym_l == sym_r)
232 return 0; 232 return 0;
233 233
234 if (sym_l->inlined || sym_r->inlined) 234 if (sym_l->inlined || sym_r->inlined) {
235 return strcmp(sym_l->name, sym_r->name); 235 int ret = strcmp(sym_l->name, sym_r->name);
236
237 if (ret)
238 return ret;
239 if ((sym_l->start <= sym_r->end) && (sym_l->end >= sym_r->start))
240 return 0;
241 }
236 242
237 if (sym_l->start != sym_r->start) 243 if (sym_l->start != sym_r->start)
238 return (int64_t)(sym_r->start - sym_l->start); 244 return (int64_t)(sym_r->start - sym_l->start);
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index 00f215580b5a..10ca1533937e 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -104,7 +104,7 @@ static struct symbol *new_inline_sym(struct dso *dso,
104 } else { 104 } else {
105 /* create a fake symbol for the inline frame */ 105 /* create a fake symbol for the inline frame */
106 inline_sym = symbol__new(base_sym ? base_sym->start : 0, 106 inline_sym = symbol__new(base_sym ? base_sym->start : 0,
107 base_sym ? base_sym->end : 0, 107 base_sym ? (base_sym->end - base_sym->start) : 0,
108 base_sym ? base_sym->binding : 0, 108 base_sym ? base_sym->binding : 0,
109 base_sym ? base_sym->type : 0, 109 base_sym ? base_sym->type : 0,
110 funcname); 110 funcname);
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 320b0fef249a..3ee410fc047a 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -120,16 +120,26 @@ int mkdir_p(char *path, mode_t mode)
120int rm_rf(const char *path) 120int rm_rf(const char *path)
121{ 121{
122 DIR *dir; 122 DIR *dir;
123 int ret = 0; 123 int ret;
124 struct dirent *d; 124 struct dirent *d;
125 char namebuf[PATH_MAX]; 125 char namebuf[PATH_MAX];
126 struct stat statbuf;
126 127
128 /* Do not fail if there's no file. */
129 ret = lstat(path, &statbuf);
130 if (ret)
131 return 0;
132
133 /* Try to remove any file we get. */
134 if (!(statbuf.st_mode & S_IFDIR))
135 return unlink(path);
136
137 /* We have directory in path. */
127 dir = opendir(path); 138 dir = opendir(path);
128 if (dir == NULL) 139 if (dir == NULL)
129 return 0; 140 return -1;
130 141
131 while ((d = readdir(dir)) != NULL && !ret) { 142 while ((d = readdir(dir)) != NULL && !ret) {
132 struct stat statbuf;
133 143
134 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) 144 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
135 continue; 145 continue;