mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-19 18:24:14 +08:00
perf/core improvements and fixes:
. 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> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (GNU/Linux) iQIcBAABAgAGBQJSIOp5AAoJENZQFvNTUqpAV5IQAJmBo4Sj87jRWD4e6FoPMemJ d7F2rgbIHtgnO3xackKxScKiU5xjQTyV/CMLx0ZJOdBxg9T2icMeJFuSbD+XETIE BI1u5PmMAntAOC2v/FEZx5kKLQUZvuxjK4c7t6FXlFEcKER5H8DDBlqYAdLoFETl I9/K4x9+SGyUldwBSXxXeA1SWOYodsLYrhLjMlvL0NLd5qQgocRExS8Lk6oVZu7F UtrtCRpSfD8DONcVfQpKitT3bKIlHBCk4EQAklR7BZbxo9wm3DhtL9l0EVwhbBvz knfF/bZn8B/XaN7w0lQxkEwlTXXIKp0gkAkASAJ3kst7OHJFP41xP/Srz3B0uY5E MgU/E7UYazuusL8MYt9TLmN6xtGLWSe2CE0u/5+FMXzTIXO9v1ik6Ip9//0pj/NF rPojZsCzIqVnwUUDlQTW1WY8S1/l6kS3THLCX6Cz16RKyj5JC0uXKyPiN3vuI/RS bDLYEi2vlSFvi9lg9A9FSJW8+vROdiuovO4LPtGMzjICZhC0PnvRudv8QqPlAV4X eaR7393Qt9k1BMcdUoWEjU/pU9pFFq7SsLns0ktPd7naDeUdo1wb5kuYqbjKrwTg Ciii9/X4hZ1wx4OvYK5m41ayiIdEG+77T7gR+UAfvDK8ni31Aguf52sinYvZOXv+ NVkpsSX2r735Gq5JTKt5 =iYNr -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
ea79ca0de0
@ -134,8 +134,9 @@ enum perf_event_sample_format {
|
||||
PERF_SAMPLE_STACK_USER = 1U << 13,
|
||||
PERF_SAMPLE_WEIGHT = 1U << 14,
|
||||
PERF_SAMPLE_DATA_SRC = 1U << 15,
|
||||
PERF_SAMPLE_IDENTIFIER = 1U << 16,
|
||||
|
||||
PERF_SAMPLE_MAX = 1U << 16, /* non-ABI */
|
||||
PERF_SAMPLE_MAX = 1U << 17, /* non-ABI */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -492,12 +493,12 @@ enum perf_event_type {
|
||||
/*
|
||||
* If perf_event_attr.sample_id_all is set then all event types will
|
||||
* have the sample_type selected fields related to where/when
|
||||
* (identity) an event took place (TID, TIME, ID, CPU, STREAM_ID)
|
||||
* described in PERF_RECORD_SAMPLE below, it will be stashed just after
|
||||
* the perf_event_header and the fields already present for the existing
|
||||
* fields, i.e. at the end of the payload. That way a newer perf.data
|
||||
* file will be supported by older perf tools, with these new optional
|
||||
* fields being ignored.
|
||||
* (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU,
|
||||
* IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed
|
||||
* just after the perf_event_header and the fields already present for
|
||||
* the existing fields, i.e. at the end of the payload. That way a newer
|
||||
* perf.data file will be supported by older perf tools, with these new
|
||||
* optional fields being ignored.
|
||||
*
|
||||
* struct sample_id {
|
||||
* { u32 pid, tid; } && PERF_SAMPLE_TID
|
||||
@ -505,7 +506,12 @@ enum perf_event_type {
|
||||
* { u64 id; } && PERF_SAMPLE_ID
|
||||
* { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
|
||||
* { u32 cpu, res; } && PERF_SAMPLE_CPU
|
||||
* { u64 id; } && PERF_SAMPLE_IDENTIFIER
|
||||
* } && perf_event_attr::sample_id_all
|
||||
*
|
||||
* Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The
|
||||
* advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed
|
||||
* relative to header.size.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -594,6 +600,13 @@ enum perf_event_type {
|
||||
* struct {
|
||||
* struct perf_event_header header;
|
||||
*
|
||||
* #
|
||||
* # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
|
||||
* # The advantage of PERF_SAMPLE_IDENTIFIER is that its position
|
||||
* # is fixed relative to header.
|
||||
* #
|
||||
*
|
||||
* { u64 id; } && PERF_SAMPLE_IDENTIFIER
|
||||
* { u64 ip; } && PERF_SAMPLE_IP
|
||||
* { u32 pid, tid; } && PERF_SAMPLE_TID
|
||||
* { u64 time; } && PERF_SAMPLE_TIME
|
||||
|
@ -1213,6 +1213,9 @@ static void perf_event__id_header_size(struct perf_event *event)
|
||||
if (sample_type & PERF_SAMPLE_TIME)
|
||||
size += sizeof(data->time);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_IDENTIFIER)
|
||||
size += sizeof(data->id);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_ID)
|
||||
size += sizeof(data->id);
|
||||
|
||||
@ -4280,7 +4283,7 @@ static void __perf_event_header__init_id(struct perf_event_header *header,
|
||||
if (sample_type & PERF_SAMPLE_TIME)
|
||||
data->time = perf_clock();
|
||||
|
||||
if (sample_type & PERF_SAMPLE_ID)
|
||||
if (sample_type & (PERF_SAMPLE_ID | PERF_SAMPLE_IDENTIFIER))
|
||||
data->id = primary_event_id(event);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_STREAM_ID)
|
||||
@ -4319,6 +4322,9 @@ static void __perf_event__output_id_sample(struct perf_output_handle *handle,
|
||||
|
||||
if (sample_type & PERF_SAMPLE_CPU)
|
||||
perf_output_put(handle, data->cpu_entry);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_IDENTIFIER)
|
||||
perf_output_put(handle, data->id);
|
||||
}
|
||||
|
||||
void perf_event__output_id_sample(struct perf_event *event,
|
||||
@ -4432,6 +4438,9 @@ void perf_output_sample(struct perf_output_handle *handle,
|
||||
|
||||
perf_output_put(handle, *header);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_IDENTIFIER)
|
||||
perf_output_put(handle, data->id);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_IP)
|
||||
perf_output_put(handle, data->ip);
|
||||
|
||||
|
@ -74,6 +74,10 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
|
||||
--sched:
|
||||
Accrue thread runtime and provide a summary at the end of the session.
|
||||
|
||||
-i
|
||||
--input
|
||||
Process events from a given perf data file.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-script[1]
|
||||
|
@ -360,6 +360,7 @@ LIB_OBJS += $(OUTPUT)util/rblist.o
|
||||
LIB_OBJS += $(OUTPUT)util/intlist.o
|
||||
LIB_OBJS += $(OUTPUT)util/vdso.o
|
||||
LIB_OBJS += $(OUTPUT)util/stat.o
|
||||
LIB_OBJS += $(OUTPUT)util/record.o
|
||||
|
||||
LIB_OBJS += $(OUTPUT)ui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/helpline.o
|
||||
@ -438,6 +439,7 @@ PERFLIBS = $(LIB_FILE) $(LIBLK) $(LIBTRACEEVENT)
|
||||
ifneq ($(OUTPUT),)
|
||||
CFLAGS += -I$(OUTPUT)
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)tests/sample-parsing.o
|
||||
|
||||
ifdef NO_LIBELF
|
||||
EXTLIBS := $(filter-out -lelf,$(EXTLIBS))
|
||||
|
@ -198,7 +198,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool,
|
||||
|
||||
cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
|
||||
thread = machine__findnew_thread(machine, event->ip.pid);
|
||||
thread = machine__findnew_thread(machine, sample->pid, sample->pid);
|
||||
if (thread == NULL) {
|
||||
pr_err("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
@ -206,7 +206,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool,
|
||||
}
|
||||
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
|
||||
event->ip.ip, &al);
|
||||
sample->ip, &al);
|
||||
|
||||
if (al.map != NULL) {
|
||||
if (!al.map->dso->hit) {
|
||||
@ -301,7 +301,9 @@ found:
|
||||
sample_sw.period = sample->period;
|
||||
sample_sw.time = sample->time;
|
||||
perf_event__synthesize_sample(event_sw, evsel->attr.sample_type,
|
||||
&sample_sw, false);
|
||||
evsel->attr.sample_regs_user,
|
||||
evsel->attr.read_format, &sample_sw,
|
||||
false);
|
||||
build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine);
|
||||
return perf_event__repipe(tool, event_sw, &sample_sw, machine);
|
||||
}
|
||||
|
@ -305,7 +305,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
|
||||
struct thread *thread = machine__findnew_thread(machine, sample->pid,
|
||||
sample->pid);
|
||||
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %d event, skipping it.\n",
|
||||
|
@ -815,7 +815,7 @@ static int process_sample_event(struct perf_tool *tool,
|
||||
if (skip_sample(kvm, sample))
|
||||
return 0;
|
||||
|
||||
thread = machine__findnew_thread(machine, sample->tid);
|
||||
thread = machine__findnew_thread(machine, sample->pid, sample->tid);
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
|
@ -805,7 +805,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, sample->tid);
|
||||
struct thread *thread = machine__findnew_thread(machine, sample->pid,
|
||||
sample->tid);
|
||||
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %d event, skipping it.\n",
|
||||
|
@ -94,7 +94,7 @@ dump_raw_samples(struct perf_tool *tool,
|
||||
symbol_conf.field_sep,
|
||||
sample->tid,
|
||||
symbol_conf.field_sep,
|
||||
event->ip.ip,
|
||||
sample->ip,
|
||||
symbol_conf.field_sep,
|
||||
sample->addr,
|
||||
symbol_conf.field_sep,
|
||||
|
@ -365,7 +365,7 @@ static int process_read_event(struct perf_tool *tool,
|
||||
static int perf_report__setup_sample_type(struct perf_report *rep)
|
||||
{
|
||||
struct perf_session *self = rep->session;
|
||||
u64 sample_type = perf_evlist__sample_type(self->evlist);
|
||||
u64 sample_type = perf_evlist__combined_sample_type(self->evlist);
|
||||
|
||||
if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
|
||||
if (sort__has_parent) {
|
||||
|
@ -724,8 +724,10 @@ static int replay_fork_event(struct perf_sched *sched,
|
||||
{
|
||||
struct thread *child, *parent;
|
||||
|
||||
child = machine__findnew_thread(machine, event->fork.tid);
|
||||
parent = machine__findnew_thread(machine, event->fork.ptid);
|
||||
child = machine__findnew_thread(machine, event->fork.pid,
|
||||
event->fork.tid);
|
||||
parent = machine__findnew_thread(machine, event->fork.ppid,
|
||||
event->fork.ptid);
|
||||
|
||||
if (child == NULL || parent == NULL) {
|
||||
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,
|
||||
return -1;
|
||||
}
|
||||
|
||||
sched_out = machine__findnew_thread(machine, prev_pid);
|
||||
sched_in = machine__findnew_thread(machine, next_pid);
|
||||
sched_out = machine__findnew_thread(machine, 0, prev_pid);
|
||||
sched_in = machine__findnew_thread(machine, 0, next_pid);
|
||||
|
||||
out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid);
|
||||
if (!out_events) {
|
||||
@ -978,7 +980,7 @@ static int latency_runtime_event(struct perf_sched *sched,
|
||||
{
|
||||
const u32 pid = perf_evsel__intval(evsel, sample, "pid");
|
||||
const u64 runtime = perf_evsel__intval(evsel, sample, "runtime");
|
||||
struct thread *thread = machine__findnew_thread(machine, pid);
|
||||
struct thread *thread = machine__findnew_thread(machine, 0, pid);
|
||||
struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid);
|
||||
u64 timestamp = sample->time;
|
||||
int cpu = sample->cpu;
|
||||
@ -1016,7 +1018,7 @@ static int latency_wakeup_event(struct perf_sched *sched,
|
||||
if (!success)
|
||||
return 0;
|
||||
|
||||
wakee = machine__findnew_thread(machine, pid);
|
||||
wakee = machine__findnew_thread(machine, 0, pid);
|
||||
atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid);
|
||||
if (!atoms) {
|
||||
if (thread_atoms_insert(sched, wakee))
|
||||
@ -1070,7 +1072,7 @@ static int latency_migrate_task_event(struct perf_sched *sched,
|
||||
if (sched->profile_cpu == -1)
|
||||
return 0;
|
||||
|
||||
migrant = machine__findnew_thread(machine, pid);
|
||||
migrant = machine__findnew_thread(machine, 0, pid);
|
||||
atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);
|
||||
if (!atoms) {
|
||||
if (thread_atoms_insert(sched, migrant))
|
||||
@ -1289,8 +1291,8 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||
return -1;
|
||||
}
|
||||
|
||||
sched_out = machine__findnew_thread(machine, prev_pid);
|
||||
sched_in = machine__findnew_thread(machine, next_pid);
|
||||
sched_out = machine__findnew_thread(machine, 0, prev_pid);
|
||||
sched_in = machine__findnew_thread(machine, 0, next_pid);
|
||||
|
||||
sched->curr_thread[this_cpu] = sched_in;
|
||||
|
||||
|
@ -501,7 +501,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct addr_location al;
|
||||
struct thread *thread = machine__findnew_thread(machine, event->ip.tid);
|
||||
struct thread *thread = machine__findnew_thread(machine, sample->pid,
|
||||
sample->tid);
|
||||
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %d event, skipping it.\n",
|
||||
|
@ -689,7 +689,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
|
||||
{
|
||||
struct perf_top *top = container_of(tool, struct perf_top, tool);
|
||||
struct symbol *parent = NULL;
|
||||
u64 ip = event->ip.ip;
|
||||
u64 ip = sample->ip;
|
||||
struct addr_location al;
|
||||
int err;
|
||||
|
||||
@ -699,10 +699,10 @@ static void perf_event__process_sample(struct perf_tool *tool,
|
||||
if (!seen)
|
||||
seen = intlist__new(NULL);
|
||||
|
||||
if (!intlist__has_entry(seen, event->ip.pid)) {
|
||||
if (!intlist__has_entry(seen, sample->pid)) {
|
||||
pr_err("Can't find guest [%d]'s kernel information\n",
|
||||
event->ip.pid);
|
||||
intlist__add(seen, event->ip.pid);
|
||||
sample->pid);
|
||||
intlist__add(seen, sample->pid);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -836,7 +836,8 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
|
||||
break;
|
||||
case PERF_RECORD_MISC_GUEST_KERNEL:
|
||||
++top->guest_kernel_samples;
|
||||
machine = perf_session__find_machine(session, event->ip.pid);
|
||||
machine = perf_session__find_machine(session,
|
||||
sample.pid);
|
||||
break;
|
||||
case PERF_RECORD_MISC_GUEST_USER:
|
||||
++top->guest_us_samples;
|
||||
|
@ -4,9 +4,11 @@
|
||||
#include "util/debug.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/machine.h"
|
||||
#include "util/session.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/strlist.h"
|
||||
#include "util/intlist.h"
|
||||
#include "util/thread_map.h"
|
||||
|
||||
#include <libaudit.h>
|
||||
@ -69,7 +71,9 @@ static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, unsigned
|
||||
P_MMAP_FLAG(FILE);
|
||||
P_MMAP_FLAG(FIXED);
|
||||
P_MMAP_FLAG(GROWSDOWN);
|
||||
#ifdef MAP_HUGETLB
|
||||
P_MMAP_FLAG(HUGETLB);
|
||||
#endif
|
||||
P_MMAP_FLAG(LOCKED);
|
||||
P_MMAP_FLAG(NONBLOCK);
|
||||
P_MMAP_FLAG(NORESERVE);
|
||||
@ -108,8 +112,12 @@ static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, uns
|
||||
#endif
|
||||
P_MADV_BHV(MERGEABLE);
|
||||
P_MADV_BHV(UNMERGEABLE);
|
||||
#ifdef MADV_HUGEPAGE
|
||||
P_MADV_BHV(HUGEPAGE);
|
||||
#endif
|
||||
#ifdef MADV_NOHUGEPAGE
|
||||
P_MADV_BHV(NOHUGEPAGE);
|
||||
#endif
|
||||
#ifdef MADV_DONTDUMP
|
||||
P_MADV_BHV(DONTDUMP);
|
||||
#endif
|
||||
@ -258,6 +266,8 @@ struct trace {
|
||||
unsigned long nr_events;
|
||||
struct strlist *ev_qualifier;
|
||||
bool not_ev_qualifier;
|
||||
struct intlist *tid_list;
|
||||
struct intlist *pid_list;
|
||||
bool sched;
|
||||
bool multiple_threads;
|
||||
double duration_filter;
|
||||
@ -521,7 +531,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
|
||||
if (sc->filtered)
|
||||
return 0;
|
||||
|
||||
thread = machine__findnew_thread(&trace->host, sample->tid);
|
||||
thread = machine__findnew_thread(&trace->host, sample->pid,
|
||||
sample->tid);
|
||||
ttrace = thread__trace(thread, trace->output);
|
||||
if (ttrace == NULL)
|
||||
return -1;
|
||||
@ -572,7 +583,8 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
|
||||
if (sc->filtered)
|
||||
return 0;
|
||||
|
||||
thread = machine__findnew_thread(&trace->host, sample->tid);
|
||||
thread = machine__findnew_thread(&trace->host, sample->pid,
|
||||
sample->tid);
|
||||
ttrace = thread__trace(thread, trace->output);
|
||||
if (ttrace == NULL)
|
||||
return -1;
|
||||
@ -628,7 +640,9 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs
|
||||
{
|
||||
u64 runtime = perf_evsel__intval(evsel, sample, "runtime");
|
||||
double runtime_ms = (double)runtime / NSEC_PER_MSEC;
|
||||
struct thread *thread = machine__findnew_thread(&trace->host, sample->tid);
|
||||
struct thread *thread = machine__findnew_thread(&trace->host,
|
||||
sample->pid,
|
||||
sample->tid);
|
||||
struct thread_trace *ttrace = thread__trace(thread, trace->output);
|
||||
|
||||
if (ttrace == NULL)
|
||||
@ -648,6 +662,72 @@ out_dump:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool skip_sample(struct trace *trace, struct perf_sample *sample)
|
||||
{
|
||||
if ((trace->pid_list && intlist__find(trace->pid_list, sample->pid)) ||
|
||||
(trace->tid_list && intlist__find(trace->tid_list, sample->tid)))
|
||||
return false;
|
||||
|
||||
if (trace->pid_list || trace->tid_list)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int trace__process_sample(struct perf_tool *tool,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine __maybe_unused)
|
||||
{
|
||||
struct trace *trace = container_of(tool, struct trace, tool);
|
||||
int err = 0;
|
||||
|
||||
tracepoint_handler handler = evsel->handler.func;
|
||||
|
||||
if (skip_sample(trace, sample))
|
||||
return 0;
|
||||
|
||||
if (trace->base_time == 0)
|
||||
trace->base_time = sample->time;
|
||||
|
||||
if (handler)
|
||||
handler(trace, evsel, sample);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool
|
||||
perf_session__has_tp(struct perf_session *session, const char *name)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
evsel = perf_evlist__find_tracepoint_by_name(session->evlist, name);
|
||||
|
||||
return evsel != NULL;
|
||||
}
|
||||
|
||||
static int parse_target_str(struct trace *trace)
|
||||
{
|
||||
if (trace->opts.target.pid) {
|
||||
trace->pid_list = intlist__new(trace->opts.target.pid);
|
||||
if (trace->pid_list == NULL) {
|
||||
pr_err("Error parsing process id string\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (trace->opts.target.tid) {
|
||||
trace->tid_list = intlist__new(trace->opts.target.tid);
|
||||
if (trace->tid_list == NULL) {
|
||||
pr_err("Error parsing thread id string\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trace__run(struct trace *trace, int argc, const char **argv)
|
||||
{
|
||||
struct perf_evlist *evlist = perf_evlist__new();
|
||||
@ -787,6 +867,69 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int trace__replay(struct trace *trace)
|
||||
{
|
||||
const struct perf_evsel_str_handler handlers[] = {
|
||||
{ "raw_syscalls:sys_enter", trace__sys_enter, },
|
||||
{ "raw_syscalls:sys_exit", trace__sys_exit, },
|
||||
};
|
||||
|
||||
struct perf_session *session;
|
||||
int err = -1;
|
||||
|
||||
trace->tool.sample = trace__process_sample;
|
||||
trace->tool.mmap = perf_event__process_mmap;
|
||||
trace->tool.comm = perf_event__process_comm;
|
||||
trace->tool.exit = perf_event__process_exit;
|
||||
trace->tool.fork = perf_event__process_fork;
|
||||
trace->tool.attr = perf_event__process_attr;
|
||||
trace->tool.tracing_data = perf_event__process_tracing_data;
|
||||
trace->tool.build_id = perf_event__process_build_id;
|
||||
|
||||
trace->tool.ordered_samples = true;
|
||||
trace->tool.ordering_requires_timestamps = true;
|
||||
|
||||
/* add tid to output */
|
||||
trace->multiple_threads = true;
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false,
|
||||
&trace->tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
err = perf_session__set_tracepoints_handlers(session, handlers);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (!perf_session__has_tp(session, "raw_syscalls:sys_enter")) {
|
||||
pr_err("Data file does not have raw_syscalls:sys_enter events\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!perf_session__has_tp(session, "raw_syscalls:sys_exit")) {
|
||||
pr_err("Data file does not have raw_syscalls:sys_exit events\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = parse_target_str(trace);
|
||||
if (err != 0)
|
||||
goto out;
|
||||
|
||||
setup_pager();
|
||||
|
||||
err = perf_session__process_events(session, &trace->tool);
|
||||
if (err)
|
||||
pr_err("Failed to process events, error %d", err);
|
||||
|
||||
out:
|
||||
perf_session__delete(session);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static size_t trace__fprintf_threads_header(FILE *fp)
|
||||
{
|
||||
size_t printed;
|
||||
@ -888,6 +1031,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
OPT_STRING('e', "expr", &ev_qualifier_str, "expr",
|
||||
"list of events to trace"),
|
||||
OPT_STRING('o', "output", &output_name, "file", "output file name"),
|
||||
OPT_STRING('i', "input", &input_name, "file", "Analyze events in file"),
|
||||
OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",
|
||||
"trace events on existing process id"),
|
||||
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)
|
||||
"system-wide collection from all CPUs"),
|
||||
OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu",
|
||||
"list of cpus to monitor"),
|
||||
OPT_BOOLEAN('i', "no-inherit", &trace.opts.no_inherit,
|
||||
OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit,
|
||||
"child tasks do not inherit counters"),
|
||||
OPT_UINTEGER('m', "mmap-pages", &trace.opts.mmap_pages,
|
||||
"number of mmap data pages"),
|
||||
@ -954,7 +1098,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (!argc && perf_target__none(&trace.opts.target))
|
||||
trace.opts.target.system_wide = true;
|
||||
|
||||
err = trace__run(&trace, argc, argv);
|
||||
if (input_name)
|
||||
err = trace__replay(&trace);
|
||||
else
|
||||
err = trace__run(&trace, argc, argv);
|
||||
|
||||
if (trace.sched && !err)
|
||||
trace__fprintf_thread_summary(&trace, trace.output);
|
||||
|
@ -103,6 +103,10 @@ static struct test {
|
||||
.desc = "Test object code reading",
|
||||
.func = test__code_reading,
|
||||
},
|
||||
{
|
||||
.desc = "Test sample parsing",
|
||||
.func = test__sample_parsing,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
@ -257,7 +257,7 @@ static int process_sample_event(struct machine *machine,
|
||||
return -1;
|
||||
}
|
||||
|
||||
thread = machine__findnew_thread(machine, sample.pid);
|
||||
thread = machine__findnew_thread(machine, sample.pid, sample.pid);
|
||||
if (!thread) {
|
||||
pr_debug("machine__findnew_thread failed\n");
|
||||
return -1;
|
||||
@ -446,7 +446,7 @@ static int do_test_code_reading(bool try_kcore)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
thread = machine__findnew_thread(machine, pid);
|
||||
thread = machine__findnew_thread(machine, pid, pid);
|
||||
if (!thread) {
|
||||
pr_debug("machine__findnew_thread failed\n");
|
||||
goto out_err;
|
||||
|
@ -88,7 +88,8 @@ static struct machine *setup_fake_machine(struct machines *machines)
|
||||
for (i = 0; i < ARRAY_SIZE(fake_threads); i++) {
|
||||
struct thread *thread;
|
||||
|
||||
thread = machine__findnew_thread(machine, fake_threads[i].pid);
|
||||
thread = machine__findnew_thread(machine, fake_threads[i].pid,
|
||||
fake_threads[i].pid);
|
||||
if (thread == NULL)
|
||||
goto out;
|
||||
|
||||
@ -210,15 +211,13 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) {
|
||||
const union perf_event event = {
|
||||
.ip = {
|
||||
.header = {
|
||||
.misc = PERF_RECORD_MISC_USER,
|
||||
},
|
||||
.pid = fake_common_samples[k].pid,
|
||||
.ip = fake_common_samples[k].ip,
|
||||
.header = {
|
||||
.misc = PERF_RECORD_MISC_USER,
|
||||
},
|
||||
};
|
||||
|
||||
sample.pid = fake_common_samples[k].pid;
|
||||
sample.ip = fake_common_samples[k].ip;
|
||||
if (perf_event__preprocess_sample(&event, machine, &al,
|
||||
&sample) < 0)
|
||||
goto out;
|
||||
@ -234,15 +233,13 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
|
||||
|
||||
for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) {
|
||||
const union perf_event event = {
|
||||
.ip = {
|
||||
.header = {
|
||||
.misc = PERF_RECORD_MISC_USER,
|
||||
},
|
||||
.pid = fake_samples[i][k].pid,
|
||||
.ip = fake_samples[i][k].ip,
|
||||
.header = {
|
||||
.misc = PERF_RECORD_MISC_USER,
|
||||
},
|
||||
};
|
||||
|
||||
sample.pid = fake_samples[i][k].pid;
|
||||
sample.ip = fake_samples[i][k].ip;
|
||||
if (perf_event__preprocess_sample(&event, machine, &al,
|
||||
&sample) < 0)
|
||||
goto out;
|
||||
|
@ -72,7 +72,7 @@ int test__basic_mmap(void)
|
||||
}
|
||||
|
||||
evsels[i]->attr.wakeup_events = 1;
|
||||
perf_evsel__set_sample_id(evsels[i]);
|
||||
perf_evsel__set_sample_id(evsels[i], false);
|
||||
|
||||
perf_evlist__add(evlist, evsels[i]);
|
||||
|
||||
|
316
tools/perf/tests/sample-parsing.c
Normal file
316
tools/perf/tests/sample-parsing.c
Normal file
@ -0,0 +1,316 @@
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "event.h"
|
||||
#include "evsel.h"
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
#define COMP(m) do { \
|
||||
if (s1->m != s2->m) { \
|
||||
pr_debug("Samples differ at '"#m"'\n"); \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define MCOMP(m) do { \
|
||||
if (memcmp(&s1->m, &s2->m, sizeof(s1->m))) { \
|
||||
pr_debug("Samples differ at '"#m"'\n"); \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static bool samples_same(const struct perf_sample *s1,
|
||||
const struct perf_sample *s2, u64 type, u64 regs_user,
|
||||
u64 read_format)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (type & PERF_SAMPLE_IDENTIFIER)
|
||||
COMP(id);
|
||||
|
||||
if (type & PERF_SAMPLE_IP)
|
||||
COMP(ip);
|
||||
|
||||
if (type & PERF_SAMPLE_TID) {
|
||||
COMP(pid);
|
||||
COMP(tid);
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_TIME)
|
||||
COMP(time);
|
||||
|
||||
if (type & PERF_SAMPLE_ADDR)
|
||||
COMP(addr);
|
||||
|
||||
if (type & PERF_SAMPLE_ID)
|
||||
COMP(id);
|
||||
|
||||
if (type & PERF_SAMPLE_STREAM_ID)
|
||||
COMP(stream_id);
|
||||
|
||||
if (type & PERF_SAMPLE_CPU)
|
||||
COMP(cpu);
|
||||
|
||||
if (type & PERF_SAMPLE_PERIOD)
|
||||
COMP(period);
|
||||
|
||||
if (type & PERF_SAMPLE_READ) {
|
||||
if (read_format & PERF_FORMAT_GROUP)
|
||||
COMP(read.group.nr);
|
||||
else
|
||||
COMP(read.one.value);
|
||||
if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
|
||||
COMP(read.time_enabled);
|
||||
if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
|
||||
COMP(read.time_running);
|
||||
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
|
||||
if (read_format & PERF_FORMAT_GROUP) {
|
||||
for (i = 0; i < s1->read.group.nr; i++)
|
||||
MCOMP(read.group.values[i]);
|
||||
} else {
|
||||
COMP(read.one.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_CALLCHAIN) {
|
||||
COMP(callchain->nr);
|
||||
for (i = 0; i < s1->callchain->nr; i++)
|
||||
COMP(callchain->ips[i]);
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_RAW) {
|
||||
COMP(raw_size);
|
||||
if (memcmp(s1->raw_data, s2->raw_data, s1->raw_size)) {
|
||||
pr_debug("Samples differ at 'raw_data'\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_BRANCH_STACK) {
|
||||
COMP(branch_stack->nr);
|
||||
for (i = 0; i < s1->branch_stack->nr; i++)
|
||||
MCOMP(branch_stack->entries[i]);
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_REGS_USER) {
|
||||
size_t sz = hweight_long(regs_user) * sizeof(u64);
|
||||
|
||||
COMP(user_regs.abi);
|
||||
if (s1->user_regs.abi &&
|
||||
(!s1->user_regs.regs || !s2->user_regs.regs ||
|
||||
memcmp(s1->user_regs.regs, s2->user_regs.regs, sz))) {
|
||||
pr_debug("Samples differ at 'user_regs'\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_STACK_USER) {
|
||||
COMP(user_stack.size);
|
||||
if (memcmp(s1->user_stack.data, s1->user_stack.data,
|
||||
s1->user_stack.size)) {
|
||||
pr_debug("Samples differ at 'user_stack'\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_WEIGHT)
|
||||
COMP(weight);
|
||||
|
||||
if (type & PERF_SAMPLE_DATA_SRC)
|
||||
COMP(data_src);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
|
||||
{
|
||||
struct perf_evsel evsel = {
|
||||
.needs_swap = false,
|
||||
.attr = {
|
||||
.sample_type = sample_type,
|
||||
.sample_regs_user = sample_regs_user,
|
||||
.read_format = read_format,
|
||||
},
|
||||
};
|
||||
union perf_event *event;
|
||||
union {
|
||||
struct ip_callchain callchain;
|
||||
u64 data[64];
|
||||
} callchain = {
|
||||
/* 3 ips */
|
||||
.data = {3, 201, 202, 203},
|
||||
};
|
||||
union {
|
||||
struct branch_stack branch_stack;
|
||||
u64 data[64];
|
||||
} branch_stack = {
|
||||
/* 1 branch_entry */
|
||||
.data = {1, 211, 212, 213},
|
||||
};
|
||||
u64 user_regs[64];
|
||||
const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL};
|
||||
const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL};
|
||||
struct perf_sample sample = {
|
||||
.ip = 101,
|
||||
.pid = 102,
|
||||
.tid = 103,
|
||||
.time = 104,
|
||||
.addr = 105,
|
||||
.id = 106,
|
||||
.stream_id = 107,
|
||||
.period = 108,
|
||||
.weight = 109,
|
||||
.cpu = 110,
|
||||
.raw_size = sizeof(raw_data),
|
||||
.data_src = 111,
|
||||
.raw_data = (void *)raw_data,
|
||||
.callchain = &callchain.callchain,
|
||||
.branch_stack = &branch_stack.branch_stack,
|
||||
.user_regs = {
|
||||
.abi = PERF_SAMPLE_REGS_ABI_64,
|
||||
.regs = user_regs,
|
||||
},
|
||||
.user_stack = {
|
||||
.size = sizeof(data),
|
||||
.data = (void *)data,
|
||||
},
|
||||
.read = {
|
||||
.time_enabled = 0x030a59d664fca7deULL,
|
||||
.time_running = 0x011b6ae553eb98edULL,
|
||||
},
|
||||
};
|
||||
struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},};
|
||||
struct perf_sample sample_out;
|
||||
size_t i, sz, bufsz;
|
||||
int err, ret = -1;
|
||||
|
||||
for (i = 0; i < sizeof(user_regs); i++)
|
||||
*(i + (u8 *)user_regs) = i & 0xfe;
|
||||
|
||||
if (read_format & PERF_FORMAT_GROUP) {
|
||||
sample.read.group.nr = 4;
|
||||
sample.read.group.values = values;
|
||||
} else {
|
||||
sample.read.one.value = 0x08789faeb786aa87ULL;
|
||||
sample.read.one.id = 99;
|
||||
}
|
||||
|
||||
sz = perf_event__sample_event_size(&sample, sample_type,
|
||||
sample_regs_user, read_format);
|
||||
bufsz = sz + 4096; /* Add a bit for overrun checking */
|
||||
event = malloc(bufsz);
|
||||
if (!event) {
|
||||
pr_debug("malloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(event, 0xff, bufsz);
|
||||
event->header.type = PERF_RECORD_SAMPLE;
|
||||
event->header.misc = 0;
|
||||
event->header.size = sz;
|
||||
|
||||
err = perf_event__synthesize_sample(event, sample_type,
|
||||
sample_regs_user, read_format,
|
||||
&sample, false);
|
||||
if (err) {
|
||||
pr_debug("%s failed for sample_type %#"PRIx64", error %d\n",
|
||||
"perf_event__synthesize_sample", sample_type, err);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* The data does not contain 0xff so we use that to check the size */
|
||||
for (i = bufsz; i > 0; i--) {
|
||||
if (*(i - 1 + (u8 *)event) != 0xff)
|
||||
break;
|
||||
}
|
||||
if (i != sz) {
|
||||
pr_debug("Event size mismatch: actual %zu vs expected %zu\n",
|
||||
i, sz);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
evsel.sample_size = __perf_evsel__sample_size(sample_type);
|
||||
|
||||
err = perf_evsel__parse_sample(&evsel, event, &sample_out);
|
||||
if (err) {
|
||||
pr_debug("%s failed for sample_type %#"PRIx64", error %d\n",
|
||||
"perf_evsel__parse_sample", sample_type, err);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (!samples_same(&sample, &sample_out, sample_type,
|
||||
sample_regs_user, read_format)) {
|
||||
pr_debug("parsing failed for sample_type %#"PRIx64"\n",
|
||||
sample_type);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out_free:
|
||||
free(event);
|
||||
if (ret && read_format)
|
||||
pr_debug("read_format %#"PRIx64"\n", read_format);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* test__sample_parsing - test sample parsing.
|
||||
*
|
||||
* This function implements a test that synthesizes a sample event, parses it
|
||||
* and then checks that the parsed sample matches the original sample. The test
|
||||
* checks sample format bits separately and together. If the test passes %0 is
|
||||
* returned, otherwise %-1 is returned.
|
||||
*/
|
||||
int test__sample_parsing(void)
|
||||
{
|
||||
const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15};
|
||||
u64 sample_type;
|
||||
u64 sample_regs_user;
|
||||
size_t i;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Fail the test if it has not been updated when new sample format bits
|
||||
* were added.
|
||||
*/
|
||||
if (PERF_SAMPLE_MAX > PERF_SAMPLE_IDENTIFIER << 1) {
|
||||
pr_debug("sample format has changed - test needs updating\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Test each sample format bit separately */
|
||||
for (sample_type = 1; sample_type != PERF_SAMPLE_MAX;
|
||||
sample_type <<= 1) {
|
||||
/* Test read_format variations */
|
||||
if (sample_type == PERF_SAMPLE_READ) {
|
||||
for (i = 0; i < ARRAY_SIZE(rf); i++) {
|
||||
err = do_test(sample_type, 0, rf[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sample_type == PERF_SAMPLE_REGS_USER)
|
||||
sample_regs_user = 0x3fff;
|
||||
else
|
||||
sample_regs_user = 0;
|
||||
|
||||
err = do_test(sample_type, sample_regs_user, 0);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Test all sample format bits together */
|
||||
sample_type = PERF_SAMPLE_MAX - 1;
|
||||
sample_regs_user = 0x3fff;
|
||||
for (i = 0; i < ARRAY_SIZE(rf); i++) {
|
||||
err = do_test(sample_type, sample_regs_user, rf[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -37,5 +37,6 @@ int test__task_exit(void);
|
||||
int test__sw_clock_freq(void);
|
||||
int test__perf_time_to_tsc(void);
|
||||
int test__code_reading(void);
|
||||
int test__sample_parsing(void);
|
||||
|
||||
#endif /* TESTS_H */
|
||||
|
@ -18,13 +18,14 @@
|
||||
|
||||
int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel __maybe_unused,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct addr_location al;
|
||||
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
|
||||
struct thread *thread = machine__findnew_thread(machine, sample->pid,
|
||||
sample->pid);
|
||||
|
||||
if (thread == NULL) {
|
||||
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,
|
||||
}
|
||||
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
|
||||
event->ip.ip, &al);
|
||||
sample->ip, &al);
|
||||
|
||||
if (al.map != NULL)
|
||||
al.map->dso->hit = 1;
|
||||
@ -47,7 +48,9 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused,
|
||||
__maybe_unused,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, event->fork.tid);
|
||||
struct thread *thread = machine__findnew_thread(machine,
|
||||
event->fork.pid,
|
||||
event->fork.tid);
|
||||
|
||||
dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid,
|
||||
event->fork.ppid, event->fork.ptid);
|
||||
|
@ -21,14 +21,6 @@
|
||||
|
||||
__thread struct callchain_cursor callchain_cursor;
|
||||
|
||||
bool ip_callchain__valid(struct ip_callchain *chain,
|
||||
const union perf_event *event)
|
||||
{
|
||||
unsigned int chain_size = event->header.size;
|
||||
chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
|
||||
return chain->nr * sizeof(u64) <= chain_size;
|
||||
}
|
||||
|
||||
#define chain_for_each_child(child, parent) \
|
||||
list_for_each_entry(child, &parent->children, siblings)
|
||||
|
||||
|
@ -109,11 +109,6 @@ int callchain_append(struct callchain_root *root,
|
||||
int callchain_merge(struct callchain_cursor *cursor,
|
||||
struct callchain_root *dst, struct callchain_root *src);
|
||||
|
||||
struct ip_callchain;
|
||||
union perf_event;
|
||||
|
||||
bool ip_callchain__valid(struct ip_callchain *chain,
|
||||
const union perf_event *event);
|
||||
/*
|
||||
* Initialize a cursor before adding entries inside, but keep
|
||||
* the previously allocated entries as a cache.
|
||||
|
@ -686,7 +686,8 @@ int perf_event__preprocess_sample(const union perf_event *event,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
|
||||
struct thread *thread = machine__findnew_thread(machine, sample->pid,
|
||||
sample->pid);
|
||||
|
||||
if (thread == NULL)
|
||||
return -1;
|
||||
@ -708,7 +709,7 @@ int perf_event__preprocess_sample(const union perf_event *event,
|
||||
machine__create_kernel_maps(machine);
|
||||
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
|
||||
event->ip.ip, al);
|
||||
sample->ip, al);
|
||||
dump_printf(" ...... dso: %s\n",
|
||||
al->map ? al->map->dso->long_name :
|
||||
al->level == 'H' ? "[hypervisor]" : "<not found>");
|
||||
|
@ -8,16 +8,6 @@
|
||||
#include "map.h"
|
||||
#include "build-id.h"
|
||||
|
||||
/*
|
||||
* PERF_SAMPLE_IP | PERF_SAMPLE_TID | *
|
||||
*/
|
||||
struct ip_event {
|
||||
struct perf_event_header header;
|
||||
u64 ip;
|
||||
u32 pid, tid;
|
||||
unsigned char __more_data[];
|
||||
};
|
||||
|
||||
struct mmap_event {
|
||||
struct perf_event_header header;
|
||||
u32 pid, tid;
|
||||
@ -63,7 +53,8 @@ struct read_event {
|
||||
(PERF_SAMPLE_IP | PERF_SAMPLE_TID | \
|
||||
PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | \
|
||||
PERF_SAMPLE_ID | PERF_SAMPLE_STREAM_ID | \
|
||||
PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD)
|
||||
PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | \
|
||||
PERF_SAMPLE_IDENTIFIER)
|
||||
|
||||
struct sample_event {
|
||||
struct perf_event_header header;
|
||||
@ -71,6 +62,7 @@ struct sample_event {
|
||||
};
|
||||
|
||||
struct regs_dump {
|
||||
u64 abi;
|
||||
u64 *regs;
|
||||
};
|
||||
|
||||
@ -166,7 +158,6 @@ struct tracing_data_event {
|
||||
|
||||
union perf_event {
|
||||
struct perf_event_header header;
|
||||
struct ip_event ip;
|
||||
struct mmap_event mmap;
|
||||
struct comm_event comm;
|
||||
struct fork_event fork;
|
||||
@ -238,7 +229,10 @@ int perf_event__preprocess_sample(const union perf_event *self,
|
||||
|
||||
const char *perf_event__name(unsigned int id);
|
||||
|
||||
size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
|
||||
u64 sample_regs_user, u64 read_format);
|
||||
int perf_event__synthesize_sample(union perf_event *event, u64 type,
|
||||
u64 sample_regs_user, u64 read_format,
|
||||
const struct perf_sample *sample,
|
||||
bool swapped);
|
||||
|
||||
|
@ -49,26 +49,19 @@ struct perf_evlist *perf_evlist__new(void)
|
||||
return evlist;
|
||||
}
|
||||
|
||||
void perf_evlist__config(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts)
|
||||
/**
|
||||
* perf_evlist__set_id_pos - set the positions of event ids.
|
||||
* @evlist: selected event list
|
||||
*
|
||||
* Events with compatible sample types all have the same id_pos
|
||||
* and is_pos. For convenience, put a copy on evlist.
|
||||
*/
|
||||
void perf_evlist__set_id_pos(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
/*
|
||||
* Set the evsel leader links before we configure attributes,
|
||||
* since some might depend on this info.
|
||||
*/
|
||||
if (opts->group)
|
||||
perf_evlist__set_leader(evlist);
|
||||
struct perf_evsel *first = perf_evlist__first(evlist);
|
||||
|
||||
if (evlist->cpus->map[0] < 0)
|
||||
opts->no_inherit = true;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
perf_evsel__config(evsel, opts);
|
||||
|
||||
if (evlist->nr_entries > 1)
|
||||
perf_evsel__set_sample_id(evsel);
|
||||
}
|
||||
evlist->id_pos = first->id_pos;
|
||||
evlist->is_pos = first->is_pos;
|
||||
}
|
||||
|
||||
static void perf_evlist__purge(struct perf_evlist *evlist)
|
||||
@ -101,15 +94,20 @@ void perf_evlist__delete(struct perf_evlist *evlist)
|
||||
void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
|
||||
{
|
||||
list_add_tail(&entry->node, &evlist->entries);
|
||||
++evlist->nr_entries;
|
||||
if (!evlist->nr_entries++)
|
||||
perf_evlist__set_id_pos(evlist);
|
||||
}
|
||||
|
||||
void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
|
||||
struct list_head *list,
|
||||
int nr_entries)
|
||||
{
|
||||
bool set_id_pos = !evlist->nr_entries;
|
||||
|
||||
list_splice_tail(list, &evlist->entries);
|
||||
evlist->nr_entries += nr_entries;
|
||||
if (set_id_pos)
|
||||
perf_evlist__set_id_pos(evlist);
|
||||
}
|
||||
|
||||
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)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct perf_evsel *
|
||||
perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,
|
||||
const char *name)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
if ((evsel->attr.type == PERF_TYPE_TRACEPOINT) &&
|
||||
(strcmp(evsel->name, name) == 0))
|
||||
return evsel;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int perf_evlist__add_newtp(struct perf_evlist *evlist,
|
||||
const char *sys, const char *name, void *handler)
|
||||
{
|
||||
@ -371,6 +384,55 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int perf_evlist__event2id(struct perf_evlist *evlist,
|
||||
union perf_event *event, u64 *id)
|
||||
{
|
||||
const u64 *array = event->sample.array;
|
||||
ssize_t n;
|
||||
|
||||
n = (event->header.size - sizeof(event->header)) >> 3;
|
||||
|
||||
if (event->header.type == PERF_RECORD_SAMPLE) {
|
||||
if (evlist->id_pos >= n)
|
||||
return -1;
|
||||
*id = array[evlist->id_pos];
|
||||
} else {
|
||||
if (evlist->is_pos > n)
|
||||
return -1;
|
||||
n -= evlist->is_pos;
|
||||
*id = array[n];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist,
|
||||
union perf_event *event)
|
||||
{
|
||||
struct hlist_head *head;
|
||||
struct perf_sample_id *sid;
|
||||
int hash;
|
||||
u64 id;
|
||||
|
||||
if (evlist->nr_entries == 1)
|
||||
return perf_evlist__first(evlist);
|
||||
|
||||
if (perf_evlist__event2id(evlist, event, &id))
|
||||
return NULL;
|
||||
|
||||
/* Synthesized events have an id of zero */
|
||||
if (!id)
|
||||
return perf_evlist__first(evlist);
|
||||
|
||||
hash = hash_64(id, PERF_EVLIST__HLIST_BITS);
|
||||
head = &evlist->heads[hash];
|
||||
|
||||
hlist_for_each_entry(sid, head, node) {
|
||||
if (sid->id == id)
|
||||
return sid->evsel;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
|
||||
{
|
||||
struct perf_mmap *md = &evlist->mmap[idx];
|
||||
@ -681,20 +743,40 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter)
|
||||
|
||||
bool perf_evlist__valid_sample_type(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *first = perf_evlist__first(evlist), *pos = first;
|
||||
struct perf_evsel *pos;
|
||||
|
||||
list_for_each_entry_continue(pos, &evlist->entries, node) {
|
||||
if (first->attr.sample_type != pos->attr.sample_type)
|
||||
if (evlist->nr_entries == 1)
|
||||
return true;
|
||||
|
||||
if (evlist->id_pos < 0 || evlist->is_pos < 0)
|
||||
return false;
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
if (pos->id_pos != evlist->id_pos ||
|
||||
pos->is_pos != evlist->is_pos)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 perf_evlist__sample_type(struct perf_evlist *evlist)
|
||||
u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *first = perf_evlist__first(evlist);
|
||||
return first->attr.sample_type;
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
if (evlist->combined_sample_type)
|
||||
return evlist->combined_sample_type;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node)
|
||||
evlist->combined_sample_type |= evsel->attr.sample_type;
|
||||
|
||||
return evlist->combined_sample_type;
|
||||
}
|
||||
|
||||
u64 perf_evlist__combined_sample_type(struct perf_evlist *evlist)
|
||||
{
|
||||
evlist->combined_sample_type = 0;
|
||||
return __perf_evlist__combined_sample_type(evlist);
|
||||
}
|
||||
|
||||
bool perf_evlist__valid_read_format(struct perf_evlist *evlist)
|
||||
@ -749,6 +831,9 @@ u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist)
|
||||
|
||||
if (sample_type & PERF_SAMPLE_CPU)
|
||||
size += sizeof(data->cpu) * 2;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_IDENTIFIER)
|
||||
size += sizeof(data->id);
|
||||
out:
|
||||
return size;
|
||||
}
|
||||
@ -907,7 +992,10 @@ int perf_evlist__start_workload(struct perf_evlist *evlist)
|
||||
int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__first(evlist);
|
||||
struct perf_evsel *evsel = perf_evlist__event2evsel(evlist, event);
|
||||
|
||||
if (!evsel)
|
||||
return -EFAULT;
|
||||
return perf_evsel__parse_sample(evsel, event, sample);
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,9 @@ struct perf_evlist {
|
||||
int nr_fds;
|
||||
int nr_mmaps;
|
||||
int mmap_len;
|
||||
int id_pos;
|
||||
int is_pos;
|
||||
u64 combined_sample_type;
|
||||
struct {
|
||||
int cork_fd;
|
||||
pid_t pid;
|
||||
@ -71,6 +74,10 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter);
|
||||
struct perf_evsel *
|
||||
perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id);
|
||||
|
||||
struct perf_evsel *
|
||||
perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,
|
||||
const char *name);
|
||||
|
||||
void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel,
|
||||
int cpu, int thread, u64 id);
|
||||
|
||||
@ -85,6 +92,8 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx);
|
||||
int perf_evlist__open(struct perf_evlist *evlist);
|
||||
void perf_evlist__close(struct perf_evlist *evlist);
|
||||
|
||||
void perf_evlist__set_id_pos(struct perf_evlist *evlist);
|
||||
bool perf_can_sample_identifier(void);
|
||||
void perf_evlist__config(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts);
|
||||
|
||||
@ -121,7 +130,8 @@ void __perf_evlist__set_leader(struct list_head *list);
|
||||
void perf_evlist__set_leader(struct perf_evlist *evlist);
|
||||
|
||||
u64 perf_evlist__read_format(struct perf_evlist *evlist);
|
||||
u64 perf_evlist__sample_type(struct perf_evlist *evlist);
|
||||
u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist);
|
||||
u64 perf_evlist__combined_sample_type(struct perf_evlist *evlist);
|
||||
bool perf_evlist__sample_id_all(struct perf_evlist *evlist);
|
||||
u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist);
|
||||
|
||||
|
@ -31,7 +31,7 @@ static struct {
|
||||
|
||||
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
|
||||
|
||||
static int __perf_evsel__sample_size(u64 sample_type)
|
||||
int __perf_evsel__sample_size(u64 sample_type)
|
||||
{
|
||||
u64 mask = sample_type & PERF_SAMPLE_MASK;
|
||||
int size = 0;
|
||||
@ -47,6 +47,72 @@ static int __perf_evsel__sample_size(u64 sample_type)
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* __perf_evsel__calc_id_pos - calculate id_pos.
|
||||
* @sample_type: sample type
|
||||
*
|
||||
* This function returns the position of the event id (PERF_SAMPLE_ID or
|
||||
* PERF_SAMPLE_IDENTIFIER) in a sample event i.e. in the array of struct
|
||||
* sample_event.
|
||||
*/
|
||||
static int __perf_evsel__calc_id_pos(u64 sample_type)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_IDENTIFIER)
|
||||
return 0;
|
||||
|
||||
if (!(sample_type & PERF_SAMPLE_ID))
|
||||
return -1;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_IP)
|
||||
idx += 1;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_TID)
|
||||
idx += 1;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_TIME)
|
||||
idx += 1;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_ADDR)
|
||||
idx += 1;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* __perf_evsel__calc_is_pos - calculate is_pos.
|
||||
* @sample_type: sample type
|
||||
*
|
||||
* This function returns the position (counting backwards) of the event id
|
||||
* (PERF_SAMPLE_ID or PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if
|
||||
* sample_id_all is used there is an id sample appended to non-sample events.
|
||||
*/
|
||||
static int __perf_evsel__calc_is_pos(u64 sample_type)
|
||||
{
|
||||
int idx = 1;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_IDENTIFIER)
|
||||
return 1;
|
||||
|
||||
if (!(sample_type & PERF_SAMPLE_ID))
|
||||
return -1;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_CPU)
|
||||
idx += 1;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_STREAM_ID)
|
||||
idx += 1;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
void perf_evsel__calc_id_pos(struct perf_evsel *evsel)
|
||||
{
|
||||
evsel->id_pos = __perf_evsel__calc_id_pos(evsel->attr.sample_type);
|
||||
evsel->is_pos = __perf_evsel__calc_is_pos(evsel->attr.sample_type);
|
||||
}
|
||||
|
||||
void hists__init(struct hists *hists)
|
||||
{
|
||||
memset(hists, 0, sizeof(*hists));
|
||||
@ -63,6 +129,7 @@ void __perf_evsel__set_sample_bit(struct perf_evsel *evsel,
|
||||
if (!(evsel->attr.sample_type & bit)) {
|
||||
evsel->attr.sample_type |= bit;
|
||||
evsel->sample_size += sizeof(u64);
|
||||
perf_evsel__calc_id_pos(evsel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,12 +139,19 @@ void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel,
|
||||
if (evsel->attr.sample_type & bit) {
|
||||
evsel->attr.sample_type &= ~bit;
|
||||
evsel->sample_size -= sizeof(u64);
|
||||
perf_evsel__calc_id_pos(evsel);
|
||||
}
|
||||
}
|
||||
|
||||
void perf_evsel__set_sample_id(struct perf_evsel *evsel)
|
||||
void perf_evsel__set_sample_id(struct perf_evsel *evsel,
|
||||
bool can_sample_identifier)
|
||||
{
|
||||
perf_evsel__set_sample_bit(evsel, ID);
|
||||
if (can_sample_identifier) {
|
||||
perf_evsel__reset_sample_bit(evsel, ID);
|
||||
perf_evsel__set_sample_bit(evsel, IDENTIFIER);
|
||||
} else {
|
||||
perf_evsel__set_sample_bit(evsel, ID);
|
||||
}
|
||||
evsel->attr.read_format |= PERF_FORMAT_ID;
|
||||
}
|
||||
|
||||
@ -90,6 +164,7 @@ void perf_evsel__init(struct perf_evsel *evsel,
|
||||
INIT_LIST_HEAD(&evsel->node);
|
||||
hists__init(&evsel->hists);
|
||||
evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
|
||||
perf_evsel__calc_id_pos(evsel);
|
||||
}
|
||||
|
||||
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,
|
||||
* We need ID even in case of single event, because
|
||||
* PERF_SAMPLE_READ process ID specific data.
|
||||
*/
|
||||
perf_evsel__set_sample_id(evsel);
|
||||
perf_evsel__set_sample_id(evsel, false);
|
||||
|
||||
/*
|
||||
* 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,
|
||||
array += ((event->header.size -
|
||||
sizeof(event->header)) / sizeof(u64)) - 1;
|
||||
|
||||
if (type & PERF_SAMPLE_IDENTIFIER) {
|
||||
sample->id = *array;
|
||||
array--;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_CPU) {
|
||||
u.val64 = *array;
|
||||
if (swapped) {
|
||||
@ -1131,24 +1211,30 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool sample_overlap(const union perf_event *event,
|
||||
const void *offset, u64 size)
|
||||
static inline bool overflow(const void *endp, u16 max_size, const void *offset,
|
||||
u64 size)
|
||||
{
|
||||
const void *base = event;
|
||||
|
||||
if (offset + size > base + event->header.size)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return size > max_size || offset + size > endp;
|
||||
}
|
||||
|
||||
#define OVERFLOW_CHECK(offset, size, max_size) \
|
||||
do { \
|
||||
if (overflow(endp, (max_size), (offset), (size))) \
|
||||
return -EFAULT; \
|
||||
} while (0)
|
||||
|
||||
#define OVERFLOW_CHECK_u64(offset) \
|
||||
OVERFLOW_CHECK(offset, sizeof(u64), sizeof(u64))
|
||||
|
||||
int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||
struct perf_sample *data)
|
||||
{
|
||||
u64 type = evsel->attr.sample_type;
|
||||
u64 regs_user = evsel->attr.sample_regs_user;
|
||||
bool swapped = evsel->needs_swap;
|
||||
const u64 *array;
|
||||
u16 max_size = event->header.size;
|
||||
const void *endp = (void *)event + max_size;
|
||||
u64 sz;
|
||||
|
||||
/*
|
||||
* 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,
|
||||
|
||||
array = event->sample.array;
|
||||
|
||||
/*
|
||||
* The evsel's sample_size is based on PERF_SAMPLE_MASK which includes
|
||||
* up to PERF_SAMPLE_PERIOD. After that overflow() must be used to
|
||||
* check the format does not go past the end of the event.
|
||||
*/
|
||||
if (evsel->sample_size + sizeof(event->header) > event->header.size)
|
||||
return -EFAULT;
|
||||
|
||||
data->id = -1ULL;
|
||||
if (type & PERF_SAMPLE_IDENTIFIER) {
|
||||
data->id = *array;
|
||||
array++;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_IP) {
|
||||
data->ip = event->ip.ip;
|
||||
data->ip = *array;
|
||||
array++;
|
||||
}
|
||||
|
||||
@ -1203,7 +1300,6 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||
array++;
|
||||
}
|
||||
|
||||
data->id = -1ULL;
|
||||
if (type & PERF_SAMPLE_ID) {
|
||||
data->id = *array;
|
||||
array++;
|
||||
@ -1235,6 +1331,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||
if (type & PERF_SAMPLE_READ) {
|
||||
u64 read_format = evsel->attr.read_format;
|
||||
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
if (read_format & PERF_FORMAT_GROUP)
|
||||
data->read.group.nr = *array;
|
||||
else
|
||||
@ -1243,41 +1340,51 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||
array++;
|
||||
|
||||
if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
data->read.time_enabled = *array;
|
||||
array++;
|
||||
}
|
||||
|
||||
if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
data->read.time_running = *array;
|
||||
array++;
|
||||
}
|
||||
|
||||
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
|
||||
if (read_format & PERF_FORMAT_GROUP) {
|
||||
data->read.group.values = (struct sample_read_value *) array;
|
||||
array = (void *) array + data->read.group.nr *
|
||||
sizeof(struct sample_read_value);
|
||||
const u64 max_group_nr = UINT64_MAX /
|
||||
sizeof(struct sample_read_value);
|
||||
|
||||
if (data->read.group.nr > max_group_nr)
|
||||
return -EFAULT;
|
||||
sz = data->read.group.nr *
|
||||
sizeof(struct sample_read_value);
|
||||
OVERFLOW_CHECK(array, sz, max_size);
|
||||
data->read.group.values =
|
||||
(struct sample_read_value *)array;
|
||||
array = (void *)array + sz;
|
||||
} else {
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
data->read.one.id = *array;
|
||||
array++;
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_CALLCHAIN) {
|
||||
if (sample_overlap(event, array, sizeof(data->callchain->nr)))
|
||||
const u64 max_callchain_nr = UINT64_MAX / sizeof(u64);
|
||||
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
data->callchain = (struct ip_callchain *)array++;
|
||||
if (data->callchain->nr > max_callchain_nr)
|
||||
return -EFAULT;
|
||||
|
||||
data->callchain = (struct ip_callchain *)array;
|
||||
|
||||
if (sample_overlap(event, array, data->callchain->nr))
|
||||
return -EFAULT;
|
||||
|
||||
array += 1 + data->callchain->nr;
|
||||
sz = data->callchain->nr * sizeof(u64);
|
||||
OVERFLOW_CHECK(array, sz, max_size);
|
||||
array = (void *)array + sz;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_RAW) {
|
||||
const u64 *pdata;
|
||||
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
u.val64 = *array;
|
||||
if (WARN_ONCE(swapped,
|
||||
"Endianness of raw data not corrected!\n")) {
|
||||
@ -1286,65 +1393,71 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||
u.val32[0] = bswap_32(u.val32[0]);
|
||||
u.val32[1] = bswap_32(u.val32[1]);
|
||||
}
|
||||
|
||||
if (sample_overlap(event, array, sizeof(u32)))
|
||||
return -EFAULT;
|
||||
|
||||
data->raw_size = u.val32[0];
|
||||
pdata = (void *) array + sizeof(u32);
|
||||
array = (void *)array + sizeof(u32);
|
||||
|
||||
if (sample_overlap(event, pdata, data->raw_size))
|
||||
return -EFAULT;
|
||||
|
||||
data->raw_data = (void *) pdata;
|
||||
|
||||
array = (void *)array + data->raw_size + sizeof(u32);
|
||||
OVERFLOW_CHECK(array, data->raw_size, max_size);
|
||||
data->raw_data = (void *)array;
|
||||
array = (void *)array + data->raw_size;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_BRANCH_STACK) {
|
||||
u64 sz;
|
||||
const u64 max_branch_nr = UINT64_MAX /
|
||||
sizeof(struct branch_entry);
|
||||
|
||||
data->branch_stack = (struct branch_stack *)array;
|
||||
array++; /* nr */
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
data->branch_stack = (struct branch_stack *)array++;
|
||||
|
||||
if (data->branch_stack->nr > max_branch_nr)
|
||||
return -EFAULT;
|
||||
sz = data->branch_stack->nr * sizeof(struct branch_entry);
|
||||
sz /= sizeof(u64);
|
||||
array += sz;
|
||||
OVERFLOW_CHECK(array, sz, max_size);
|
||||
array = (void *)array + sz;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_REGS_USER) {
|
||||
/* First u64 tells us if we have any regs in sample. */
|
||||
u64 avail = *array++;
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
data->user_regs.abi = *array;
|
||||
array++;
|
||||
|
||||
if (avail) {
|
||||
if (data->user_regs.abi) {
|
||||
u64 regs_user = evsel->attr.sample_regs_user;
|
||||
|
||||
sz = hweight_long(regs_user) * sizeof(u64);
|
||||
OVERFLOW_CHECK(array, sz, max_size);
|
||||
data->user_regs.regs = (u64 *)array;
|
||||
array += hweight_long(regs_user);
|
||||
array = (void *)array + sz;
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_STACK_USER) {
|
||||
u64 size = *array++;
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
sz = *array++;
|
||||
|
||||
data->user_stack.offset = ((char *)(array - 1)
|
||||
- (char *) event);
|
||||
|
||||
if (!size) {
|
||||
if (!sz) {
|
||||
data->user_stack.size = 0;
|
||||
} else {
|
||||
OVERFLOW_CHECK(array, sz, max_size);
|
||||
data->user_stack.data = (char *)array;
|
||||
array += size / sizeof(*array);
|
||||
array = (void *)array + sz;
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
data->user_stack.size = *array++;
|
||||
}
|
||||
}
|
||||
|
||||
data->weight = 0;
|
||||
if (type & PERF_SAMPLE_WEIGHT) {
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
data->weight = *array;
|
||||
array++;
|
||||
}
|
||||
|
||||
data->data_src = PERF_MEM_DATA_SRC_NONE;
|
||||
if (type & PERF_SAMPLE_DATA_SRC) {
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
data->data_src = *array;
|
||||
array++;
|
||||
}
|
||||
@ -1352,12 +1465,105 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
|
||||
u64 sample_regs_user, u64 read_format)
|
||||
{
|
||||
size_t sz, result = sizeof(struct sample_event);
|
||||
|
||||
if (type & PERF_SAMPLE_IDENTIFIER)
|
||||
result += sizeof(u64);
|
||||
|
||||
if (type & PERF_SAMPLE_IP)
|
||||
result += sizeof(u64);
|
||||
|
||||
if (type & PERF_SAMPLE_TID)
|
||||
result += sizeof(u64);
|
||||
|
||||
if (type & PERF_SAMPLE_TIME)
|
||||
result += sizeof(u64);
|
||||
|
||||
if (type & PERF_SAMPLE_ADDR)
|
||||
result += sizeof(u64);
|
||||
|
||||
if (type & PERF_SAMPLE_ID)
|
||||
result += sizeof(u64);
|
||||
|
||||
if (type & PERF_SAMPLE_STREAM_ID)
|
||||
result += sizeof(u64);
|
||||
|
||||
if (type & PERF_SAMPLE_CPU)
|
||||
result += sizeof(u64);
|
||||
|
||||
if (type & PERF_SAMPLE_PERIOD)
|
||||
result += sizeof(u64);
|
||||
|
||||
if (type & PERF_SAMPLE_READ) {
|
||||
result += sizeof(u64);
|
||||
if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
|
||||
result += sizeof(u64);
|
||||
if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
|
||||
result += sizeof(u64);
|
||||
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
|
||||
if (read_format & PERF_FORMAT_GROUP) {
|
||||
sz = sample->read.group.nr *
|
||||
sizeof(struct sample_read_value);
|
||||
result += sz;
|
||||
} else {
|
||||
result += sizeof(u64);
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_CALLCHAIN) {
|
||||
sz = (sample->callchain->nr + 1) * sizeof(u64);
|
||||
result += sz;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_RAW) {
|
||||
result += sizeof(u32);
|
||||
result += sample->raw_size;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_BRANCH_STACK) {
|
||||
sz = sample->branch_stack->nr * sizeof(struct branch_entry);
|
||||
sz += sizeof(u64);
|
||||
result += sz;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_REGS_USER) {
|
||||
if (sample->user_regs.abi) {
|
||||
result += sizeof(u64);
|
||||
sz = hweight_long(sample_regs_user) * sizeof(u64);
|
||||
result += sz;
|
||||
} else {
|
||||
result += sizeof(u64);
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_STACK_USER) {
|
||||
sz = sample->user_stack.size;
|
||||
result += sizeof(u64);
|
||||
if (sz) {
|
||||
result += sz;
|
||||
result += sizeof(u64);
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_WEIGHT)
|
||||
result += sizeof(u64);
|
||||
|
||||
if (type & PERF_SAMPLE_DATA_SRC)
|
||||
result += sizeof(u64);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_sample(union perf_event *event, u64 type,
|
||||
u64 sample_regs_user, u64 read_format,
|
||||
const struct perf_sample *sample,
|
||||
bool swapped)
|
||||
{
|
||||
u64 *array;
|
||||
|
||||
size_t sz;
|
||||
/*
|
||||
* used for cross-endian analysis. See git commit 65014ab3
|
||||
* for why this goofiness is needed.
|
||||
@ -1366,8 +1572,13 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
|
||||
|
||||
array = event->sample.array;
|
||||
|
||||
if (type & PERF_SAMPLE_IDENTIFIER) {
|
||||
*array = sample->id;
|
||||
array++;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_IP) {
|
||||
event->ip.ip = sample->ip;
|
||||
*array = sample->ip;
|
||||
array++;
|
||||
}
|
||||
|
||||
@ -1425,6 +1636,97 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
|
||||
array++;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_READ) {
|
||||
if (read_format & PERF_FORMAT_GROUP)
|
||||
*array = sample->read.group.nr;
|
||||
else
|
||||
*array = sample->read.one.value;
|
||||
array++;
|
||||
|
||||
if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
|
||||
*array = sample->read.time_enabled;
|
||||
array++;
|
||||
}
|
||||
|
||||
if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
|
||||
*array = sample->read.time_running;
|
||||
array++;
|
||||
}
|
||||
|
||||
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
|
||||
if (read_format & PERF_FORMAT_GROUP) {
|
||||
sz = sample->read.group.nr *
|
||||
sizeof(struct sample_read_value);
|
||||
memcpy(array, sample->read.group.values, sz);
|
||||
array = (void *)array + sz;
|
||||
} else {
|
||||
*array = sample->read.one.id;
|
||||
array++;
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_CALLCHAIN) {
|
||||
sz = (sample->callchain->nr + 1) * sizeof(u64);
|
||||
memcpy(array, sample->callchain, sz);
|
||||
array = (void *)array + sz;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_RAW) {
|
||||
u.val32[0] = sample->raw_size;
|
||||
if (WARN_ONCE(swapped,
|
||||
"Endianness of raw data not corrected!\n")) {
|
||||
/*
|
||||
* Inverse of what is done in perf_evsel__parse_sample
|
||||
*/
|
||||
u.val32[0] = bswap_32(u.val32[0]);
|
||||
u.val32[1] = bswap_32(u.val32[1]);
|
||||
u.val64 = bswap_64(u.val64);
|
||||
}
|
||||
*array = u.val64;
|
||||
array = (void *)array + sizeof(u32);
|
||||
|
||||
memcpy(array, sample->raw_data, sample->raw_size);
|
||||
array = (void *)array + sample->raw_size;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_BRANCH_STACK) {
|
||||
sz = sample->branch_stack->nr * sizeof(struct branch_entry);
|
||||
sz += sizeof(u64);
|
||||
memcpy(array, sample->branch_stack, sz);
|
||||
array = (void *)array + sz;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_REGS_USER) {
|
||||
if (sample->user_regs.abi) {
|
||||
*array++ = sample->user_regs.abi;
|
||||
sz = hweight_long(sample_regs_user) * sizeof(u64);
|
||||
memcpy(array, sample->user_regs.regs, sz);
|
||||
array = (void *)array + sz;
|
||||
} else {
|
||||
*array++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_STACK_USER) {
|
||||
sz = sample->user_stack.size;
|
||||
*array++ = sz;
|
||||
if (sz) {
|
||||
memcpy(array, sample->user_stack.data, sz);
|
||||
array = (void *)array + sz;
|
||||
*array++ = sz;
|
||||
}
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_WEIGHT) {
|
||||
*array = sample->weight;
|
||||
array++;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_DATA_SRC) {
|
||||
*array = sample->data_src;
|
||||
array++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1554,6 +1856,7 @@ static int sample_type__fprintf(FILE *fp, bool *first, u64 value)
|
||||
bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU),
|
||||
bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW),
|
||||
bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER),
|
||||
bit_name(IDENTIFIER),
|
||||
{ .name = NULL, }
|
||||
};
|
||||
#undef bit_name
|
||||
|
@ -48,6 +48,12 @@ struct perf_sample_id {
|
||||
* @name - Can be set to retain the original event name passed by the user,
|
||||
* so that when showing results in tools such as 'perf stat', we
|
||||
* show the name used, not some alias.
|
||||
* @id_pos: the position of the event id (PERF_SAMPLE_ID or
|
||||
* PERF_SAMPLE_IDENTIFIER) in a sample event i.e. in the array of
|
||||
* struct sample_event
|
||||
* @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or
|
||||
* PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all
|
||||
* is used there is an id sample appended to non-sample events
|
||||
*/
|
||||
struct perf_evsel {
|
||||
struct list_head node;
|
||||
@ -74,6 +80,8 @@ struct perf_evsel {
|
||||
} handler;
|
||||
struct cpu_map *cpus;
|
||||
unsigned int sample_size;
|
||||
int id_pos;
|
||||
int is_pos;
|
||||
bool supported;
|
||||
bool needs_swap;
|
||||
/* parse modifier helper */
|
||||
@ -104,6 +112,9 @@ void perf_evsel__delete(struct perf_evsel *evsel);
|
||||
void perf_evsel__config(struct perf_evsel *evsel,
|
||||
struct perf_record_opts *opts);
|
||||
|
||||
int __perf_evsel__sample_size(u64 sample_type);
|
||||
void perf_evsel__calc_id_pos(struct perf_evsel *evsel);
|
||||
|
||||
bool perf_evsel__is_cache_op_valid(u8 type, u8 op);
|
||||
|
||||
#define PERF_EVSEL__MAX_ALIASES 8
|
||||
@ -142,7 +153,8 @@ void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel,
|
||||
#define perf_evsel__reset_sample_bit(evsel, bit) \
|
||||
__perf_evsel__reset_sample_bit(evsel, PERF_SAMPLE_##bit)
|
||||
|
||||
void perf_evsel__set_sample_id(struct perf_evsel *evsel);
|
||||
void perf_evsel__set_sample_id(struct perf_evsel *evsel,
|
||||
bool use_sample_identifier);
|
||||
|
||||
int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
|
||||
const char *filter);
|
||||
|
@ -32,7 +32,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pid != HOST_KERNEL_ID) {
|
||||
struct thread *thread = machine__findnew_thread(machine, pid);
|
||||
struct thread *thread = machine__findnew_thread(machine, 0,
|
||||
pid);
|
||||
char comm[64];
|
||||
|
||||
if (thread == NULL)
|
||||
@ -302,9 +303,10 @@ static struct thread *__machine__findnew_thread(struct machine *machine,
|
||||
return th;
|
||||
}
|
||||
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t tid)
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t pid,
|
||||
pid_t tid)
|
||||
{
|
||||
return __machine__findnew_thread(machine, 0, tid, true);
|
||||
return __machine__findnew_thread(machine, pid, tid, true);
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
int machine__process_comm_event(struct machine *machine, union perf_event *event)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, event->comm.tid);
|
||||
struct thread *thread = machine__findnew_thread(machine,
|
||||
event->comm.pid,
|
||||
event->comm.tid);
|
||||
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_comm(event, stdout);
|
||||
@ -1012,7 +1016,8 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
|
||||
return 0;
|
||||
}
|
||||
|
||||
thread = machine__findnew_thread(machine, event->mmap.pid);
|
||||
thread = machine__findnew_thread(machine, event->mmap.pid,
|
||||
event->mmap.pid);
|
||||
if (thread == NULL)
|
||||
goto out_problem;
|
||||
|
||||
@ -1051,13 +1056,16 @@ static void machine__remove_thread(struct machine *machine, struct thread *th)
|
||||
int machine__process_fork_event(struct machine *machine, union perf_event *event)
|
||||
{
|
||||
struct thread *thread = machine__find_thread(machine, event->fork.tid);
|
||||
struct thread *parent = machine__findnew_thread(machine, event->fork.ptid);
|
||||
struct thread *parent = machine__findnew_thread(machine,
|
||||
event->fork.ppid,
|
||||
event->fork.ptid);
|
||||
|
||||
/* if a thread currently exists for the thread id remove it */
|
||||
if (thread != NULL)
|
||||
machine__remove_thread(machine, thread);
|
||||
|
||||
thread = machine__findnew_thread(machine, event->fork.tid);
|
||||
thread = machine__findnew_thread(machine, event->fork.pid,
|
||||
event->fork.tid);
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_task(event, stdout);
|
||||
|
||||
|
@ -106,7 +106,8 @@ static inline bool machine__is_host(struct machine *machine)
|
||||
return machine ? machine->pid == HOST_KERNEL_ID : false;
|
||||
}
|
||||
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t tid);
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t pid,
|
||||
pid_t tid);
|
||||
|
||||
size_t machine__fprintf(struct machine *machine, FILE *fp);
|
||||
|
||||
|
108
tools/perf/util/record.c
Normal file
108
tools/perf/util/record.c
Normal file
@ -0,0 +1,108 @@
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "cpumap.h"
|
||||
#include "parse-events.h"
|
||||
|
||||
typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel);
|
||||
|
||||
static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
|
||||
{
|
||||
struct perf_evlist *evlist;
|
||||
struct perf_evsel *evsel;
|
||||
int err = -EAGAIN, fd;
|
||||
|
||||
evlist = perf_evlist__new();
|
||||
if (!evlist)
|
||||
return -ENOMEM;
|
||||
|
||||
if (parse_events(evlist, str))
|
||||
goto out_delete;
|
||||
|
||||
evsel = perf_evlist__first(evlist);
|
||||
|
||||
fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0);
|
||||
if (fd < 0)
|
||||
goto out_delete;
|
||||
close(fd);
|
||||
|
||||
fn(evsel);
|
||||
|
||||
fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0);
|
||||
if (fd < 0) {
|
||||
if (errno == EINVAL)
|
||||
err = -EINVAL;
|
||||
goto out_delete;
|
||||
}
|
||||
close(fd);
|
||||
err = 0;
|
||||
|
||||
out_delete:
|
||||
perf_evlist__delete(evlist);
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool perf_probe_api(setup_probe_fn_t fn)
|
||||
{
|
||||
const char *try[] = {"cycles:u", "instructions:u", "cpu-clock", NULL};
|
||||
struct cpu_map *cpus;
|
||||
int cpu, ret, i = 0;
|
||||
|
||||
cpus = cpu_map__new(NULL);
|
||||
if (!cpus)
|
||||
return false;
|
||||
cpu = cpus->map[0];
|
||||
cpu_map__delete(cpus);
|
||||
|
||||
do {
|
||||
ret = perf_do_probe_api(fn, cpu, try[i++]);
|
||||
if (!ret)
|
||||
return true;
|
||||
} while (ret == -EAGAIN && try[i]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void perf_probe_sample_identifier(struct perf_evsel *evsel)
|
||||
{
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_IDENTIFIER;
|
||||
}
|
||||
|
||||
bool perf_can_sample_identifier(void)
|
||||
{
|
||||
return perf_probe_api(perf_probe_sample_identifier);
|
||||
}
|
||||
|
||||
void perf_evlist__config(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
bool use_sample_identifier = false;
|
||||
|
||||
/*
|
||||
* Set the evsel leader links before we configure attributes,
|
||||
* since some might depend on this info.
|
||||
*/
|
||||
if (opts->group)
|
||||
perf_evlist__set_leader(evlist);
|
||||
|
||||
if (evlist->cpus->map[0] < 0)
|
||||
opts->no_inherit = true;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node)
|
||||
perf_evsel__config(evsel, opts);
|
||||
|
||||
if (evlist->nr_entries > 1) {
|
||||
struct perf_evsel *first = perf_evlist__first(evlist);
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
if (evsel->attr.sample_type == first->attr.sample_type)
|
||||
continue;
|
||||
use_sample_identifier = perf_can_sample_identifier();
|
||||
break;
|
||||
}
|
||||
list_for_each_entry(evsel, &evlist->entries, node)
|
||||
perf_evsel__set_sample_id(evsel, use_sample_identifier);
|
||||
}
|
||||
|
||||
perf_evlist__set_id_pos(evlist);
|
||||
}
|
@ -739,7 +739,7 @@ static void perf_session__print_tstamp(struct perf_session *session,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
u64 sample_type = perf_evlist__sample_type(session->evlist);
|
||||
u64 sample_type = __perf_evlist__combined_sample_type(session->evlist);
|
||||
|
||||
if (event->header.type != PERF_RECORD_SAMPLE &&
|
||||
!perf_evlist__sample_id_all(session->evlist)) {
|
||||
@ -840,7 +840,8 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||
|
||||
static struct machine *
|
||||
perf_session__find_machine_for_cpumode(struct perf_session *session,
|
||||
union perf_event *event)
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
|
||||
@ -852,7 +853,7 @@ static struct machine *
|
||||
if (event->header.type == PERF_RECORD_MMAP)
|
||||
pid = event->mmap.pid;
|
||||
else
|
||||
pid = event->ip.pid;
|
||||
pid = sample->pid;
|
||||
|
||||
return perf_session__findnew_machine(session, pid);
|
||||
}
|
||||
@ -958,7 +959,8 @@ static int perf_session_deliver_event(struct perf_session *session,
|
||||
hists__inc_nr_events(&evsel->hists, event->header.type);
|
||||
}
|
||||
|
||||
machine = perf_session__find_machine_for_cpumode(session, event);
|
||||
machine = perf_session__find_machine_for_cpumode(session, event,
|
||||
sample);
|
||||
|
||||
switch (event->header.type) {
|
||||
case PERF_RECORD_SAMPLE:
|
||||
@ -997,22 +999,6 @@ static int perf_session_deliver_event(struct perf_session *session,
|
||||
}
|
||||
}
|
||||
|
||||
static int perf_session__preprocess_sample(struct perf_session *session,
|
||||
union perf_event *event, struct perf_sample *sample)
|
||||
{
|
||||
if (event->header.type != PERF_RECORD_SAMPLE ||
|
||||
!(perf_evlist__sample_type(session->evlist) & PERF_SAMPLE_CALLCHAIN))
|
||||
return 0;
|
||||
|
||||
if (!ip_callchain__valid(sample->callchain, event)) {
|
||||
pr_debug("call-chain problem with event, skipping it.\n");
|
||||
++session->stats.nr_invalid_chains;
|
||||
session->stats.total_invalid_chains += sample->period;
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_session__process_user_event(struct perf_session *session, union perf_event *event,
|
||||
struct perf_tool *tool, u64 file_offset)
|
||||
{
|
||||
@ -1075,10 +1061,6 @@ static int perf_session__process_event(struct perf_session *session,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Preprocess sample records - precheck callchains */
|
||||
if (perf_session__preprocess_sample(session, event, &sample))
|
||||
return 0;
|
||||
|
||||
if (tool->ordered_samples) {
|
||||
ret = perf_session_queue_event(session, event, &sample,
|
||||
file_offset);
|
||||
@ -1099,7 +1081,7 @@ void perf_event_header__bswap(struct perf_event_header *self)
|
||||
|
||||
struct thread *perf_session__findnew(struct perf_session *session, pid_t pid)
|
||||
{
|
||||
return machine__findnew_thread(&session->machines.host, pid);
|
||||
return machine__findnew_thread(&session->machines.host, 0, pid);
|
||||
}
|
||||
|
||||
static struct thread *perf_session__register_idle_thread(struct perf_session *self)
|
||||
|
Loading…
Reference in New Issue
Block a user