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:
Ingo Molnar 2013-08-31 10:05:48 +02:00
commit ea79ca0de0
33 changed files with 1195 additions and 197 deletions

View File

@ -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

View File

@ -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);

View File

@ -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]

View File

@ -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))

View File

@ -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);
}

View File

@ -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",

View File

@ -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);

View File

@ -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",

View File

@ -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,

View File

@ -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) {

View File

@ -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;

View File

@ -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",

View File

@ -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;

View File

@ -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);

View File

@ -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,
},

View File

@ -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;

View File

@ -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;

View File

@ -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]);

View 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;
}

View File

@ -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 */

View File

@ -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);

View File

@ -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)

View File

@ -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.

View File

@ -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>");

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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
View 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);
}

View File

@ -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)