diff options
author | Ingo Molnar <mingo@kernel.org> | 2013-08-31 04:05:48 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2013-08-31 04:05:48 -0400 |
commit | ea79ca0de05198159bcb8a45479122a75e4a5861 (patch) | |
tree | 0288b2e20a404a96e4727b0c4590e97a58baac21 /tools | |
parent | 00e4cb1ced1b17c35465defafe86d156cbd7544e (diff) | |
parent | f2935f3e585226b8203ec3861907e1cb16ad3d6a (diff) |
Merge tag 'perf-core-for-mingo' 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:
* Tidy up sample parsing validation, from Adrian Hunter.
* Make events stream always parsable by adding a new sample_type bit:
PERF_SAMPLE_IDENTIFIER, that when requested will be always aat a fixed
position in all PERF_RECORD_ records, from Adrian Hunter.
* Add a sample parsing test, from Adrian Hunter.
* Add option to 'perf trace' to analyze events in a file versus live,
so that one can do:
[root@zoo ~]# perf record -a -e raw_syscalls:* sleep 1
[ perf record: Woken up 0 times to write data ]
[ perf record: Captured and wrote 25.150 MB perf.data (~1098836 samples) ]
[root@zoo ~]# perf trace -i perf.data -e futex --duration 1
17.799 ( 1.020 ms): 7127 futex(uaddr: 0x7fff3f6c6674, op: 393, val: 1, utime: 0x7fff3f6c6470, ua
113.344 (95.429 ms): 7127 futex(uaddr: 0x7fff3f6c6674, op: 393, val: 1, utime: 0x7fff3f6c6470, uaddr2: 0x7fff3f6c6648, val3: 4294967
133.778 ( 1.042 ms): 18004 futex(uaddr: 0x7fff3f6c6674, op: 393, val: 1, utime: 0x7fff3f6c6470, uaddr2: 0x7fff3f6c6648, val3: 429496
[root@zoo ~]#
From David Ahern.
* Honor target pid / tid options in 'perf trace' when analyzing a file,
from David Ahern.
* Handle missing HUGEPAGE defines in the mmap beautifier in 'perf trace',
from David Ahern.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
31 files changed, 1163 insertions, 187 deletions
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index fe19811faf90..daccd2c0a48f 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
@@ -74,6 +74,10 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. | |||
74 | --sched: | 74 | --sched: |
75 | Accrue thread runtime and provide a summary at the end of the session. | 75 | Accrue thread runtime and provide a summary at the end of the session. |
76 | 76 | ||
77 | -i | ||
78 | --input | ||
79 | Process events from a given perf data file. | ||
80 | |||
77 | SEE ALSO | 81 | SEE ALSO |
78 | -------- | 82 | -------- |
79 | linkperf:perf-record[1], linkperf:perf-script[1] | 83 | linkperf:perf-record[1], linkperf:perf-script[1] |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 7d30a7ddcc88..ecebfd00295e 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -360,6 +360,7 @@ LIB_OBJS += $(OUTPUT)util/rblist.o | |||
360 | LIB_OBJS += $(OUTPUT)util/intlist.o | 360 | LIB_OBJS += $(OUTPUT)util/intlist.o |
361 | LIB_OBJS += $(OUTPUT)util/vdso.o | 361 | LIB_OBJS += $(OUTPUT)util/vdso.o |
362 | LIB_OBJS += $(OUTPUT)util/stat.o | 362 | LIB_OBJS += $(OUTPUT)util/stat.o |
363 | LIB_OBJS += $(OUTPUT)util/record.o | ||
363 | 364 | ||
364 | LIB_OBJS += $(OUTPUT)ui/setup.o | 365 | LIB_OBJS += $(OUTPUT)ui/setup.o |
365 | LIB_OBJS += $(OUTPUT)ui/helpline.o | 366 | LIB_OBJS += $(OUTPUT)ui/helpline.o |
@@ -438,6 +439,7 @@ PERFLIBS = $(LIB_FILE) $(LIBLK) $(LIBTRACEEVENT) | |||
438 | ifneq ($(OUTPUT),) | 439 | ifneq ($(OUTPUT),) |
439 | CFLAGS += -I$(OUTPUT) | 440 | CFLAGS += -I$(OUTPUT) |
440 | endif | 441 | endif |
442 | LIB_OBJS += $(OUTPUT)tests/sample-parsing.o | ||
441 | 443 | ||
442 | ifdef NO_LIBELF | 444 | ifdef NO_LIBELF |
443 | EXTLIBS := $(filter-out -lelf,$(EXTLIBS)) | 445 | EXTLIBS := $(filter-out -lelf,$(EXTLIBS)) |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 1d8de2e4a407..9b336fdb6f71 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -198,7 +198,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool, | |||
198 | 198 | ||
199 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 199 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
200 | 200 | ||
201 | thread = machine__findnew_thread(machine, event->ip.pid); | 201 | thread = machine__findnew_thread(machine, sample->pid, sample->pid); |
202 | if (thread == NULL) { | 202 | if (thread == NULL) { |
203 | pr_err("problem processing %d event, skipping it.\n", | 203 | pr_err("problem processing %d event, skipping it.\n", |
204 | event->header.type); | 204 | event->header.type); |
@@ -206,7 +206,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool, | |||
206 | } | 206 | } |
207 | 207 | ||
208 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 208 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, |
209 | event->ip.ip, &al); | 209 | sample->ip, &al); |
210 | 210 | ||
211 | if (al.map != NULL) { | 211 | if (al.map != NULL) { |
212 | if (!al.map->dso->hit) { | 212 | if (!al.map->dso->hit) { |
@@ -301,7 +301,9 @@ found: | |||
301 | sample_sw.period = sample->period; | 301 | sample_sw.period = sample->period; |
302 | sample_sw.time = sample->time; | 302 | sample_sw.time = sample->time; |
303 | perf_event__synthesize_sample(event_sw, evsel->attr.sample_type, | 303 | perf_event__synthesize_sample(event_sw, evsel->attr.sample_type, |
304 | &sample_sw, false); | 304 | evsel->attr.sample_regs_user, |
305 | evsel->attr.read_format, &sample_sw, | ||
306 | false); | ||
305 | build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); | 307 | build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); |
306 | return perf_event__repipe(tool, event_sw, &sample_sw, machine); | 308 | return perf_event__repipe(tool, event_sw, &sample_sw, machine); |
307 | } | 309 | } |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index b49f5c58e152..c2dff9cb1f2c 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -305,7 +305,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
305 | struct perf_evsel *evsel, | 305 | struct perf_evsel *evsel, |
306 | struct machine *machine) | 306 | struct machine *machine) |
307 | { | 307 | { |
308 | struct thread *thread = machine__findnew_thread(machine, event->ip.pid); | 308 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
309 | sample->pid); | ||
309 | 310 | ||
310 | if (thread == NULL) { | 311 | if (thread == NULL) { |
311 | pr_debug("problem processing %d event, skipping it.\n", | 312 | pr_debug("problem processing %d event, skipping it.\n", |
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 6cd4de59be21..47b35407c2f2 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
@@ -815,7 +815,7 @@ static int process_sample_event(struct perf_tool *tool, | |||
815 | if (skip_sample(kvm, sample)) | 815 | if (skip_sample(kvm, sample)) |
816 | return 0; | 816 | return 0; |
817 | 817 | ||
818 | thread = machine__findnew_thread(machine, sample->tid); | 818 | thread = machine__findnew_thread(machine, sample->pid, sample->tid); |
819 | if (thread == NULL) { | 819 | if (thread == NULL) { |
820 | pr_debug("problem processing %d event, skipping it.\n", | 820 | pr_debug("problem processing %d event, skipping it.\n", |
821 | event->header.type); | 821 | event->header.type); |
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 76543a4a7a30..ee33ba2f05dd 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
@@ -805,7 +805,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
805 | struct perf_evsel *evsel, | 805 | struct perf_evsel *evsel, |
806 | struct machine *machine) | 806 | struct machine *machine) |
807 | { | 807 | { |
808 | struct thread *thread = machine__findnew_thread(machine, sample->tid); | 808 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
809 | sample->tid); | ||
809 | 810 | ||
810 | if (thread == NULL) { | 811 | if (thread == NULL) { |
811 | pr_debug("problem processing %d event, skipping it.\n", | 812 | pr_debug("problem processing %d event, skipping it.\n", |
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 706a1faa9559..791b432df847 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c | |||
@@ -94,7 +94,7 @@ dump_raw_samples(struct perf_tool *tool, | |||
94 | symbol_conf.field_sep, | 94 | symbol_conf.field_sep, |
95 | sample->tid, | 95 | sample->tid, |
96 | symbol_conf.field_sep, | 96 | symbol_conf.field_sep, |
97 | event->ip.ip, | 97 | sample->ip, |
98 | symbol_conf.field_sep, | 98 | symbol_conf.field_sep, |
99 | sample->addr, | 99 | sample->addr, |
100 | symbol_conf.field_sep, | 100 | symbol_conf.field_sep, |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 958a56a0e39e..9725aa375414 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -365,7 +365,7 @@ static int process_read_event(struct perf_tool *tool, | |||
365 | static int perf_report__setup_sample_type(struct perf_report *rep) | 365 | static int perf_report__setup_sample_type(struct perf_report *rep) |
366 | { | 366 | { |
367 | struct perf_session *self = rep->session; | 367 | struct perf_session *self = rep->session; |
368 | u64 sample_type = perf_evlist__sample_type(self->evlist); | 368 | u64 sample_type = perf_evlist__combined_sample_type(self->evlist); |
369 | 369 | ||
370 | if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { | 370 | if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { |
371 | if (sort__has_parent) { | 371 | if (sort__has_parent) { |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index f809cc7fb7d9..d8c51b2f263f 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -724,8 +724,10 @@ static int replay_fork_event(struct perf_sched *sched, | |||
724 | { | 724 | { |
725 | struct thread *child, *parent; | 725 | struct thread *child, *parent; |
726 | 726 | ||
727 | child = machine__findnew_thread(machine, event->fork.tid); | 727 | child = machine__findnew_thread(machine, event->fork.pid, |
728 | parent = machine__findnew_thread(machine, event->fork.ptid); | 728 | event->fork.tid); |
729 | parent = machine__findnew_thread(machine, event->fork.ppid, | ||
730 | event->fork.ptid); | ||
729 | 731 | ||
730 | if (child == NULL || parent == NULL) { | 732 | if (child == NULL || parent == NULL) { |
731 | pr_debug("thread does not exist on fork event: child %p, parent %p\n", | 733 | pr_debug("thread does not exist on fork event: child %p, parent %p\n", |
@@ -934,8 +936,8 @@ static int latency_switch_event(struct perf_sched *sched, | |||
934 | return -1; | 936 | return -1; |
935 | } | 937 | } |
936 | 938 | ||
937 | sched_out = machine__findnew_thread(machine, prev_pid); | 939 | sched_out = machine__findnew_thread(machine, 0, prev_pid); |
938 | sched_in = machine__findnew_thread(machine, next_pid); | 940 | sched_in = machine__findnew_thread(machine, 0, next_pid); |
939 | 941 | ||
940 | out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid); | 942 | out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid); |
941 | if (!out_events) { | 943 | if (!out_events) { |
@@ -978,7 +980,7 @@ static int latency_runtime_event(struct perf_sched *sched, | |||
978 | { | 980 | { |
979 | const u32 pid = perf_evsel__intval(evsel, sample, "pid"); | 981 | const u32 pid = perf_evsel__intval(evsel, sample, "pid"); |
980 | const u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); | 982 | const u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); |
981 | struct thread *thread = machine__findnew_thread(machine, pid); | 983 | struct thread *thread = machine__findnew_thread(machine, 0, pid); |
982 | struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); | 984 | struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); |
983 | u64 timestamp = sample->time; | 985 | u64 timestamp = sample->time; |
984 | int cpu = sample->cpu; | 986 | int cpu = sample->cpu; |
@@ -1016,7 +1018,7 @@ static int latency_wakeup_event(struct perf_sched *sched, | |||
1016 | if (!success) | 1018 | if (!success) |
1017 | return 0; | 1019 | return 0; |
1018 | 1020 | ||
1019 | wakee = machine__findnew_thread(machine, pid); | 1021 | wakee = machine__findnew_thread(machine, 0, pid); |
1020 | atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid); | 1022 | atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid); |
1021 | if (!atoms) { | 1023 | if (!atoms) { |
1022 | if (thread_atoms_insert(sched, wakee)) | 1024 | if (thread_atoms_insert(sched, wakee)) |
@@ -1070,7 +1072,7 @@ static int latency_migrate_task_event(struct perf_sched *sched, | |||
1070 | if (sched->profile_cpu == -1) | 1072 | if (sched->profile_cpu == -1) |
1071 | return 0; | 1073 | return 0; |
1072 | 1074 | ||
1073 | migrant = machine__findnew_thread(machine, pid); | 1075 | migrant = machine__findnew_thread(machine, 0, pid); |
1074 | atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid); | 1076 | atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid); |
1075 | if (!atoms) { | 1077 | if (!atoms) { |
1076 | if (thread_atoms_insert(sched, migrant)) | 1078 | if (thread_atoms_insert(sched, migrant)) |
@@ -1289,8 +1291,8 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, | |||
1289 | return -1; | 1291 | return -1; |
1290 | } | 1292 | } |
1291 | 1293 | ||
1292 | sched_out = machine__findnew_thread(machine, prev_pid); | 1294 | sched_out = machine__findnew_thread(machine, 0, prev_pid); |
1293 | sched_in = machine__findnew_thread(machine, next_pid); | 1295 | sched_in = machine__findnew_thread(machine, 0, next_pid); |
1294 | 1296 | ||
1295 | sched->curr_thread[this_cpu] = sched_in; | 1297 | sched->curr_thread[this_cpu] = sched_in; |
1296 | 1298 | ||
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 2ad9d5b6fb3c..93a34cef9676 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -501,7 +501,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
501 | struct machine *machine) | 501 | struct machine *machine) |
502 | { | 502 | { |
503 | struct addr_location al; | 503 | struct addr_location al; |
504 | struct thread *thread = machine__findnew_thread(machine, event->ip.tid); | 504 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
505 | sample->tid); | ||
505 | 506 | ||
506 | if (thread == NULL) { | 507 | if (thread == NULL) { |
507 | pr_debug("problem processing %d event, skipping it.\n", | 508 | pr_debug("problem processing %d event, skipping it.\n", |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index e37521fc715a..212214162bb2 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -689,7 +689,7 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
689 | { | 689 | { |
690 | struct perf_top *top = container_of(tool, struct perf_top, tool); | 690 | struct perf_top *top = container_of(tool, struct perf_top, tool); |
691 | struct symbol *parent = NULL; | 691 | struct symbol *parent = NULL; |
692 | u64 ip = event->ip.ip; | 692 | u64 ip = sample->ip; |
693 | struct addr_location al; | 693 | struct addr_location al; |
694 | int err; | 694 | int err; |
695 | 695 | ||
@@ -699,10 +699,10 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
699 | if (!seen) | 699 | if (!seen) |
700 | seen = intlist__new(NULL); | 700 | seen = intlist__new(NULL); |
701 | 701 | ||
702 | if (!intlist__has_entry(seen, event->ip.pid)) { | 702 | if (!intlist__has_entry(seen, sample->pid)) { |
703 | pr_err("Can't find guest [%d]'s kernel information\n", | 703 | pr_err("Can't find guest [%d]'s kernel information\n", |
704 | event->ip.pid); | 704 | sample->pid); |
705 | intlist__add(seen, event->ip.pid); | 705 | intlist__add(seen, sample->pid); |
706 | } | 706 | } |
707 | return; | 707 | return; |
708 | } | 708 | } |
@@ -836,7 +836,8 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
836 | break; | 836 | break; |
837 | case PERF_RECORD_MISC_GUEST_KERNEL: | 837 | case PERF_RECORD_MISC_GUEST_KERNEL: |
838 | ++top->guest_kernel_samples; | 838 | ++top->guest_kernel_samples; |
839 | machine = perf_session__find_machine(session, event->ip.pid); | 839 | machine = perf_session__find_machine(session, |
840 | sample.pid); | ||
840 | break; | 841 | break; |
841 | case PERF_RECORD_MISC_GUEST_USER: | 842 | case PERF_RECORD_MISC_GUEST_USER: |
842 | ++top->guest_us_samples; | 843 | ++top->guest_us_samples; |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index b72afc73f9a7..69a065e51135 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -4,9 +4,11 @@ | |||
4 | #include "util/debug.h" | 4 | #include "util/debug.h" |
5 | #include "util/evlist.h" | 5 | #include "util/evlist.h" |
6 | #include "util/machine.h" | 6 | #include "util/machine.h" |
7 | #include "util/session.h" | ||
7 | #include "util/thread.h" | 8 | #include "util/thread.h" |
8 | #include "util/parse-options.h" | 9 | #include "util/parse-options.h" |
9 | #include "util/strlist.h" | 10 | #include "util/strlist.h" |
11 | #include "util/intlist.h" | ||
10 | #include "util/thread_map.h" | 12 | #include "util/thread_map.h" |
11 | 13 | ||
12 | #include <libaudit.h> | 14 | #include <libaudit.h> |
@@ -69,7 +71,9 @@ static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, unsigned | |||
69 | P_MMAP_FLAG(FILE); | 71 | P_MMAP_FLAG(FILE); |
70 | P_MMAP_FLAG(FIXED); | 72 | P_MMAP_FLAG(FIXED); |
71 | P_MMAP_FLAG(GROWSDOWN); | 73 | P_MMAP_FLAG(GROWSDOWN); |
74 | #ifdef MAP_HUGETLB | ||
72 | P_MMAP_FLAG(HUGETLB); | 75 | P_MMAP_FLAG(HUGETLB); |
76 | #endif | ||
73 | P_MMAP_FLAG(LOCKED); | 77 | P_MMAP_FLAG(LOCKED); |
74 | P_MMAP_FLAG(NONBLOCK); | 78 | P_MMAP_FLAG(NONBLOCK); |
75 | P_MMAP_FLAG(NORESERVE); | 79 | P_MMAP_FLAG(NORESERVE); |
@@ -108,8 +112,12 @@ static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, uns | |||
108 | #endif | 112 | #endif |
109 | P_MADV_BHV(MERGEABLE); | 113 | P_MADV_BHV(MERGEABLE); |
110 | P_MADV_BHV(UNMERGEABLE); | 114 | P_MADV_BHV(UNMERGEABLE); |
115 | #ifdef MADV_HUGEPAGE | ||
111 | P_MADV_BHV(HUGEPAGE); | 116 | P_MADV_BHV(HUGEPAGE); |
117 | #endif | ||
118 | #ifdef MADV_NOHUGEPAGE | ||
112 | P_MADV_BHV(NOHUGEPAGE); | 119 | P_MADV_BHV(NOHUGEPAGE); |
120 | #endif | ||
113 | #ifdef MADV_DONTDUMP | 121 | #ifdef MADV_DONTDUMP |
114 | P_MADV_BHV(DONTDUMP); | 122 | P_MADV_BHV(DONTDUMP); |
115 | #endif | 123 | #endif |
@@ -258,6 +266,8 @@ struct trace { | |||
258 | unsigned long nr_events; | 266 | unsigned long nr_events; |
259 | struct strlist *ev_qualifier; | 267 | struct strlist *ev_qualifier; |
260 | bool not_ev_qualifier; | 268 | bool not_ev_qualifier; |
269 | struct intlist *tid_list; | ||
270 | struct intlist *pid_list; | ||
261 | bool sched; | 271 | bool sched; |
262 | bool multiple_threads; | 272 | bool multiple_threads; |
263 | double duration_filter; | 273 | double duration_filter; |
@@ -521,7 +531,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
521 | if (sc->filtered) | 531 | if (sc->filtered) |
522 | return 0; | 532 | return 0; |
523 | 533 | ||
524 | thread = machine__findnew_thread(&trace->host, sample->tid); | 534 | thread = machine__findnew_thread(&trace->host, sample->pid, |
535 | sample->tid); | ||
525 | ttrace = thread__trace(thread, trace->output); | 536 | ttrace = thread__trace(thread, trace->output); |
526 | if (ttrace == NULL) | 537 | if (ttrace == NULL) |
527 | return -1; | 538 | return -1; |
@@ -572,7 +583,8 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
572 | if (sc->filtered) | 583 | if (sc->filtered) |
573 | return 0; | 584 | return 0; |
574 | 585 | ||
575 | thread = machine__findnew_thread(&trace->host, sample->tid); | 586 | thread = machine__findnew_thread(&trace->host, sample->pid, |
587 | sample->tid); | ||
576 | ttrace = thread__trace(thread, trace->output); | 588 | ttrace = thread__trace(thread, trace->output); |
577 | if (ttrace == NULL) | 589 | if (ttrace == NULL) |
578 | return -1; | 590 | return -1; |
@@ -628,7 +640,9 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs | |||
628 | { | 640 | { |
629 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); | 641 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); |
630 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; | 642 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; |
631 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | 643 | struct thread *thread = machine__findnew_thread(&trace->host, |
644 | sample->pid, | ||
645 | sample->tid); | ||
632 | struct thread_trace *ttrace = thread__trace(thread, trace->output); | 646 | struct thread_trace *ttrace = thread__trace(thread, trace->output); |
633 | 647 | ||
634 | if (ttrace == NULL) | 648 | if (ttrace == NULL) |
@@ -648,6 +662,72 @@ out_dump: | |||
648 | return 0; | 662 | return 0; |
649 | } | 663 | } |
650 | 664 | ||
665 | static bool skip_sample(struct trace *trace, struct perf_sample *sample) | ||
666 | { | ||
667 | if ((trace->pid_list && intlist__find(trace->pid_list, sample->pid)) || | ||
668 | (trace->tid_list && intlist__find(trace->tid_list, sample->tid))) | ||
669 | return false; | ||
670 | |||
671 | if (trace->pid_list || trace->tid_list) | ||
672 | return true; | ||
673 | |||
674 | return false; | ||
675 | } | ||
676 | |||
677 | static int trace__process_sample(struct perf_tool *tool, | ||
678 | union perf_event *event __maybe_unused, | ||
679 | struct perf_sample *sample, | ||
680 | struct perf_evsel *evsel, | ||
681 | struct machine *machine __maybe_unused) | ||
682 | { | ||
683 | struct trace *trace = container_of(tool, struct trace, tool); | ||
684 | int err = 0; | ||
685 | |||
686 | tracepoint_handler handler = evsel->handler.func; | ||
687 | |||
688 | if (skip_sample(trace, sample)) | ||
689 | return 0; | ||
690 | |||
691 | if (trace->base_time == 0) | ||
692 | trace->base_time = sample->time; | ||
693 | |||
694 | if (handler) | ||
695 | handler(trace, evsel, sample); | ||
696 | |||
697 | return err; | ||
698 | } | ||
699 | |||
700 | static bool | ||
701 | perf_session__has_tp(struct perf_session *session, const char *name) | ||
702 | { | ||
703 | struct perf_evsel *evsel; | ||
704 | |||
705 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, name); | ||
706 | |||
707 | return evsel != NULL; | ||
708 | } | ||
709 | |||
710 | static int parse_target_str(struct trace *trace) | ||
711 | { | ||
712 | if (trace->opts.target.pid) { | ||
713 | trace->pid_list = intlist__new(trace->opts.target.pid); | ||
714 | if (trace->pid_list == NULL) { | ||
715 | pr_err("Error parsing process id string\n"); | ||
716 | return -EINVAL; | ||
717 | } | ||
718 | } | ||
719 | |||
720 | if (trace->opts.target.tid) { | ||
721 | trace->tid_list = intlist__new(trace->opts.target.tid); | ||
722 | if (trace->tid_list == NULL) { | ||
723 | pr_err("Error parsing thread id string\n"); | ||
724 | return -EINVAL; | ||
725 | } | ||
726 | } | ||
727 | |||
728 | return 0; | ||
729 | } | ||
730 | |||
651 | static int trace__run(struct trace *trace, int argc, const char **argv) | 731 | static int trace__run(struct trace *trace, int argc, const char **argv) |
652 | { | 732 | { |
653 | struct perf_evlist *evlist = perf_evlist__new(); | 733 | struct perf_evlist *evlist = perf_evlist__new(); |
@@ -787,6 +867,69 @@ out: | |||
787 | return err; | 867 | return err; |
788 | } | 868 | } |
789 | 869 | ||
870 | static int trace__replay(struct trace *trace) | ||
871 | { | ||
872 | const struct perf_evsel_str_handler handlers[] = { | ||
873 | { "raw_syscalls:sys_enter", trace__sys_enter, }, | ||
874 | { "raw_syscalls:sys_exit", trace__sys_exit, }, | ||
875 | }; | ||
876 | |||
877 | struct perf_session *session; | ||
878 | int err = -1; | ||
879 | |||
880 | trace->tool.sample = trace__process_sample; | ||
881 | trace->tool.mmap = perf_event__process_mmap; | ||
882 | trace->tool.comm = perf_event__process_comm; | ||
883 | trace->tool.exit = perf_event__process_exit; | ||
884 | trace->tool.fork = perf_event__process_fork; | ||
885 | trace->tool.attr = perf_event__process_attr; | ||
886 | trace->tool.tracing_data = perf_event__process_tracing_data; | ||
887 | trace->tool.build_id = perf_event__process_build_id; | ||
888 | |||
889 | trace->tool.ordered_samples = true; | ||
890 | trace->tool.ordering_requires_timestamps = true; | ||
891 | |||
892 | /* add tid to output */ | ||
893 | trace->multiple_threads = true; | ||
894 | |||
895 | if (symbol__init() < 0) | ||
896 | return -1; | ||
897 | |||
898 | session = perf_session__new(input_name, O_RDONLY, 0, false, | ||
899 | &trace->tool); | ||
900 | if (session == NULL) | ||
901 | return -ENOMEM; | ||
902 | |||
903 | err = perf_session__set_tracepoints_handlers(session, handlers); | ||
904 | if (err) | ||
905 | goto out; | ||
906 | |||
907 | if (!perf_session__has_tp(session, "raw_syscalls:sys_enter")) { | ||
908 | pr_err("Data file does not have raw_syscalls:sys_enter events\n"); | ||
909 | goto out; | ||
910 | } | ||
911 | |||
912 | if (!perf_session__has_tp(session, "raw_syscalls:sys_exit")) { | ||
913 | pr_err("Data file does not have raw_syscalls:sys_exit events\n"); | ||
914 | goto out; | ||
915 | } | ||
916 | |||
917 | err = parse_target_str(trace); | ||
918 | if (err != 0) | ||
919 | goto out; | ||
920 | |||
921 | setup_pager(); | ||
922 | |||
923 | err = perf_session__process_events(session, &trace->tool); | ||
924 | if (err) | ||
925 | pr_err("Failed to process events, error %d", err); | ||
926 | |||
927 | out: | ||
928 | perf_session__delete(session); | ||
929 | |||
930 | return err; | ||
931 | } | ||
932 | |||
790 | static size_t trace__fprintf_threads_header(FILE *fp) | 933 | static size_t trace__fprintf_threads_header(FILE *fp) |
791 | { | 934 | { |
792 | size_t printed; | 935 | size_t printed; |
@@ -888,6 +1031,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
888 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", | 1031 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", |
889 | "list of events to trace"), | 1032 | "list of events to trace"), |
890 | OPT_STRING('o', "output", &output_name, "file", "output file name"), | 1033 | OPT_STRING('o', "output", &output_name, "file", "output file name"), |
1034 | OPT_STRING('i', "input", &input_name, "file", "Analyze events in file"), | ||
891 | OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", | 1035 | OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", |
892 | "trace events on existing process id"), | 1036 | "trace events on existing process id"), |
893 | OPT_STRING('t', "tid", &trace.opts.target.tid, "tid", | 1037 | OPT_STRING('t', "tid", &trace.opts.target.tid, "tid", |
@@ -896,7 +1040,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
896 | "system-wide collection from all CPUs"), | 1040 | "system-wide collection from all CPUs"), |
897 | OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu", | 1041 | OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu", |
898 | "list of cpus to monitor"), | 1042 | "list of cpus to monitor"), |
899 | OPT_BOOLEAN('i', "no-inherit", &trace.opts.no_inherit, | 1043 | OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit, |
900 | "child tasks do not inherit counters"), | 1044 | "child tasks do not inherit counters"), |
901 | OPT_UINTEGER('m', "mmap-pages", &trace.opts.mmap_pages, | 1045 | OPT_UINTEGER('m', "mmap-pages", &trace.opts.mmap_pages, |
902 | "number of mmap data pages"), | 1046 | "number of mmap data pages"), |
@@ -954,7 +1098,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
954 | if (!argc && perf_target__none(&trace.opts.target)) | 1098 | if (!argc && perf_target__none(&trace.opts.target)) |
955 | trace.opts.target.system_wide = true; | 1099 | trace.opts.target.system_wide = true; |
956 | 1100 | ||
957 | err = trace__run(&trace, argc, argv); | 1101 | if (input_name) |
1102 | err = trace__replay(&trace); | ||
1103 | else | ||
1104 | err = trace__run(&trace, argc, argv); | ||
958 | 1105 | ||
959 | if (trace.sched && !err) | 1106 | if (trace.sched && !err) |
960 | trace__fprintf_thread_summary(&trace, trace.output); | 1107 | trace__fprintf_thread_summary(&trace, trace.output); |
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index f5af19244a05..8ad9415dd847 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -104,6 +104,10 @@ static struct test { | |||
104 | .func = test__code_reading, | 104 | .func = test__code_reading, |
105 | }, | 105 | }, |
106 | { | 106 | { |
107 | .desc = "Test sample parsing", | ||
108 | .func = test__sample_parsing, | ||
109 | }, | ||
110 | { | ||
107 | .func = NULL, | 111 | .func = NULL, |
108 | }, | 112 | }, |
109 | }; | 113 | }; |
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index df9afd9cab4c..6fb781d5586c 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c | |||
@@ -257,7 +257,7 @@ static int process_sample_event(struct machine *machine, | |||
257 | return -1; | 257 | return -1; |
258 | } | 258 | } |
259 | 259 | ||
260 | thread = machine__findnew_thread(machine, sample.pid); | 260 | thread = machine__findnew_thread(machine, sample.pid, sample.pid); |
261 | if (!thread) { | 261 | if (!thread) { |
262 | pr_debug("machine__findnew_thread failed\n"); | 262 | pr_debug("machine__findnew_thread failed\n"); |
263 | return -1; | 263 | return -1; |
@@ -446,7 +446,7 @@ static int do_test_code_reading(bool try_kcore) | |||
446 | goto out_err; | 446 | goto out_err; |
447 | } | 447 | } |
448 | 448 | ||
449 | thread = machine__findnew_thread(machine, pid); | 449 | thread = machine__findnew_thread(machine, pid, pid); |
450 | if (!thread) { | 450 | if (!thread) { |
451 | pr_debug("machine__findnew_thread failed\n"); | 451 | pr_debug("machine__findnew_thread failed\n"); |
452 | goto out_err; | 452 | goto out_err; |
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 50bfb01183ea..4228ffc0d968 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c | |||
@@ -88,7 +88,8 @@ static struct machine *setup_fake_machine(struct machines *machines) | |||
88 | for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { | 88 | for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { |
89 | struct thread *thread; | 89 | struct thread *thread; |
90 | 90 | ||
91 | thread = machine__findnew_thread(machine, fake_threads[i].pid); | 91 | thread = machine__findnew_thread(machine, fake_threads[i].pid, |
92 | fake_threads[i].pid); | ||
92 | if (thread == NULL) | 93 | if (thread == NULL) |
93 | goto out; | 94 | goto out; |
94 | 95 | ||
@@ -210,15 +211,13 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
210 | list_for_each_entry(evsel, &evlist->entries, node) { | 211 | list_for_each_entry(evsel, &evlist->entries, node) { |
211 | for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { | 212 | for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { |
212 | const union perf_event event = { | 213 | const union perf_event event = { |
213 | .ip = { | 214 | .header = { |
214 | .header = { | 215 | .misc = PERF_RECORD_MISC_USER, |
215 | .misc = PERF_RECORD_MISC_USER, | ||
216 | }, | ||
217 | .pid = fake_common_samples[k].pid, | ||
218 | .ip = fake_common_samples[k].ip, | ||
219 | }, | 216 | }, |
220 | }; | 217 | }; |
221 | 218 | ||
219 | sample.pid = fake_common_samples[k].pid; | ||
220 | sample.ip = fake_common_samples[k].ip; | ||
222 | if (perf_event__preprocess_sample(&event, machine, &al, | 221 | if (perf_event__preprocess_sample(&event, machine, &al, |
223 | &sample) < 0) | 222 | &sample) < 0) |
224 | goto out; | 223 | goto out; |
@@ -234,15 +233,13 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
234 | 233 | ||
235 | for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) { | 234 | for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) { |
236 | const union perf_event event = { | 235 | const union perf_event event = { |
237 | .ip = { | 236 | .header = { |
238 | .header = { | 237 | .misc = PERF_RECORD_MISC_USER, |
239 | .misc = PERF_RECORD_MISC_USER, | ||
240 | }, | ||
241 | .pid = fake_samples[i][k].pid, | ||
242 | .ip = fake_samples[i][k].ip, | ||
243 | }, | 238 | }, |
244 | }; | 239 | }; |
245 | 240 | ||
241 | sample.pid = fake_samples[i][k].pid; | ||
242 | sample.ip = fake_samples[i][k].ip; | ||
246 | if (perf_event__preprocess_sample(&event, machine, &al, | 243 | if (perf_event__preprocess_sample(&event, machine, &al, |
247 | &sample) < 0) | 244 | &sample) < 0) |
248 | goto out; | 245 | goto out; |
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index 5b1b5aba722b..c4185b9aeb80 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c | |||
@@ -72,7 +72,7 @@ int test__basic_mmap(void) | |||
72 | } | 72 | } |
73 | 73 | ||
74 | evsels[i]->attr.wakeup_events = 1; | 74 | evsels[i]->attr.wakeup_events = 1; |
75 | perf_evsel__set_sample_id(evsels[i]); | 75 | perf_evsel__set_sample_id(evsels[i], false); |
76 | 76 | ||
77 | perf_evlist__add(evlist, evsels[i]); | 77 | perf_evlist__add(evlist, evsels[i]); |
78 | 78 | ||
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c new file mode 100644 index 000000000000..77f598dbd97a --- /dev/null +++ b/tools/perf/tests/sample-parsing.c | |||
@@ -0,0 +1,316 @@ | |||
1 | #include <stdbool.h> | ||
2 | #include <inttypes.h> | ||
3 | |||
4 | #include "util.h" | ||
5 | #include "event.h" | ||
6 | #include "evsel.h" | ||
7 | |||
8 | #include "tests.h" | ||
9 | |||
10 | #define COMP(m) do { \ | ||
11 | if (s1->m != s2->m) { \ | ||
12 | pr_debug("Samples differ at '"#m"'\n"); \ | ||
13 | return false; \ | ||
14 | } \ | ||
15 | } while (0) | ||
16 | |||
17 | #define MCOMP(m) do { \ | ||
18 | if (memcmp(&s1->m, &s2->m, sizeof(s1->m))) { \ | ||
19 | pr_debug("Samples differ at '"#m"'\n"); \ | ||
20 | return false; \ | ||
21 | } \ | ||
22 | } while (0) | ||
23 | |||
24 | static bool samples_same(const struct perf_sample *s1, | ||
25 | const struct perf_sample *s2, u64 type, u64 regs_user, | ||
26 | u64 read_format) | ||
27 | { | ||
28 | size_t i; | ||
29 | |||
30 | if (type & PERF_SAMPLE_IDENTIFIER) | ||
31 | COMP(id); | ||
32 | |||
33 | if (type & PERF_SAMPLE_IP) | ||
34 | COMP(ip); | ||
35 | |||
36 | if (type & PERF_SAMPLE_TID) { | ||
37 | COMP(pid); | ||
38 | COMP(tid); | ||
39 | } | ||
40 | |||
41 | if (type & PERF_SAMPLE_TIME) | ||
42 | COMP(time); | ||
43 | |||
44 | if (type & PERF_SAMPLE_ADDR) | ||
45 | COMP(addr); | ||
46 | |||
47 | if (type & PERF_SAMPLE_ID) | ||
48 | COMP(id); | ||
49 | |||
50 | if (type & PERF_SAMPLE_STREAM_ID) | ||
51 | COMP(stream_id); | ||
52 | |||
53 | if (type & PERF_SAMPLE_CPU) | ||
54 | COMP(cpu); | ||
55 | |||
56 | if (type & PERF_SAMPLE_PERIOD) | ||
57 | COMP(period); | ||
58 | |||
59 | if (type & PERF_SAMPLE_READ) { | ||
60 | if (read_format & PERF_FORMAT_GROUP) | ||
61 | COMP(read.group.nr); | ||
62 | else | ||
63 | COMP(read.one.value); | ||
64 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
65 | COMP(read.time_enabled); | ||
66 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
67 | COMP(read.time_running); | ||
68 | /* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */ | ||
69 | if (read_format & PERF_FORMAT_GROUP) { | ||
70 | for (i = 0; i < s1->read.group.nr; i++) | ||
71 | MCOMP(read.group.values[i]); | ||
72 | } else { | ||
73 | COMP(read.one.id); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
78 | COMP(callchain->nr); | ||
79 | for (i = 0; i < s1->callchain->nr; i++) | ||
80 | COMP(callchain->ips[i]); | ||
81 | } | ||
82 | |||
83 | if (type & PERF_SAMPLE_RAW) { | ||
84 | COMP(raw_size); | ||
85 | if (memcmp(s1->raw_data, s2->raw_data, s1->raw_size)) { | ||
86 | pr_debug("Samples differ at 'raw_data'\n"); | ||
87 | return false; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | if (type & PERF_SAMPLE_BRANCH_STACK) { | ||
92 | COMP(branch_stack->nr); | ||
93 | for (i = 0; i < s1->branch_stack->nr; i++) | ||
94 | MCOMP(branch_stack->entries[i]); | ||
95 | } | ||
96 | |||
97 | if (type & PERF_SAMPLE_REGS_USER) { | ||
98 | size_t sz = hweight_long(regs_user) * sizeof(u64); | ||
99 | |||
100 | COMP(user_regs.abi); | ||
101 | if (s1->user_regs.abi && | ||
102 | (!s1->user_regs.regs || !s2->user_regs.regs || | ||
103 | memcmp(s1->user_regs.regs, s2->user_regs.regs, sz))) { | ||
104 | pr_debug("Samples differ at 'user_regs'\n"); | ||
105 | return false; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | if (type & PERF_SAMPLE_STACK_USER) { | ||
110 | COMP(user_stack.size); | ||
111 | if (memcmp(s1->user_stack.data, s1->user_stack.data, | ||
112 | s1->user_stack.size)) { | ||
113 | pr_debug("Samples differ at 'user_stack'\n"); | ||
114 | return false; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | if (type & PERF_SAMPLE_WEIGHT) | ||
119 | COMP(weight); | ||
120 | |||
121 | if (type & PERF_SAMPLE_DATA_SRC) | ||
122 | COMP(data_src); | ||
123 | |||
124 | return true; | ||
125 | } | ||
126 | |||
127 | static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | ||
128 | { | ||
129 | struct perf_evsel evsel = { | ||
130 | .needs_swap = false, | ||
131 | .attr = { | ||
132 | .sample_type = sample_type, | ||
133 | .sample_regs_user = sample_regs_user, | ||
134 | .read_format = read_format, | ||
135 | }, | ||
136 | }; | ||
137 | union perf_event *event; | ||
138 | union { | ||
139 | struct ip_callchain callchain; | ||
140 | u64 data[64]; | ||
141 | } callchain = { | ||
142 | /* 3 ips */ | ||
143 | .data = {3, 201, 202, 203}, | ||
144 | }; | ||
145 | union { | ||
146 | struct branch_stack branch_stack; | ||
147 | u64 data[64]; | ||
148 | } branch_stack = { | ||
149 | /* 1 branch_entry */ | ||
150 | .data = {1, 211, 212, 213}, | ||
151 | }; | ||
152 | u64 user_regs[64]; | ||
153 | const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL}; | ||
154 | const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL}; | ||
155 | struct perf_sample sample = { | ||
156 | .ip = 101, | ||
157 | .pid = 102, | ||
158 | .tid = 103, | ||
159 | .time = 104, | ||
160 | .addr = 105, | ||
161 | .id = 106, | ||
162 | .stream_id = 107, | ||
163 | .period = 108, | ||
164 | .weight = 109, | ||
165 | .cpu = 110, | ||
166 | .raw_size = sizeof(raw_data), | ||
167 | .data_src = 111, | ||
168 | .raw_data = (void *)raw_data, | ||
169 | .callchain = &callchain.callchain, | ||
170 | .branch_stack = &branch_stack.branch_stack, | ||
171 | .user_regs = { | ||
172 | .abi = PERF_SAMPLE_REGS_ABI_64, | ||
173 | .regs = user_regs, | ||
174 | }, | ||
175 | .user_stack = { | ||
176 | .size = sizeof(data), | ||
177 | .data = (void *)data, | ||
178 | }, | ||
179 | .read = { | ||
180 | .time_enabled = 0x030a59d664fca7deULL, | ||
181 | .time_running = 0x011b6ae553eb98edULL, | ||
182 | }, | ||
183 | }; | ||
184 | struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},}; | ||
185 | struct perf_sample sample_out; | ||
186 | size_t i, sz, bufsz; | ||
187 | int err, ret = -1; | ||
188 | |||
189 | for (i = 0; i < sizeof(user_regs); i++) | ||
190 | *(i + (u8 *)user_regs) = i & 0xfe; | ||
191 | |||
192 | if (read_format & PERF_FORMAT_GROUP) { | ||
193 | sample.read.group.nr = 4; | ||
194 | sample.read.group.values = values; | ||
195 | } else { | ||
196 | sample.read.one.value = 0x08789faeb786aa87ULL; | ||
197 | sample.read.one.id = 99; | ||
198 | } | ||
199 | |||
200 | sz = perf_event__sample_event_size(&sample, sample_type, | ||
201 | sample_regs_user, read_format); | ||
202 | bufsz = sz + 4096; /* Add a bit for overrun checking */ | ||
203 | event = malloc(bufsz); | ||
204 | if (!event) { | ||
205 | pr_debug("malloc failed\n"); | ||
206 | return -1; | ||
207 | } | ||
208 | |||
209 | memset(event, 0xff, bufsz); | ||
210 | event->header.type = PERF_RECORD_SAMPLE; | ||
211 | event->header.misc = 0; | ||
212 | event->header.size = sz; | ||
213 | |||
214 | err = perf_event__synthesize_sample(event, sample_type, | ||
215 | sample_regs_user, read_format, | ||
216 | &sample, false); | ||
217 | if (err) { | ||
218 | pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", | ||
219 | "perf_event__synthesize_sample", sample_type, err); | ||
220 | goto out_free; | ||
221 | } | ||
222 | |||
223 | /* The data does not contain 0xff so we use that to check the size */ | ||
224 | for (i = bufsz; i > 0; i--) { | ||
225 | if (*(i - 1 + (u8 *)event) != 0xff) | ||
226 | break; | ||
227 | } | ||
228 | if (i != sz) { | ||
229 | pr_debug("Event size mismatch: actual %zu vs expected %zu\n", | ||
230 | i, sz); | ||
231 | goto out_free; | ||
232 | } | ||
233 | |||
234 | evsel.sample_size = __perf_evsel__sample_size(sample_type); | ||
235 | |||
236 | err = perf_evsel__parse_sample(&evsel, event, &sample_out); | ||
237 | if (err) { | ||
238 | pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", | ||
239 | "perf_evsel__parse_sample", sample_type, err); | ||
240 | goto out_free; | ||
241 | } | ||
242 | |||
243 | if (!samples_same(&sample, &sample_out, sample_type, | ||
244 | sample_regs_user, read_format)) { | ||
245 | pr_debug("parsing failed for sample_type %#"PRIx64"\n", | ||
246 | sample_type); | ||
247 | goto out_free; | ||
248 | } | ||
249 | |||
250 | ret = 0; | ||
251 | out_free: | ||
252 | free(event); | ||
253 | if (ret && read_format) | ||
254 | pr_debug("read_format %#"PRIx64"\n", read_format); | ||
255 | return ret; | ||
256 | } | ||
257 | |||
258 | /** | ||
259 | * test__sample_parsing - test sample parsing. | ||
260 | * | ||
261 | * This function implements a test that synthesizes a sample event, parses it | ||
262 | * and then checks that the parsed sample matches the original sample. The test | ||
263 | * checks sample format bits separately and together. If the test passes %0 is | ||
264 | * returned, otherwise %-1 is returned. | ||
265 | */ | ||
266 | int test__sample_parsing(void) | ||
267 | { | ||
268 | const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15}; | ||
269 | u64 sample_type; | ||
270 | u64 sample_regs_user; | ||
271 | size_t i; | ||
272 | int err; | ||
273 | |||
274 | /* | ||
275 | * Fail the test if it has not been updated when new sample format bits | ||
276 | * were added. | ||
277 | */ | ||
278 | if (PERF_SAMPLE_MAX > PERF_SAMPLE_IDENTIFIER << 1) { | ||
279 | pr_debug("sample format has changed - test needs updating\n"); | ||
280 | return -1; | ||
281 | } | ||
282 | |||
283 | /* Test each sample format bit separately */ | ||
284 | for (sample_type = 1; sample_type != PERF_SAMPLE_MAX; | ||
285 | sample_type <<= 1) { | ||
286 | /* Test read_format variations */ | ||
287 | if (sample_type == PERF_SAMPLE_READ) { | ||
288 | for (i = 0; i < ARRAY_SIZE(rf); i++) { | ||
289 | err = do_test(sample_type, 0, rf[i]); | ||
290 | if (err) | ||
291 | return err; | ||
292 | } | ||
293 | continue; | ||
294 | } | ||
295 | |||
296 | if (sample_type == PERF_SAMPLE_REGS_USER) | ||
297 | sample_regs_user = 0x3fff; | ||
298 | else | ||
299 | sample_regs_user = 0; | ||
300 | |||
301 | err = do_test(sample_type, sample_regs_user, 0); | ||
302 | if (err) | ||
303 | return err; | ||
304 | } | ||
305 | |||
306 | /* Test all sample format bits together */ | ||
307 | sample_type = PERF_SAMPLE_MAX - 1; | ||
308 | sample_regs_user = 0x3fff; | ||
309 | for (i = 0; i < ARRAY_SIZE(rf); i++) { | ||
310 | err = do_test(sample_type, sample_regs_user, rf[i]); | ||
311 | if (err) | ||
312 | return err; | ||
313 | } | ||
314 | |||
315 | return 0; | ||
316 | } | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index c748f532b20f..83d5b71a3ce4 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -37,5 +37,6 @@ int test__task_exit(void); | |||
37 | int test__sw_clock_freq(void); | 37 | int test__sw_clock_freq(void); |
38 | int test__perf_time_to_tsc(void); | 38 | int test__perf_time_to_tsc(void); |
39 | int test__code_reading(void); | 39 | int test__code_reading(void); |
40 | int test__sample_parsing(void); | ||
40 | 41 | ||
41 | #endif /* TESTS_H */ | 42 | #endif /* TESTS_H */ |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 5295625c0c00..fb584092eb88 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -18,13 +18,14 @@ | |||
18 | 18 | ||
19 | int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | 19 | int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, |
20 | union perf_event *event, | 20 | union perf_event *event, |
21 | struct perf_sample *sample __maybe_unused, | 21 | struct perf_sample *sample, |
22 | struct perf_evsel *evsel __maybe_unused, | 22 | struct perf_evsel *evsel __maybe_unused, |
23 | struct machine *machine) | 23 | struct machine *machine) |
24 | { | 24 | { |
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, event->ip.pid); | 27 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
28 | sample->pid); | ||
28 | 29 | ||
29 | if (thread == NULL) { | 30 | if (thread == NULL) { |
30 | pr_err("problem processing %d event, skipping it.\n", | 31 | pr_err("problem processing %d event, skipping it.\n", |
@@ -33,7 +34,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | |||
33 | } | 34 | } |
34 | 35 | ||
35 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 36 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, |
36 | event->ip.ip, &al); | 37 | sample->ip, &al); |
37 | 38 | ||
38 | if (al.map != NULL) | 39 | if (al.map != NULL) |
39 | al.map->dso->hit = 1; | 40 | al.map->dso->hit = 1; |
@@ -47,7 +48,9 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, | |||
47 | __maybe_unused, | 48 | __maybe_unused, |
48 | struct machine *machine) | 49 | struct machine *machine) |
49 | { | 50 | { |
50 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); | 51 | struct thread *thread = machine__findnew_thread(machine, |
52 | event->fork.pid, | ||
53 | event->fork.tid); | ||
51 | 54 | ||
52 | dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, | 55 | dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, |
53 | event->fork.ppid, event->fork.ptid); | 56 | event->fork.ppid, event->fork.ptid); |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 4fee33b229b0..482f68081cd8 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -21,14 +21,6 @@ | |||
21 | 21 | ||
22 | __thread struct callchain_cursor callchain_cursor; | 22 | __thread struct callchain_cursor callchain_cursor; |
23 | 23 | ||
24 | bool ip_callchain__valid(struct ip_callchain *chain, | ||
25 | const union perf_event *event) | ||
26 | { | ||
27 | unsigned int chain_size = event->header.size; | ||
28 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; | ||
29 | return chain->nr * sizeof(u64) <= chain_size; | ||
30 | } | ||
31 | |||
32 | #define chain_for_each_child(child, parent) \ | 24 | #define chain_for_each_child(child, parent) \ |
33 | list_for_each_entry(child, &parent->children, siblings) | 25 | list_for_each_entry(child, &parent->children, siblings) |
34 | 26 | ||
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 812d5a0ff2bc..2b585bc308cf 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -109,11 +109,6 @@ int callchain_append(struct callchain_root *root, | |||
109 | int callchain_merge(struct callchain_cursor *cursor, | 109 | int callchain_merge(struct callchain_cursor *cursor, |
110 | struct callchain_root *dst, struct callchain_root *src); | 110 | struct callchain_root *dst, struct callchain_root *src); |
111 | 111 | ||
112 | struct ip_callchain; | ||
113 | union perf_event; | ||
114 | |||
115 | bool ip_callchain__valid(struct ip_callchain *chain, | ||
116 | const union perf_event *event); | ||
117 | /* | 112 | /* |
118 | * Initialize a cursor before adding entries inside, but keep | 113 | * Initialize a cursor before adding entries inside, but keep |
119 | * the previously allocated entries as a cache. | 114 | * the previously allocated entries as a cache. |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 49713ae46551..8d51f21107aa 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -686,7 +686,8 @@ int perf_event__preprocess_sample(const union perf_event *event, | |||
686 | struct perf_sample *sample) | 686 | struct perf_sample *sample) |
687 | { | 687 | { |
688 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 688 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
689 | struct thread *thread = machine__findnew_thread(machine, event->ip.pid); | 689 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
690 | sample->pid); | ||
690 | 691 | ||
691 | if (thread == NULL) | 692 | if (thread == NULL) |
692 | return -1; | 693 | return -1; |
@@ -708,7 +709,7 @@ int perf_event__preprocess_sample(const union perf_event *event, | |||
708 | machine__create_kernel_maps(machine); | 709 | machine__create_kernel_maps(machine); |
709 | 710 | ||
710 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 711 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, |
711 | event->ip.ip, al); | 712 | sample->ip, al); |
712 | dump_printf(" ...... dso: %s\n", | 713 | dump_printf(" ...... dso: %s\n", |
713 | al->map ? al->map->dso->long_name : | 714 | al->map ? al->map->dso->long_name : |
714 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 715 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 15db071d96b5..93130d856bf0 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -8,16 +8,6 @@ | |||
8 | #include "map.h" | 8 | #include "map.h" |
9 | #include "build-id.h" | 9 | #include "build-id.h" |
10 | 10 | ||
11 | /* | ||
12 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * | ||
13 | */ | ||
14 | struct ip_event { | ||
15 | struct perf_event_header header; | ||
16 | u64 ip; | ||
17 | u32 pid, tid; | ||
18 | unsigned char __more_data[]; | ||
19 | }; | ||
20 | |||
21 | struct mmap_event { | 11 | struct mmap_event { |
22 | struct perf_event_header header; | 12 | struct perf_event_header header; |
23 | u32 pid, tid; | 13 | u32 pid, tid; |
@@ -63,7 +53,8 @@ struct read_event { | |||
63 | (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \ | 53 | (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \ |
64 | PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | \ | 54 | PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | \ |
65 | PERF_SAMPLE_ID | PERF_SAMPLE_STREAM_ID | \ | 55 | PERF_SAMPLE_ID | PERF_SAMPLE_STREAM_ID | \ |
66 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD) | 56 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | \ |
57 | PERF_SAMPLE_IDENTIFIER) | ||
67 | 58 | ||
68 | struct sample_event { | 59 | struct sample_event { |
69 | struct perf_event_header header; | 60 | struct perf_event_header header; |
@@ -71,6 +62,7 @@ struct sample_event { | |||
71 | }; | 62 | }; |
72 | 63 | ||
73 | struct regs_dump { | 64 | struct regs_dump { |
65 | u64 abi; | ||
74 | u64 *regs; | 66 | u64 *regs; |
75 | }; | 67 | }; |
76 | 68 | ||
@@ -166,7 +158,6 @@ struct tracing_data_event { | |||
166 | 158 | ||
167 | union perf_event { | 159 | union perf_event { |
168 | struct perf_event_header header; | 160 | struct perf_event_header header; |
169 | struct ip_event ip; | ||
170 | struct mmap_event mmap; | 161 | struct mmap_event mmap; |
171 | struct comm_event comm; | 162 | struct comm_event comm; |
172 | struct fork_event fork; | 163 | struct fork_event fork; |
@@ -238,7 +229,10 @@ int perf_event__preprocess_sample(const union perf_event *self, | |||
238 | 229 | ||
239 | const char *perf_event__name(unsigned int id); | 230 | const char *perf_event__name(unsigned int id); |
240 | 231 | ||
232 | size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, | ||
233 | u64 sample_regs_user, u64 read_format); | ||
241 | int perf_event__synthesize_sample(union perf_event *event, u64 type, | 234 | int perf_event__synthesize_sample(union perf_event *event, u64 type, |
235 | u64 sample_regs_user, u64 read_format, | ||
242 | const struct perf_sample *sample, | 236 | const struct perf_sample *sample, |
243 | bool swapped); | 237 | bool swapped); |
244 | 238 | ||
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 1f5105ac5c85..5df4ca91bed3 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -49,26 +49,19 @@ struct perf_evlist *perf_evlist__new(void) | |||
49 | return evlist; | 49 | return evlist; |
50 | } | 50 | } |
51 | 51 | ||
52 | void perf_evlist__config(struct perf_evlist *evlist, | 52 | /** |
53 | struct perf_record_opts *opts) | 53 | * perf_evlist__set_id_pos - set the positions of event ids. |
54 | * @evlist: selected event list | ||
55 | * | ||
56 | * Events with compatible sample types all have the same id_pos | ||
57 | * and is_pos. For convenience, put a copy on evlist. | ||
58 | */ | ||
59 | void perf_evlist__set_id_pos(struct perf_evlist *evlist) | ||
54 | { | 60 | { |
55 | struct perf_evsel *evsel; | 61 | struct perf_evsel *first = perf_evlist__first(evlist); |
56 | /* | ||
57 | * Set the evsel leader links before we configure attributes, | ||
58 | * since some might depend on this info. | ||
59 | */ | ||
60 | if (opts->group) | ||
61 | perf_evlist__set_leader(evlist); | ||
62 | |||
63 | if (evlist->cpus->map[0] < 0) | ||
64 | opts->no_inherit = true; | ||
65 | |||
66 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
67 | perf_evsel__config(evsel, opts); | ||
68 | 62 | ||
69 | if (evlist->nr_entries > 1) | 63 | evlist->id_pos = first->id_pos; |
70 | perf_evsel__set_sample_id(evsel); | 64 | evlist->is_pos = first->is_pos; |
71 | } | ||
72 | } | 65 | } |
73 | 66 | ||
74 | static void perf_evlist__purge(struct perf_evlist *evlist) | 67 | static void perf_evlist__purge(struct perf_evlist *evlist) |
@@ -101,15 +94,20 @@ void perf_evlist__delete(struct perf_evlist *evlist) | |||
101 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | 94 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) |
102 | { | 95 | { |
103 | list_add_tail(&entry->node, &evlist->entries); | 96 | list_add_tail(&entry->node, &evlist->entries); |
104 | ++evlist->nr_entries; | 97 | if (!evlist->nr_entries++) |
98 | perf_evlist__set_id_pos(evlist); | ||
105 | } | 99 | } |
106 | 100 | ||
107 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, | 101 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, |
108 | struct list_head *list, | 102 | struct list_head *list, |
109 | int nr_entries) | 103 | int nr_entries) |
110 | { | 104 | { |
105 | bool set_id_pos = !evlist->nr_entries; | ||
106 | |||
111 | list_splice_tail(list, &evlist->entries); | 107 | list_splice_tail(list, &evlist->entries); |
112 | evlist->nr_entries += nr_entries; | 108 | evlist->nr_entries += nr_entries; |
109 | if (set_id_pos) | ||
110 | perf_evlist__set_id_pos(evlist); | ||
113 | } | 111 | } |
114 | 112 | ||
115 | void __perf_evlist__set_leader(struct list_head *list) | 113 | void __perf_evlist__set_leader(struct list_head *list) |
@@ -210,6 +208,21 @@ perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) | |||
210 | return NULL; | 208 | return NULL; |
211 | } | 209 | } |
212 | 210 | ||
211 | struct perf_evsel * | ||
212 | perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, | ||
213 | const char *name) | ||
214 | { | ||
215 | struct perf_evsel *evsel; | ||
216 | |||
217 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
218 | if ((evsel->attr.type == PERF_TYPE_TRACEPOINT) && | ||
219 | (strcmp(evsel->name, name) == 0)) | ||
220 | return evsel; | ||
221 | } | ||
222 | |||
223 | return NULL; | ||
224 | } | ||
225 | |||
213 | int perf_evlist__add_newtp(struct perf_evlist *evlist, | 226 | int perf_evlist__add_newtp(struct perf_evlist *evlist, |
214 | const char *sys, const char *name, void *handler) | 227 | const char *sys, const char *name, void *handler) |
215 | { | 228 | { |
@@ -371,6 +384,55 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | |||
371 | return NULL; | 384 | return NULL; |
372 | } | 385 | } |
373 | 386 | ||
387 | static int perf_evlist__event2id(struct perf_evlist *evlist, | ||
388 | union perf_event *event, u64 *id) | ||
389 | { | ||
390 | const u64 *array = event->sample.array; | ||
391 | ssize_t n; | ||
392 | |||
393 | n = (event->header.size - sizeof(event->header)) >> 3; | ||
394 | |||
395 | if (event->header.type == PERF_RECORD_SAMPLE) { | ||
396 | if (evlist->id_pos >= n) | ||
397 | return -1; | ||
398 | *id = array[evlist->id_pos]; | ||
399 | } else { | ||
400 | if (evlist->is_pos > n) | ||
401 | return -1; | ||
402 | n -= evlist->is_pos; | ||
403 | *id = array[n]; | ||
404 | } | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist, | ||
409 | union perf_event *event) | ||
410 | { | ||
411 | struct hlist_head *head; | ||
412 | struct perf_sample_id *sid; | ||
413 | int hash; | ||
414 | u64 id; | ||
415 | |||
416 | if (evlist->nr_entries == 1) | ||
417 | return perf_evlist__first(evlist); | ||
418 | |||
419 | if (perf_evlist__event2id(evlist, event, &id)) | ||
420 | return NULL; | ||
421 | |||
422 | /* Synthesized events have an id of zero */ | ||
423 | if (!id) | ||
424 | return perf_evlist__first(evlist); | ||
425 | |||
426 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | ||
427 | head = &evlist->heads[hash]; | ||
428 | |||
429 | hlist_for_each_entry(sid, head, node) { | ||
430 | if (sid->id == id) | ||
431 | return sid->evsel; | ||
432 | } | ||
433 | return NULL; | ||
434 | } | ||
435 | |||
374 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | 436 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) |
375 | { | 437 | { |
376 | struct perf_mmap *md = &evlist->mmap[idx]; | 438 | struct perf_mmap *md = &evlist->mmap[idx]; |
@@ -681,20 +743,40 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) | |||
681 | 743 | ||
682 | bool perf_evlist__valid_sample_type(struct perf_evlist *evlist) | 744 | bool perf_evlist__valid_sample_type(struct perf_evlist *evlist) |
683 | { | 745 | { |
684 | struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; | 746 | struct perf_evsel *pos; |
685 | 747 | ||
686 | list_for_each_entry_continue(pos, &evlist->entries, node) { | 748 | if (evlist->nr_entries == 1) |
687 | if (first->attr.sample_type != pos->attr.sample_type) | 749 | return true; |
750 | |||
751 | if (evlist->id_pos < 0 || evlist->is_pos < 0) | ||
752 | return false; | ||
753 | |||
754 | list_for_each_entry(pos, &evlist->entries, node) { | ||
755 | if (pos->id_pos != evlist->id_pos || | ||
756 | pos->is_pos != evlist->is_pos) | ||
688 | return false; | 757 | return false; |
689 | } | 758 | } |
690 | 759 | ||
691 | return true; | 760 | return true; |
692 | } | 761 | } |
693 | 762 | ||
694 | u64 perf_evlist__sample_type(struct perf_evlist *evlist) | 763 | u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist) |
695 | { | 764 | { |
696 | struct perf_evsel *first = perf_evlist__first(evlist); | 765 | struct perf_evsel *evsel; |
697 | return first->attr.sample_type; | 766 | |
767 | if (evlist->combined_sample_type) | ||
768 | return evlist->combined_sample_type; | ||
769 | |||
770 | list_for_each_entry(evsel, &evlist->entries, node) | ||
771 | evlist->combined_sample_type |= evsel->attr.sample_type; | ||
772 | |||
773 | return evlist->combined_sample_type; | ||
774 | } | ||
775 | |||
776 | u64 perf_evlist__combined_sample_type(struct perf_evlist *evlist) | ||
777 | { | ||
778 | evlist->combined_sample_type = 0; | ||
779 | return __perf_evlist__combined_sample_type(evlist); | ||
698 | } | 780 | } |
699 | 781 | ||
700 | bool perf_evlist__valid_read_format(struct perf_evlist *evlist) | 782 | bool perf_evlist__valid_read_format(struct perf_evlist *evlist) |
@@ -749,6 +831,9 @@ u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist) | |||
749 | 831 | ||
750 | if (sample_type & PERF_SAMPLE_CPU) | 832 | if (sample_type & PERF_SAMPLE_CPU) |
751 | size += sizeof(data->cpu) * 2; | 833 | size += sizeof(data->cpu) * 2; |
834 | |||
835 | if (sample_type & PERF_SAMPLE_IDENTIFIER) | ||
836 | size += sizeof(data->id); | ||
752 | out: | 837 | out: |
753 | return size; | 838 | return size; |
754 | } | 839 | } |
@@ -907,7 +992,10 @@ int perf_evlist__start_workload(struct perf_evlist *evlist) | |||
907 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, | 992 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, |
908 | struct perf_sample *sample) | 993 | struct perf_sample *sample) |
909 | { | 994 | { |
910 | struct perf_evsel *evsel = perf_evlist__first(evlist); | 995 | struct perf_evsel *evsel = perf_evlist__event2evsel(evlist, event); |
996 | |||
997 | if (!evsel) | ||
998 | return -EFAULT; | ||
911 | return perf_evsel__parse_sample(evsel, event, sample); | 999 | return perf_evsel__parse_sample(evsel, event, sample); |
912 | } | 1000 | } |
913 | 1001 | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 327ababa67b6..841a39405f6a 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -32,6 +32,9 @@ struct perf_evlist { | |||
32 | int nr_fds; | 32 | int nr_fds; |
33 | int nr_mmaps; | 33 | int nr_mmaps; |
34 | int mmap_len; | 34 | int mmap_len; |
35 | int id_pos; | ||
36 | int is_pos; | ||
37 | u64 combined_sample_type; | ||
35 | struct { | 38 | struct { |
36 | int cork_fd; | 39 | int cork_fd; |
37 | pid_t pid; | 40 | pid_t pid; |
@@ -71,6 +74,10 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter); | |||
71 | struct perf_evsel * | 74 | struct perf_evsel * |
72 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id); | 75 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id); |
73 | 76 | ||
77 | struct perf_evsel * | ||
78 | perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, | ||
79 | const char *name); | ||
80 | |||
74 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, | 81 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, |
75 | int cpu, int thread, u64 id); | 82 | int cpu, int thread, u64 id); |
76 | 83 | ||
@@ -85,6 +92,8 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); | |||
85 | int perf_evlist__open(struct perf_evlist *evlist); | 92 | int perf_evlist__open(struct perf_evlist *evlist); |
86 | void perf_evlist__close(struct perf_evlist *evlist); | 93 | void perf_evlist__close(struct perf_evlist *evlist); |
87 | 94 | ||
95 | void perf_evlist__set_id_pos(struct perf_evlist *evlist); | ||
96 | bool perf_can_sample_identifier(void); | ||
88 | void perf_evlist__config(struct perf_evlist *evlist, | 97 | void perf_evlist__config(struct perf_evlist *evlist, |
89 | struct perf_record_opts *opts); | 98 | struct perf_record_opts *opts); |
90 | 99 | ||
@@ -121,7 +130,8 @@ void __perf_evlist__set_leader(struct list_head *list); | |||
121 | void perf_evlist__set_leader(struct perf_evlist *evlist); | 130 | void perf_evlist__set_leader(struct perf_evlist *evlist); |
122 | 131 | ||
123 | u64 perf_evlist__read_format(struct perf_evlist *evlist); | 132 | u64 perf_evlist__read_format(struct perf_evlist *evlist); |
124 | u64 perf_evlist__sample_type(struct perf_evlist *evlist); | 133 | u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist); |
134 | u64 perf_evlist__combined_sample_type(struct perf_evlist *evlist); | ||
125 | bool perf_evlist__sample_id_all(struct perf_evlist *evlist); | 135 | bool perf_evlist__sample_id_all(struct perf_evlist *evlist); |
126 | u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist); | 136 | u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist); |
127 | 137 | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 47cbe1e58b73..e8745fb635a7 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -31,7 +31,7 @@ static struct { | |||
31 | 31 | ||
32 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 32 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
33 | 33 | ||
34 | static int __perf_evsel__sample_size(u64 sample_type) | 34 | int __perf_evsel__sample_size(u64 sample_type) |
35 | { | 35 | { |
36 | u64 mask = sample_type & PERF_SAMPLE_MASK; | 36 | u64 mask = sample_type & PERF_SAMPLE_MASK; |
37 | int size = 0; | 37 | int size = 0; |
@@ -47,6 +47,72 @@ static int __perf_evsel__sample_size(u64 sample_type) | |||
47 | return size; | 47 | return size; |
48 | } | 48 | } |
49 | 49 | ||
50 | /** | ||
51 | * __perf_evsel__calc_id_pos - calculate id_pos. | ||
52 | * @sample_type: sample type | ||
53 | * | ||
54 | * This function returns the position of the event id (PERF_SAMPLE_ID or | ||
55 | * PERF_SAMPLE_IDENTIFIER) in a sample event i.e. in the array of struct | ||
56 | * sample_event. | ||
57 | */ | ||
58 | static int __perf_evsel__calc_id_pos(u64 sample_type) | ||
59 | { | ||
60 | int idx = 0; | ||
61 | |||
62 | if (sample_type & PERF_SAMPLE_IDENTIFIER) | ||
63 | return 0; | ||
64 | |||
65 | if (!(sample_type & PERF_SAMPLE_ID)) | ||
66 | return -1; | ||
67 | |||
68 | if (sample_type & PERF_SAMPLE_IP) | ||
69 | idx += 1; | ||
70 | |||
71 | if (sample_type & PERF_SAMPLE_TID) | ||
72 | idx += 1; | ||
73 | |||
74 | if (sample_type & PERF_SAMPLE_TIME) | ||
75 | idx += 1; | ||
76 | |||
77 | if (sample_type & PERF_SAMPLE_ADDR) | ||
78 | idx += 1; | ||
79 | |||
80 | return idx; | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * __perf_evsel__calc_is_pos - calculate is_pos. | ||
85 | * @sample_type: sample type | ||
86 | * | ||
87 | * This function returns the position (counting backwards) of the event id | ||
88 | * (PERF_SAMPLE_ID or PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if | ||
89 | * sample_id_all is used there is an id sample appended to non-sample events. | ||
90 | */ | ||
91 | static int __perf_evsel__calc_is_pos(u64 sample_type) | ||
92 | { | ||
93 | int idx = 1; | ||
94 | |||
95 | if (sample_type & PERF_SAMPLE_IDENTIFIER) | ||
96 | return 1; | ||
97 | |||
98 | if (!(sample_type & PERF_SAMPLE_ID)) | ||
99 | return -1; | ||
100 | |||
101 | if (sample_type & PERF_SAMPLE_CPU) | ||
102 | idx += 1; | ||
103 | |||
104 | if (sample_type & PERF_SAMPLE_STREAM_ID) | ||
105 | idx += 1; | ||
106 | |||
107 | return idx; | ||
108 | } | ||
109 | |||
110 | void perf_evsel__calc_id_pos(struct perf_evsel *evsel) | ||
111 | { | ||
112 | evsel->id_pos = __perf_evsel__calc_id_pos(evsel->attr.sample_type); | ||
113 | evsel->is_pos = __perf_evsel__calc_is_pos(evsel->attr.sample_type); | ||
114 | } | ||
115 | |||
50 | void hists__init(struct hists *hists) | 116 | void hists__init(struct hists *hists) |
51 | { | 117 | { |
52 | memset(hists, 0, sizeof(*hists)); | 118 | memset(hists, 0, sizeof(*hists)); |
@@ -63,6 +129,7 @@ void __perf_evsel__set_sample_bit(struct perf_evsel *evsel, | |||
63 | if (!(evsel->attr.sample_type & bit)) { | 129 | if (!(evsel->attr.sample_type & bit)) { |
64 | evsel->attr.sample_type |= bit; | 130 | evsel->attr.sample_type |= bit; |
65 | evsel->sample_size += sizeof(u64); | 131 | evsel->sample_size += sizeof(u64); |
132 | perf_evsel__calc_id_pos(evsel); | ||
66 | } | 133 | } |
67 | } | 134 | } |
68 | 135 | ||
@@ -72,12 +139,19 @@ void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel, | |||
72 | if (evsel->attr.sample_type & bit) { | 139 | if (evsel->attr.sample_type & bit) { |
73 | evsel->attr.sample_type &= ~bit; | 140 | evsel->attr.sample_type &= ~bit; |
74 | evsel->sample_size -= sizeof(u64); | 141 | evsel->sample_size -= sizeof(u64); |
142 | perf_evsel__calc_id_pos(evsel); | ||
75 | } | 143 | } |
76 | } | 144 | } |
77 | 145 | ||
78 | void perf_evsel__set_sample_id(struct perf_evsel *evsel) | 146 | void perf_evsel__set_sample_id(struct perf_evsel *evsel, |
147 | bool can_sample_identifier) | ||
79 | { | 148 | { |
80 | perf_evsel__set_sample_bit(evsel, ID); | 149 | if (can_sample_identifier) { |
150 | perf_evsel__reset_sample_bit(evsel, ID); | ||
151 | perf_evsel__set_sample_bit(evsel, IDENTIFIER); | ||
152 | } else { | ||
153 | perf_evsel__set_sample_bit(evsel, ID); | ||
154 | } | ||
81 | evsel->attr.read_format |= PERF_FORMAT_ID; | 155 | evsel->attr.read_format |= PERF_FORMAT_ID; |
82 | } | 156 | } |
83 | 157 | ||
@@ -90,6 +164,7 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
90 | INIT_LIST_HEAD(&evsel->node); | 164 | INIT_LIST_HEAD(&evsel->node); |
91 | hists__init(&evsel->hists); | 165 | hists__init(&evsel->hists); |
92 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); | 166 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); |
167 | perf_evsel__calc_id_pos(evsel); | ||
93 | } | 168 | } |
94 | 169 | ||
95 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 170 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
@@ -509,7 +584,7 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
509 | * We need ID even in case of single event, because | 584 | * We need ID even in case of single event, because |
510 | * PERF_SAMPLE_READ process ID specific data. | 585 | * PERF_SAMPLE_READ process ID specific data. |
511 | */ | 586 | */ |
512 | perf_evsel__set_sample_id(evsel); | 587 | perf_evsel__set_sample_id(evsel, false); |
513 | 588 | ||
514 | /* | 589 | /* |
515 | * Apply group format only if we belong to group | 590 | * Apply group format only if we belong to group |
@@ -1088,6 +1163,11 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, | |||
1088 | array += ((event->header.size - | 1163 | array += ((event->header.size - |
1089 | sizeof(event->header)) / sizeof(u64)) - 1; | 1164 | sizeof(event->header)) / sizeof(u64)) - 1; |
1090 | 1165 | ||
1166 | if (type & PERF_SAMPLE_IDENTIFIER) { | ||
1167 | sample->id = *array; | ||
1168 | array--; | ||
1169 | } | ||
1170 | |||
1091 | if (type & PERF_SAMPLE_CPU) { | 1171 | if (type & PERF_SAMPLE_CPU) { |
1092 | u.val64 = *array; | 1172 | u.val64 = *array; |
1093 | if (swapped) { | 1173 | if (swapped) { |
@@ -1131,24 +1211,30 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, | |||
1131 | return 0; | 1211 | return 0; |
1132 | } | 1212 | } |
1133 | 1213 | ||
1134 | static bool sample_overlap(const union perf_event *event, | 1214 | static inline bool overflow(const void *endp, u16 max_size, const void *offset, |
1135 | const void *offset, u64 size) | 1215 | u64 size) |
1136 | { | 1216 | { |
1137 | const void *base = event; | 1217 | return size > max_size || offset + size > endp; |
1218 | } | ||
1138 | 1219 | ||
1139 | if (offset + size > base + event->header.size) | 1220 | #define OVERFLOW_CHECK(offset, size, max_size) \ |
1140 | return true; | 1221 | do { \ |
1222 | if (overflow(endp, (max_size), (offset), (size))) \ | ||
1223 | return -EFAULT; \ | ||
1224 | } while (0) | ||
1141 | 1225 | ||
1142 | return false; | 1226 | #define OVERFLOW_CHECK_u64(offset) \ |
1143 | } | 1227 | OVERFLOW_CHECK(offset, sizeof(u64), sizeof(u64)) |
1144 | 1228 | ||
1145 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | 1229 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, |
1146 | struct perf_sample *data) | 1230 | struct perf_sample *data) |
1147 | { | 1231 | { |
1148 | u64 type = evsel->attr.sample_type; | 1232 | u64 type = evsel->attr.sample_type; |
1149 | u64 regs_user = evsel->attr.sample_regs_user; | ||
1150 | bool swapped = evsel->needs_swap; | 1233 | bool swapped = evsel->needs_swap; |
1151 | const u64 *array; | 1234 | const u64 *array; |
1235 | u16 max_size = event->header.size; | ||
1236 | const void *endp = (void *)event + max_size; | ||
1237 | u64 sz; | ||
1152 | 1238 | ||
1153 | /* | 1239 | /* |
1154 | * used for cross-endian analysis. See git commit 65014ab3 | 1240 | * used for cross-endian analysis. See git commit 65014ab3 |
@@ -1170,11 +1256,22 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1170 | 1256 | ||
1171 | array = event->sample.array; | 1257 | array = event->sample.array; |
1172 | 1258 | ||
1259 | /* | ||
1260 | * The evsel's sample_size is based on PERF_SAMPLE_MASK which includes | ||
1261 | * up to PERF_SAMPLE_PERIOD. After that overflow() must be used to | ||
1262 | * check the format does not go past the end of the event. | ||
1263 | */ | ||
1173 | if (evsel->sample_size + sizeof(event->header) > event->header.size) | 1264 | if (evsel->sample_size + sizeof(event->header) > event->header.size) |
1174 | return -EFAULT; | 1265 | return -EFAULT; |
1175 | 1266 | ||
1267 | data->id = -1ULL; | ||
1268 | if (type & PERF_SAMPLE_IDENTIFIER) { | ||
1269 | data->id = *array; | ||
1270 | array++; | ||
1271 | } | ||
1272 | |||
1176 | if (type & PERF_SAMPLE_IP) { | 1273 | if (type & PERF_SAMPLE_IP) { |
1177 | data->ip = event->ip.ip; | 1274 | data->ip = *array; |
1178 | array++; | 1275 | array++; |
1179 | } | 1276 | } |
1180 | 1277 | ||
@@ -1203,7 +1300,6 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1203 | array++; | 1300 | array++; |
1204 | } | 1301 | } |
1205 | 1302 | ||
1206 | data->id = -1ULL; | ||
1207 | if (type & PERF_SAMPLE_ID) { | 1303 | if (type & PERF_SAMPLE_ID) { |
1208 | data->id = *array; | 1304 | data->id = *array; |
1209 | array++; | 1305 | array++; |
@@ -1235,6 +1331,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1235 | if (type & PERF_SAMPLE_READ) { | 1331 | if (type & PERF_SAMPLE_READ) { |
1236 | u64 read_format = evsel->attr.read_format; | 1332 | u64 read_format = evsel->attr.read_format; |
1237 | 1333 | ||
1334 | OVERFLOW_CHECK_u64(array); | ||
1238 | if (read_format & PERF_FORMAT_GROUP) | 1335 | if (read_format & PERF_FORMAT_GROUP) |
1239 | data->read.group.nr = *array; | 1336 | data->read.group.nr = *array; |
1240 | else | 1337 | else |
@@ -1243,41 +1340,51 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1243 | array++; | 1340 | array++; |
1244 | 1341 | ||
1245 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { | 1342 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { |
1343 | OVERFLOW_CHECK_u64(array); | ||
1246 | data->read.time_enabled = *array; | 1344 | data->read.time_enabled = *array; |
1247 | array++; | 1345 | array++; |
1248 | } | 1346 | } |
1249 | 1347 | ||
1250 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { | 1348 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { |
1349 | OVERFLOW_CHECK_u64(array); | ||
1251 | data->read.time_running = *array; | 1350 | data->read.time_running = *array; |
1252 | array++; | 1351 | array++; |
1253 | } | 1352 | } |
1254 | 1353 | ||
1255 | /* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */ | 1354 | /* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */ |
1256 | if (read_format & PERF_FORMAT_GROUP) { | 1355 | if (read_format & PERF_FORMAT_GROUP) { |
1257 | data->read.group.values = (struct sample_read_value *) array; | 1356 | const u64 max_group_nr = UINT64_MAX / |
1258 | array = (void *) array + data->read.group.nr * | 1357 | sizeof(struct sample_read_value); |
1259 | sizeof(struct sample_read_value); | 1358 | |
1359 | if (data->read.group.nr > max_group_nr) | ||
1360 | return -EFAULT; | ||
1361 | sz = data->read.group.nr * | ||
1362 | sizeof(struct sample_read_value); | ||
1363 | OVERFLOW_CHECK(array, sz, max_size); | ||
1364 | data->read.group.values = | ||
1365 | (struct sample_read_value *)array; | ||
1366 | array = (void *)array + sz; | ||
1260 | } else { | 1367 | } else { |
1368 | OVERFLOW_CHECK_u64(array); | ||
1261 | data->read.one.id = *array; | 1369 | data->read.one.id = *array; |
1262 | array++; | 1370 | array++; |
1263 | } | 1371 | } |
1264 | } | 1372 | } |
1265 | 1373 | ||
1266 | if (type & PERF_SAMPLE_CALLCHAIN) { | 1374 | if (type & PERF_SAMPLE_CALLCHAIN) { |
1267 | if (sample_overlap(event, array, sizeof(data->callchain->nr))) | 1375 | const u64 max_callchain_nr = UINT64_MAX / sizeof(u64); |
1268 | return -EFAULT; | ||
1269 | |||
1270 | data->callchain = (struct ip_callchain *)array; | ||
1271 | 1376 | ||
1272 | if (sample_overlap(event, array, data->callchain->nr)) | 1377 | OVERFLOW_CHECK_u64(array); |
1378 | data->callchain = (struct ip_callchain *)array++; | ||
1379 | if (data->callchain->nr > max_callchain_nr) | ||
1273 | return -EFAULT; | 1380 | return -EFAULT; |
1274 | 1381 | sz = data->callchain->nr * sizeof(u64); | |
1275 | array += 1 + data->callchain->nr; | 1382 | OVERFLOW_CHECK(array, sz, max_size); |
1383 | array = (void *)array + sz; | ||
1276 | } | 1384 | } |
1277 | 1385 | ||
1278 | if (type & PERF_SAMPLE_RAW) { | 1386 | if (type & PERF_SAMPLE_RAW) { |
1279 | const u64 *pdata; | 1387 | OVERFLOW_CHECK_u64(array); |
1280 | |||
1281 | u.val64 = *array; | 1388 | u.val64 = *array; |
1282 | if (WARN_ONCE(swapped, | 1389 | if (WARN_ONCE(swapped, |
1283 | "Endianness of raw data not corrected!\n")) { | 1390 | "Endianness of raw data not corrected!\n")) { |
@@ -1286,65 +1393,71 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1286 | u.val32[0] = bswap_32(u.val32[0]); | 1393 | u.val32[0] = bswap_32(u.val32[0]); |
1287 | u.val32[1] = bswap_32(u.val32[1]); | 1394 | u.val32[1] = bswap_32(u.val32[1]); |
1288 | } | 1395 | } |
1289 | |||
1290 | if (sample_overlap(event, array, sizeof(u32))) | ||
1291 | return -EFAULT; | ||
1292 | |||
1293 | data->raw_size = u.val32[0]; | 1396 | data->raw_size = u.val32[0]; |
1294 | pdata = (void *) array + sizeof(u32); | 1397 | array = (void *)array + sizeof(u32); |
1295 | |||
1296 | if (sample_overlap(event, pdata, data->raw_size)) | ||
1297 | return -EFAULT; | ||
1298 | |||
1299 | data->raw_data = (void *) pdata; | ||
1300 | 1398 | ||
1301 | array = (void *)array + data->raw_size + sizeof(u32); | 1399 | OVERFLOW_CHECK(array, data->raw_size, max_size); |
1400 | data->raw_data = (void *)array; | ||
1401 | array = (void *)array + data->raw_size; | ||
1302 | } | 1402 | } |
1303 | 1403 | ||
1304 | if (type & PERF_SAMPLE_BRANCH_STACK) { | 1404 | if (type & PERF_SAMPLE_BRANCH_STACK) { |
1305 | u64 sz; | 1405 | const u64 max_branch_nr = UINT64_MAX / |
1406 | sizeof(struct branch_entry); | ||
1306 | 1407 | ||
1307 | data->branch_stack = (struct branch_stack *)array; | 1408 | OVERFLOW_CHECK_u64(array); |
1308 | array++; /* nr */ | 1409 | data->branch_stack = (struct branch_stack *)array++; |
1309 | 1410 | ||
1411 | if (data->branch_stack->nr > max_branch_nr) | ||
1412 | return -EFAULT; | ||
1310 | sz = data->branch_stack->nr * sizeof(struct branch_entry); | 1413 | sz = data->branch_stack->nr * sizeof(struct branch_entry); |
1311 | sz /= sizeof(u64); | 1414 | OVERFLOW_CHECK(array, sz, max_size); |
1312 | array += sz; | 1415 | array = (void *)array + sz; |
1313 | } | 1416 | } |
1314 | 1417 | ||
1315 | if (type & PERF_SAMPLE_REGS_USER) { | 1418 | if (type & PERF_SAMPLE_REGS_USER) { |
1316 | /* First u64 tells us if we have any regs in sample. */ | 1419 | OVERFLOW_CHECK_u64(array); |
1317 | u64 avail = *array++; | 1420 | data->user_regs.abi = *array; |
1421 | array++; | ||
1422 | |||
1423 | if (data->user_regs.abi) { | ||
1424 | u64 regs_user = evsel->attr.sample_regs_user; | ||
1318 | 1425 | ||
1319 | if (avail) { | 1426 | sz = hweight_long(regs_user) * sizeof(u64); |
1427 | OVERFLOW_CHECK(array, sz, max_size); | ||
1320 | data->user_regs.regs = (u64 *)array; | 1428 | data->user_regs.regs = (u64 *)array; |
1321 | array += hweight_long(regs_user); | 1429 | array = (void *)array + sz; |
1322 | } | 1430 | } |
1323 | } | 1431 | } |
1324 | 1432 | ||
1325 | if (type & PERF_SAMPLE_STACK_USER) { | 1433 | if (type & PERF_SAMPLE_STACK_USER) { |
1326 | u64 size = *array++; | 1434 | OVERFLOW_CHECK_u64(array); |
1435 | sz = *array++; | ||
1327 | 1436 | ||
1328 | data->user_stack.offset = ((char *)(array - 1) | 1437 | data->user_stack.offset = ((char *)(array - 1) |
1329 | - (char *) event); | 1438 | - (char *) event); |
1330 | 1439 | ||
1331 | if (!size) { | 1440 | if (!sz) { |
1332 | data->user_stack.size = 0; | 1441 | data->user_stack.size = 0; |
1333 | } else { | 1442 | } else { |
1443 | OVERFLOW_CHECK(array, sz, max_size); | ||
1334 | data->user_stack.data = (char *)array; | 1444 | data->user_stack.data = (char *)array; |
1335 | array += size / sizeof(*array); | 1445 | array = (void *)array + sz; |
1446 | OVERFLOW_CHECK_u64(array); | ||
1336 | data->user_stack.size = *array++; | 1447 | data->user_stack.size = *array++; |
1337 | } | 1448 | } |
1338 | } | 1449 | } |
1339 | 1450 | ||
1340 | data->weight = 0; | 1451 | data->weight = 0; |
1341 | if (type & PERF_SAMPLE_WEIGHT) { | 1452 | if (type & PERF_SAMPLE_WEIGHT) { |
1453 | OVERFLOW_CHECK_u64(array); | ||
1342 | data->weight = *array; | 1454 | data->weight = *array; |
1343 | array++; | 1455 | array++; |
1344 | } | 1456 | } |
1345 | 1457 | ||
1346 | data->data_src = PERF_MEM_DATA_SRC_NONE; | 1458 | data->data_src = PERF_MEM_DATA_SRC_NONE; |
1347 | if (type & PERF_SAMPLE_DATA_SRC) { | 1459 | if (type & PERF_SAMPLE_DATA_SRC) { |
1460 | OVERFLOW_CHECK_u64(array); | ||
1348 | data->data_src = *array; | 1461 | data->data_src = *array; |
1349 | array++; | 1462 | array++; |
1350 | } | 1463 | } |
@@ -1352,12 +1465,105 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1352 | return 0; | 1465 | return 0; |
1353 | } | 1466 | } |
1354 | 1467 | ||
1468 | size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, | ||
1469 | u64 sample_regs_user, u64 read_format) | ||
1470 | { | ||
1471 | size_t sz, result = sizeof(struct sample_event); | ||
1472 | |||
1473 | if (type & PERF_SAMPLE_IDENTIFIER) | ||
1474 | result += sizeof(u64); | ||
1475 | |||
1476 | if (type & PERF_SAMPLE_IP) | ||
1477 | result += sizeof(u64); | ||
1478 | |||
1479 | if (type & PERF_SAMPLE_TID) | ||
1480 | result += sizeof(u64); | ||
1481 | |||
1482 | if (type & PERF_SAMPLE_TIME) | ||
1483 | result += sizeof(u64); | ||
1484 | |||
1485 | if (type & PERF_SAMPLE_ADDR) | ||
1486 | result += sizeof(u64); | ||
1487 | |||
1488 | if (type & PERF_SAMPLE_ID) | ||
1489 | result += sizeof(u64); | ||
1490 | |||
1491 | if (type & PERF_SAMPLE_STREAM_ID) | ||
1492 | result += sizeof(u64); | ||
1493 | |||
1494 | if (type & PERF_SAMPLE_CPU) | ||
1495 | result += sizeof(u64); | ||
1496 | |||
1497 | if (type & PERF_SAMPLE_PERIOD) | ||
1498 | result += sizeof(u64); | ||
1499 | |||
1500 | if (type & PERF_SAMPLE_READ) { | ||
1501 | result += sizeof(u64); | ||
1502 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
1503 | result += sizeof(u64); | ||
1504 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
1505 | result += sizeof(u64); | ||
1506 | /* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */ | ||
1507 | if (read_format & PERF_FORMAT_GROUP) { | ||
1508 | sz = sample->read.group.nr * | ||
1509 | sizeof(struct sample_read_value); | ||
1510 | result += sz; | ||
1511 | } else { | ||
1512 | result += sizeof(u64); | ||
1513 | } | ||
1514 | } | ||
1515 | |||
1516 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
1517 | sz = (sample->callchain->nr + 1) * sizeof(u64); | ||
1518 | result += sz; | ||
1519 | } | ||
1520 | |||
1521 | if (type & PERF_SAMPLE_RAW) { | ||
1522 | result += sizeof(u32); | ||
1523 | result += sample->raw_size; | ||
1524 | } | ||
1525 | |||
1526 | if (type & PERF_SAMPLE_BRANCH_STACK) { | ||
1527 | sz = sample->branch_stack->nr * sizeof(struct branch_entry); | ||
1528 | sz += sizeof(u64); | ||
1529 | result += sz; | ||
1530 | } | ||
1531 | |||
1532 | if (type & PERF_SAMPLE_REGS_USER) { | ||
1533 | if (sample->user_regs.abi) { | ||
1534 | result += sizeof(u64); | ||
1535 | sz = hweight_long(sample_regs_user) * sizeof(u64); | ||
1536 | result += sz; | ||
1537 | } else { | ||
1538 | result += sizeof(u64); | ||
1539 | } | ||
1540 | } | ||
1541 | |||
1542 | if (type & PERF_SAMPLE_STACK_USER) { | ||
1543 | sz = sample->user_stack.size; | ||
1544 | result += sizeof(u64); | ||
1545 | if (sz) { | ||
1546 | result += sz; | ||
1547 | result += sizeof(u64); | ||
1548 | } | ||
1549 | } | ||
1550 | |||
1551 | if (type & PERF_SAMPLE_WEIGHT) | ||
1552 | result += sizeof(u64); | ||
1553 | |||
1554 | if (type & PERF_SAMPLE_DATA_SRC) | ||
1555 | result += sizeof(u64); | ||
1556 | |||
1557 | return result; | ||
1558 | } | ||
1559 | |||
1355 | int perf_event__synthesize_sample(union perf_event *event, u64 type, | 1560 | int perf_event__synthesize_sample(union perf_event *event, u64 type, |
1561 | u64 sample_regs_user, u64 read_format, | ||
1356 | const struct perf_sample *sample, | 1562 | const struct perf_sample *sample, |
1357 | bool swapped) | 1563 | bool swapped) |
1358 | { | 1564 | { |
1359 | u64 *array; | 1565 | u64 *array; |
1360 | 1566 | size_t sz; | |
1361 | /* | 1567 | /* |
1362 | * used for cross-endian analysis. See git commit 65014ab3 | 1568 | * used for cross-endian analysis. See git commit 65014ab3 |
1363 | * for why this goofiness is needed. | 1569 | * for why this goofiness is needed. |
@@ -1366,8 +1572,13 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, | |||
1366 | 1572 | ||
1367 | array = event->sample.array; | 1573 | array = event->sample.array; |
1368 | 1574 | ||
1575 | if (type & PERF_SAMPLE_IDENTIFIER) { | ||
1576 | *array = sample->id; | ||
1577 | array++; | ||
1578 | } | ||
1579 | |||
1369 | if (type & PERF_SAMPLE_IP) { | 1580 | if (type & PERF_SAMPLE_IP) { |
1370 | event->ip.ip = sample->ip; | 1581 | *array = sample->ip; |
1371 | array++; | 1582 | array++; |
1372 | } | 1583 | } |
1373 | 1584 | ||
@@ -1425,6 +1636,97 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, | |||
1425 | array++; | 1636 | array++; |
1426 | } | 1637 | } |
1427 | 1638 | ||
1639 | if (type & PERF_SAMPLE_READ) { | ||
1640 | if (read_format & PERF_FORMAT_GROUP) | ||
1641 | *array = sample->read.group.nr; | ||
1642 | else | ||
1643 | *array = sample->read.one.value; | ||
1644 | array++; | ||
1645 | |||
1646 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { | ||
1647 | *array = sample->read.time_enabled; | ||
1648 | array++; | ||
1649 | } | ||
1650 | |||
1651 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { | ||
1652 | *array = sample->read.time_running; | ||
1653 | array++; | ||
1654 | } | ||
1655 | |||
1656 | /* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */ | ||
1657 | if (read_format & PERF_FORMAT_GROUP) { | ||
1658 | sz = sample->read.group.nr * | ||
1659 | sizeof(struct sample_read_value); | ||
1660 | memcpy(array, sample->read.group.values, sz); | ||
1661 | array = (void *)array + sz; | ||
1662 | } else { | ||
1663 | *array = sample->read.one.id; | ||
1664 | array++; | ||
1665 | } | ||
1666 | } | ||
1667 | |||
1668 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
1669 | sz = (sample->callchain->nr + 1) * sizeof(u64); | ||
1670 | memcpy(array, sample->callchain, sz); | ||
1671 | array = (void *)array + sz; | ||
1672 | } | ||
1673 | |||
1674 | if (type & PERF_SAMPLE_RAW) { | ||
1675 | u.val32[0] = sample->raw_size; | ||
1676 | if (WARN_ONCE(swapped, | ||
1677 | "Endianness of raw data not corrected!\n")) { | ||
1678 | /* | ||
1679 | * Inverse of what is done in perf_evsel__parse_sample | ||
1680 | */ | ||
1681 | u.val32[0] = bswap_32(u.val32[0]); | ||
1682 | u.val32[1] = bswap_32(u.val32[1]); | ||
1683 | u.val64 = bswap_64(u.val64); | ||
1684 | } | ||
1685 | *array = u.val64; | ||
1686 | array = (void *)array + sizeof(u32); | ||
1687 | |||
1688 | memcpy(array, sample->raw_data, sample->raw_size); | ||
1689 | array = (void *)array + sample->raw_size; | ||
1690 | } | ||
1691 | |||
1692 | if (type & PERF_SAMPLE_BRANCH_STACK) { | ||
1693 | sz = sample->branch_stack->nr * sizeof(struct branch_entry); | ||
1694 | sz += sizeof(u64); | ||
1695 | memcpy(array, sample->branch_stack, sz); | ||
1696 | array = (void *)array + sz; | ||
1697 | } | ||
1698 | |||
1699 | if (type & PERF_SAMPLE_REGS_USER) { | ||
1700 | if (sample->user_regs.abi) { | ||
1701 | *array++ = sample->user_regs.abi; | ||
1702 | sz = hweight_long(sample_regs_user) * sizeof(u64); | ||
1703 | memcpy(array, sample->user_regs.regs, sz); | ||
1704 | array = (void *)array + sz; | ||
1705 | } else { | ||
1706 | *array++ = 0; | ||
1707 | } | ||
1708 | } | ||
1709 | |||
1710 | if (type & PERF_SAMPLE_STACK_USER) { | ||
1711 | sz = sample->user_stack.size; | ||
1712 | *array++ = sz; | ||
1713 | if (sz) { | ||
1714 | memcpy(array, sample->user_stack.data, sz); | ||
1715 | array = (void *)array + sz; | ||
1716 | *array++ = sz; | ||
1717 | } | ||
1718 | } | ||
1719 | |||
1720 | if (type & PERF_SAMPLE_WEIGHT) { | ||
1721 | *array = sample->weight; | ||
1722 | array++; | ||
1723 | } | ||
1724 | |||
1725 | if (type & PERF_SAMPLE_DATA_SRC) { | ||
1726 | *array = sample->data_src; | ||
1727 | array++; | ||
1728 | } | ||
1729 | |||
1428 | return 0; | 1730 | return 0; |
1429 | } | 1731 | } |
1430 | 1732 | ||
@@ -1554,6 +1856,7 @@ static int sample_type__fprintf(FILE *fp, bool *first, u64 value) | |||
1554 | bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU), | 1856 | bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU), |
1555 | bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW), | 1857 | bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW), |
1556 | bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER), | 1858 | bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER), |
1859 | bit_name(IDENTIFIER), | ||
1557 | { .name = NULL, } | 1860 | { .name = NULL, } |
1558 | }; | 1861 | }; |
1559 | #undef bit_name | 1862 | #undef bit_name |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 532a5f925da0..4a7bdc713bab 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -48,6 +48,12 @@ struct perf_sample_id { | |||
48 | * @name - Can be set to retain the original event name passed by the user, | 48 | * @name - Can be set to retain the original event name passed by the user, |
49 | * so that when showing results in tools such as 'perf stat', we | 49 | * so that when showing results in tools such as 'perf stat', we |
50 | * show the name used, not some alias. | 50 | * show the name used, not some alias. |
51 | * @id_pos: the position of the event id (PERF_SAMPLE_ID or | ||
52 | * PERF_SAMPLE_IDENTIFIER) in a sample event i.e. in the array of | ||
53 | * struct sample_event | ||
54 | * @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or | ||
55 | * PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all | ||
56 | * is used there is an id sample appended to non-sample events | ||
51 | */ | 57 | */ |
52 | struct perf_evsel { | 58 | struct perf_evsel { |
53 | struct list_head node; | 59 | struct list_head node; |
@@ -74,6 +80,8 @@ struct perf_evsel { | |||
74 | } handler; | 80 | } handler; |
75 | struct cpu_map *cpus; | 81 | struct cpu_map *cpus; |
76 | unsigned int sample_size; | 82 | unsigned int sample_size; |
83 | int id_pos; | ||
84 | int is_pos; | ||
77 | bool supported; | 85 | bool supported; |
78 | bool needs_swap; | 86 | bool needs_swap; |
79 | /* parse modifier helper */ | 87 | /* parse modifier helper */ |
@@ -104,6 +112,9 @@ void perf_evsel__delete(struct perf_evsel *evsel); | |||
104 | void perf_evsel__config(struct perf_evsel *evsel, | 112 | void perf_evsel__config(struct perf_evsel *evsel, |
105 | struct perf_record_opts *opts); | 113 | struct perf_record_opts *opts); |
106 | 114 | ||
115 | int __perf_evsel__sample_size(u64 sample_type); | ||
116 | void perf_evsel__calc_id_pos(struct perf_evsel *evsel); | ||
117 | |||
107 | bool perf_evsel__is_cache_op_valid(u8 type, u8 op); | 118 | bool perf_evsel__is_cache_op_valid(u8 type, u8 op); |
108 | 119 | ||
109 | #define PERF_EVSEL__MAX_ALIASES 8 | 120 | #define PERF_EVSEL__MAX_ALIASES 8 |
@@ -142,7 +153,8 @@ void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel, | |||
142 | #define perf_evsel__reset_sample_bit(evsel, bit) \ | 153 | #define perf_evsel__reset_sample_bit(evsel, bit) \ |
143 | __perf_evsel__reset_sample_bit(evsel, PERF_SAMPLE_##bit) | 154 | __perf_evsel__reset_sample_bit(evsel, PERF_SAMPLE_##bit) |
144 | 155 | ||
145 | void perf_evsel__set_sample_id(struct perf_evsel *evsel); | 156 | void perf_evsel__set_sample_id(struct perf_evsel *evsel, |
157 | bool use_sample_identifier); | ||
146 | 158 | ||
147 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, | 159 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, |
148 | const char *filter); | 160 | const char *filter); |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 59486c180626..1dca61f0512d 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -32,7 +32,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | |||
32 | return -ENOMEM; | 32 | return -ENOMEM; |
33 | 33 | ||
34 | if (pid != HOST_KERNEL_ID) { | 34 | if (pid != HOST_KERNEL_ID) { |
35 | struct thread *thread = machine__findnew_thread(machine, pid); | 35 | struct thread *thread = machine__findnew_thread(machine, 0, |
36 | pid); | ||
36 | char comm[64]; | 37 | char comm[64]; |
37 | 38 | ||
38 | if (thread == NULL) | 39 | if (thread == NULL) |
@@ -302,9 +303,10 @@ static struct thread *__machine__findnew_thread(struct machine *machine, | |||
302 | return th; | 303 | return th; |
303 | } | 304 | } |
304 | 305 | ||
305 | struct thread *machine__findnew_thread(struct machine *machine, pid_t tid) | 306 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, |
307 | pid_t tid) | ||
306 | { | 308 | { |
307 | return __machine__findnew_thread(machine, 0, tid, true); | 309 | return __machine__findnew_thread(machine, pid, tid, true); |
308 | } | 310 | } |
309 | 311 | ||
310 | struct thread *machine__find_thread(struct machine *machine, pid_t tid) | 312 | struct thread *machine__find_thread(struct machine *machine, pid_t tid) |
@@ -314,7 +316,9 @@ struct thread *machine__find_thread(struct machine *machine, pid_t tid) | |||
314 | 316 | ||
315 | int machine__process_comm_event(struct machine *machine, union perf_event *event) | 317 | int machine__process_comm_event(struct machine *machine, union perf_event *event) |
316 | { | 318 | { |
317 | struct thread *thread = machine__findnew_thread(machine, event->comm.tid); | 319 | struct thread *thread = machine__findnew_thread(machine, |
320 | event->comm.pid, | ||
321 | event->comm.tid); | ||
318 | 322 | ||
319 | if (dump_trace) | 323 | if (dump_trace) |
320 | perf_event__fprintf_comm(event, stdout); | 324 | perf_event__fprintf_comm(event, stdout); |
@@ -1012,7 +1016,8 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event | |||
1012 | return 0; | 1016 | return 0; |
1013 | } | 1017 | } |
1014 | 1018 | ||
1015 | thread = machine__findnew_thread(machine, event->mmap.pid); | 1019 | thread = machine__findnew_thread(machine, event->mmap.pid, |
1020 | event->mmap.pid); | ||
1016 | if (thread == NULL) | 1021 | if (thread == NULL) |
1017 | goto out_problem; | 1022 | goto out_problem; |
1018 | 1023 | ||
@@ -1051,13 +1056,16 @@ static void machine__remove_thread(struct machine *machine, struct thread *th) | |||
1051 | int machine__process_fork_event(struct machine *machine, union perf_event *event) | 1056 | int machine__process_fork_event(struct machine *machine, union perf_event *event) |
1052 | { | 1057 | { |
1053 | struct thread *thread = machine__find_thread(machine, event->fork.tid); | 1058 | struct thread *thread = machine__find_thread(machine, event->fork.tid); |
1054 | struct thread *parent = machine__findnew_thread(machine, event->fork.ptid); | 1059 | struct thread *parent = machine__findnew_thread(machine, |
1060 | event->fork.ppid, | ||
1061 | event->fork.ptid); | ||
1055 | 1062 | ||
1056 | /* if a thread currently exists for the thread id remove it */ | 1063 | /* if a thread currently exists for the thread id remove it */ |
1057 | if (thread != NULL) | 1064 | if (thread != NULL) |
1058 | machine__remove_thread(machine, thread); | 1065 | machine__remove_thread(machine, thread); |
1059 | 1066 | ||
1060 | thread = machine__findnew_thread(machine, event->fork.tid); | 1067 | thread = machine__findnew_thread(machine, event->fork.pid, |
1068 | event->fork.tid); | ||
1061 | if (dump_trace) | 1069 | if (dump_trace) |
1062 | perf_event__fprintf_task(event, stdout); | 1070 | perf_event__fprintf_task(event, stdout); |
1063 | 1071 | ||
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 603ffba999d9..0df925ba6a44 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -106,7 +106,8 @@ static inline bool machine__is_host(struct machine *machine) | |||
106 | return machine ? machine->pid == HOST_KERNEL_ID : false; | 106 | return machine ? machine->pid == HOST_KERNEL_ID : false; |
107 | } | 107 | } |
108 | 108 | ||
109 | struct thread *machine__findnew_thread(struct machine *machine, pid_t tid); | 109 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, |
110 | pid_t tid); | ||
110 | 111 | ||
111 | size_t machine__fprintf(struct machine *machine, FILE *fp); | 112 | size_t machine__fprintf(struct machine *machine, FILE *fp); |
112 | 113 | ||
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c new file mode 100644 index 000000000000..18d73aa2f0f8 --- /dev/null +++ b/tools/perf/util/record.c | |||
@@ -0,0 +1,108 @@ | |||
1 | #include "evlist.h" | ||
2 | #include "evsel.h" | ||
3 | #include "cpumap.h" | ||
4 | #include "parse-events.h" | ||
5 | |||
6 | typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel); | ||
7 | |||
8 | static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str) | ||
9 | { | ||
10 | struct perf_evlist *evlist; | ||
11 | struct perf_evsel *evsel; | ||
12 | int err = -EAGAIN, fd; | ||
13 | |||
14 | evlist = perf_evlist__new(); | ||
15 | if (!evlist) | ||
16 | return -ENOMEM; | ||
17 | |||
18 | if (parse_events(evlist, str)) | ||
19 | goto out_delete; | ||
20 | |||
21 | evsel = perf_evlist__first(evlist); | ||
22 | |||
23 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0); | ||
24 | if (fd < 0) | ||
25 | goto out_delete; | ||
26 | close(fd); | ||
27 | |||
28 | fn(evsel); | ||
29 | |||
30 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0); | ||
31 | if (fd < 0) { | ||
32 | if (errno == EINVAL) | ||
33 | err = -EINVAL; | ||
34 | goto out_delete; | ||
35 | } | ||
36 | close(fd); | ||
37 | err = 0; | ||
38 | |||
39 | out_delete: | ||
40 | perf_evlist__delete(evlist); | ||
41 | return err; | ||
42 | } | ||
43 | |||
44 | static bool perf_probe_api(setup_probe_fn_t fn) | ||
45 | { | ||
46 | const char *try[] = {"cycles:u", "instructions:u", "cpu-clock", NULL}; | ||
47 | struct cpu_map *cpus; | ||
48 | int cpu, ret, i = 0; | ||
49 | |||
50 | cpus = cpu_map__new(NULL); | ||
51 | if (!cpus) | ||
52 | return false; | ||
53 | cpu = cpus->map[0]; | ||
54 | cpu_map__delete(cpus); | ||
55 | |||
56 | do { | ||
57 | ret = perf_do_probe_api(fn, cpu, try[i++]); | ||
58 | if (!ret) | ||
59 | return true; | ||
60 | } while (ret == -EAGAIN && try[i]); | ||
61 | |||
62 | return false; | ||
63 | } | ||
64 | |||
65 | static void perf_probe_sample_identifier(struct perf_evsel *evsel) | ||
66 | { | ||
67 | evsel->attr.sample_type |= PERF_SAMPLE_IDENTIFIER; | ||
68 | } | ||
69 | |||
70 | bool perf_can_sample_identifier(void) | ||
71 | { | ||
72 | return perf_probe_api(perf_probe_sample_identifier); | ||
73 | } | ||
74 | |||
75 | void perf_evlist__config(struct perf_evlist *evlist, | ||
76 | struct perf_record_opts *opts) | ||
77 | { | ||
78 | struct perf_evsel *evsel; | ||
79 | bool use_sample_identifier = false; | ||
80 | |||
81 | /* | ||
82 | * Set the evsel leader links before we configure attributes, | ||
83 | * since some might depend on this info. | ||
84 | */ | ||
85 | if (opts->group) | ||
86 | perf_evlist__set_leader(evlist); | ||
87 | |||
88 | if (evlist->cpus->map[0] < 0) | ||
89 | opts->no_inherit = true; | ||
90 | |||
91 | list_for_each_entry(evsel, &evlist->entries, node) | ||
92 | perf_evsel__config(evsel, opts); | ||
93 | |||
94 | if (evlist->nr_entries > 1) { | ||
95 | struct perf_evsel *first = perf_evlist__first(evlist); | ||
96 | |||
97 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
98 | if (evsel->attr.sample_type == first->attr.sample_type) | ||
99 | continue; | ||
100 | use_sample_identifier = perf_can_sample_identifier(); | ||
101 | break; | ||
102 | } | ||
103 | list_for_each_entry(evsel, &evlist->entries, node) | ||
104 | perf_evsel__set_sample_id(evsel, use_sample_identifier); | ||
105 | } | ||
106 | |||
107 | perf_evlist__set_id_pos(evlist); | ||
108 | } | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index de16a7736859..07642a7b9346 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -739,7 +739,7 @@ static void perf_session__print_tstamp(struct perf_session *session, | |||
739 | union perf_event *event, | 739 | union perf_event *event, |
740 | struct perf_sample *sample) | 740 | struct perf_sample *sample) |
741 | { | 741 | { |
742 | u64 sample_type = perf_evlist__sample_type(session->evlist); | 742 | u64 sample_type = __perf_evlist__combined_sample_type(session->evlist); |
743 | 743 | ||
744 | if (event->header.type != PERF_RECORD_SAMPLE && | 744 | if (event->header.type != PERF_RECORD_SAMPLE && |
745 | !perf_evlist__sample_id_all(session->evlist)) { | 745 | !perf_evlist__sample_id_all(session->evlist)) { |
@@ -840,7 +840,8 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, | |||
840 | 840 | ||
841 | static struct machine * | 841 | static struct machine * |
842 | perf_session__find_machine_for_cpumode(struct perf_session *session, | 842 | perf_session__find_machine_for_cpumode(struct perf_session *session, |
843 | union perf_event *event) | 843 | union perf_event *event, |
844 | struct perf_sample *sample) | ||
844 | { | 845 | { |
845 | const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 846 | const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
846 | 847 | ||
@@ -852,7 +853,7 @@ static struct machine * | |||
852 | if (event->header.type == PERF_RECORD_MMAP) | 853 | if (event->header.type == PERF_RECORD_MMAP) |
853 | pid = event->mmap.pid; | 854 | pid = event->mmap.pid; |
854 | else | 855 | else |
855 | pid = event->ip.pid; | 856 | pid = sample->pid; |
856 | 857 | ||
857 | return perf_session__findnew_machine(session, pid); | 858 | return perf_session__findnew_machine(session, pid); |
858 | } | 859 | } |
@@ -958,7 +959,8 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
958 | hists__inc_nr_events(&evsel->hists, event->header.type); | 959 | hists__inc_nr_events(&evsel->hists, event->header.type); |
959 | } | 960 | } |
960 | 961 | ||
961 | machine = perf_session__find_machine_for_cpumode(session, event); | 962 | machine = perf_session__find_machine_for_cpumode(session, event, |
963 | sample); | ||
962 | 964 | ||
963 | switch (event->header.type) { | 965 | switch (event->header.type) { |
964 | case PERF_RECORD_SAMPLE: | 966 | case PERF_RECORD_SAMPLE: |
@@ -997,22 +999,6 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
997 | } | 999 | } |
998 | } | 1000 | } |
999 | 1001 | ||
1000 | static int perf_session__preprocess_sample(struct perf_session *session, | ||
1001 | union perf_event *event, struct perf_sample *sample) | ||
1002 | { | ||
1003 | if (event->header.type != PERF_RECORD_SAMPLE || | ||
1004 | !(perf_evlist__sample_type(session->evlist) & PERF_SAMPLE_CALLCHAIN)) | ||
1005 | return 0; | ||
1006 | |||
1007 | if (!ip_callchain__valid(sample->callchain, event)) { | ||
1008 | pr_debug("call-chain problem with event, skipping it.\n"); | ||
1009 | ++session->stats.nr_invalid_chains; | ||
1010 | session->stats.total_invalid_chains += sample->period; | ||
1011 | return -EINVAL; | ||
1012 | } | ||
1013 | return 0; | ||
1014 | } | ||
1015 | |||
1016 | static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, | 1002 | static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, |
1017 | struct perf_tool *tool, u64 file_offset) | 1003 | struct perf_tool *tool, u64 file_offset) |
1018 | { | 1004 | { |
@@ -1075,10 +1061,6 @@ static int perf_session__process_event(struct perf_session *session, | |||
1075 | if (ret) | 1061 | if (ret) |
1076 | return ret; | 1062 | return ret; |
1077 | 1063 | ||
1078 | /* Preprocess sample records - precheck callchains */ | ||
1079 | if (perf_session__preprocess_sample(session, event, &sample)) | ||
1080 | return 0; | ||
1081 | |||
1082 | if (tool->ordered_samples) { | 1064 | if (tool->ordered_samples) { |
1083 | ret = perf_session_queue_event(session, event, &sample, | 1065 | ret = perf_session_queue_event(session, event, &sample, |
1084 | file_offset); | 1066 | file_offset); |
@@ -1099,7 +1081,7 @@ void perf_event_header__bswap(struct perf_event_header *self) | |||
1099 | 1081 | ||
1100 | struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) | 1082 | struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) |
1101 | { | 1083 | { |
1102 | return machine__findnew_thread(&session->machines.host, pid); | 1084 | return machine__findnew_thread(&session->machines.host, 0, pid); |
1103 | } | 1085 | } |
1104 | 1086 | ||
1105 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | 1087 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) |