perf: Make common SAMPLE_EVENT parser

Currently, sample event data is parsed for each commands, and it
is assuming that the data is not including other data. (E.g.
timechart, trace, etc. can't parse the event if it has
PERF_SAMPLE_CALLCHAIN)

So, even if we record the superset data for multiple commands at
a time, commands can't parse. etc.

To fix it, this makes common sample event parser, and use it to
parse sample event correctly. (PERF_SAMPLE_READ is unsupported
for now though, it seems to be not using.)

Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <87hbs48imv.fsf@devron.myhome.or.jp>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
OGAWA Hirofumi 2009-12-06 20:08:24 +09:00 committed by Ingo Molnar
parent 028c515253
commit 180f95e29a
7 changed files with 155 additions and 146 deletions

View File

@ -320,35 +320,23 @@ process_raw_event(event_t *raw_event __used, void *more_data,
static int process_sample_event(event_t *event) static int process_sample_event(event_t *event)
{ {
u64 ip = event->ip.ip; struct sample_data data;
u64 timestamp = -1; struct thread *thread;
u32 cpu = -1;
u64 period = 1;
void *more_data = event->ip.__more_data;
struct thread *thread = threads__findnew(event->ip.pid);
if (sample_type & PERF_SAMPLE_TIME) { memset(&data, 0, sizeof(data));
timestamp = *(u64 *)more_data; data.time = -1;
more_data += sizeof(u64); data.cpu = -1;
} data.period = 1;
if (sample_type & PERF_SAMPLE_CPU) { event__parse_sample(event, sample_type, &data);
cpu = *(u32 *)more_data;
more_data += sizeof(u32);
more_data += sizeof(u32); /* reserved */
}
if (sample_type & PERF_SAMPLE_PERIOD) {
period = *(u64 *)more_data;
more_data += sizeof(u64);
}
dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", dump_printf("(IP, %d): %d/%d: %p period: %Ld\n",
event->header.misc, event->header.misc,
event->ip.pid, event->ip.tid, data.pid, data.tid,
(void *)(long)ip, (void *)(long)data.ip,
(long long)period); (long long)data.period);
thread = threads__findnew(event->ip.pid);
if (thread == NULL) { if (thread == NULL) {
pr_debug("problem processing %d event, skipping it.\n", pr_debug("problem processing %d event, skipping it.\n",
event->header.type); event->header.type);
@ -357,7 +345,7 @@ static int process_sample_event(event_t *event)
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
process_raw_event(event, more_data, cpu, timestamp, thread); process_raw_event(event, data.raw_data, data.cpu, data.time, thread);
return 0; return 0;
} }

View File

@ -605,44 +605,41 @@ static int validate_chain(struct ip_callchain *chain, event_t *event)
static int process_sample_event(event_t *event) static int process_sample_event(event_t *event)
{ {
u64 ip = event->ip.ip; struct sample_data data;
u64 period = 1;
void *more_data = event->ip.__more_data;
struct ip_callchain *chain = NULL;
int cpumode; int cpumode;
struct addr_location al; struct addr_location al;
struct thread *thread = threads__findnew(event->ip.pid); struct thread *thread;
if (sample_type & PERF_SAMPLE_PERIOD) { memset(&data, 0, sizeof(data));
period = *(u64 *)more_data; data.period = 1;
more_data += sizeof(u64);
} event__parse_sample(event, sample_type, &data);
dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", dump_printf("(IP, %d): %d/%d: %p period: %Ld\n",
event->header.misc, event->header.misc,
event->ip.pid, event->ip.tid, data.pid, data.tid,
(void *)(long)ip, (void *)(long)data.ip,
(long long)period); (long long)data.period);
if (sample_type & PERF_SAMPLE_CALLCHAIN) { if (sample_type & PERF_SAMPLE_CALLCHAIN) {
unsigned int i; unsigned int i;
chain = (void *)more_data; dump_printf("... chain: nr:%Lu\n", data.callchain->nr);
dump_printf("... chain: nr:%Lu\n", chain->nr); if (validate_chain(data.callchain, event) < 0) {
if (validate_chain(chain, event) < 0) {
pr_debug("call-chain problem with event, " pr_debug("call-chain problem with event, "
"skipping it.\n"); "skipping it.\n");
return 0; return 0;
} }
if (dump_trace) { if (dump_trace) {
for (i = 0; i < chain->nr; i++) for (i = 0; i < data.callchain->nr; i++)
dump_printf("..... %2d: %016Lx\n", i, chain->ips[i]); dump_printf("..... %2d: %016Lx\n",
i, data.callchain->ips[i]);
} }
} }
thread = threads__findnew(data.pid);
if (thread == NULL) { if (thread == NULL) {
pr_debug("problem processing %d event, skipping it.\n", pr_debug("problem processing %d event, skipping it.\n",
event->header.type); event->header.type);
@ -657,7 +654,7 @@ static int process_sample_event(event_t *event)
cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
thread__find_addr_location(thread, cpumode, thread__find_addr_location(thread, cpumode,
MAP__FUNCTION, ip, &al, NULL); MAP__FUNCTION, data.ip, &al, NULL);
/* /*
* We have to do this here as we may have a dso with no symbol hit that * We have to do this here as we may have a dso with no symbol hit that
* has a name longer than the ones with symbols sampled. * has a name longer than the ones with symbols sampled.
@ -675,12 +672,12 @@ static int process_sample_event(event_t *event)
if (sym_list && al.sym && !strlist__has_entry(sym_list, al.sym->name)) if (sym_list && al.sym && !strlist__has_entry(sym_list, al.sym->name))
return 0; return 0;
if (hist_entry__add(&al, chain, period)) { if (hist_entry__add(&al, data.callchain, data.period)) {
pr_debug("problem incrementing symbol count, skipping event\n"); pr_debug("problem incrementing symbol count, skipping event\n");
return -1; return -1;
} }
event__stats.total += period; event__stats.total += data.period;
return 0; return 0;
} }

View File

@ -1598,40 +1598,26 @@ process_raw_event(event_t *raw_event __used, void *more_data,
static int process_sample_event(event_t *event) static int process_sample_event(event_t *event)
{ {
struct sample_data data;
struct thread *thread; struct thread *thread;
u64 ip = event->ip.ip;
u64 timestamp = -1;
u32 cpu = -1;
u64 period = 1;
void *more_data = event->ip.__more_data;
if (!(sample_type & PERF_SAMPLE_RAW)) if (!(sample_type & PERF_SAMPLE_RAW))
return 0; return 0;
thread = threads__findnew(event->ip.pid); memset(&data, 0, sizeof(data));
data.time = -1;
data.cpu = -1;
data.period = -1;
if (sample_type & PERF_SAMPLE_TIME) { event__parse_sample(event, sample_type, &data);
timestamp = *(u64 *)more_data;
more_data += sizeof(u64);
}
if (sample_type & PERF_SAMPLE_CPU) {
cpu = *(u32 *)more_data;
more_data += sizeof(u32);
more_data += sizeof(u32); /* reserved */
}
if (sample_type & PERF_SAMPLE_PERIOD) {
period = *(u64 *)more_data;
more_data += sizeof(u64);
}
dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", dump_printf("(IP, %d): %d/%d: %p period: %Ld\n",
event->header.misc, event->header.misc,
event->ip.pid, event->ip.tid, data.pid, data.tid,
(void *)(long)ip, (void *)(long)data.ip,
(long long)period); (long long)data.period);
thread = threads__findnew(data.pid);
if (thread == NULL) { if (thread == NULL) {
pr_debug("problem processing %d event, skipping it.\n", pr_debug("problem processing %d event, skipping it.\n",
event->header.type); event->header.type);
@ -1640,10 +1626,10 @@ static int process_sample_event(event_t *event)
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
if (profile_cpu != -1 && profile_cpu != (int) cpu) if (profile_cpu != -1 && profile_cpu != (int)data.cpu)
return 0; return 0;
process_raw_event(event, more_data, cpu, timestamp, thread); process_raw_event(event, data.raw_data, data.cpu, data.time, thread);
return 0; return 0;
} }

View File

@ -483,48 +483,22 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
static int static int
process_sample_event(event_t *event) process_sample_event(event_t *event)
{ {
int cursor = 0; struct sample_data data;
u64 addr = 0;
u64 stamp = 0;
u32 cpu = 0;
u32 pid = 0;
u32 size, *size_ptr;
struct trace_entry *te; struct trace_entry *te;
if (sample_type & PERF_SAMPLE_IP) memset(&data, 0, sizeof(data));
cursor++;
event__parse_sample(event, sample_type, &data);
if (sample_type & PERF_SAMPLE_TID) {
pid = event->sample.array[cursor]>>32;
cursor++;
}
if (sample_type & PERF_SAMPLE_TIME) { if (sample_type & PERF_SAMPLE_TIME) {
stamp = event->sample.array[cursor++]; if (!first_time || first_time > data.time)
first_time = data.time;
if (!first_time || first_time > stamp) if (last_time < data.time)
first_time = stamp; last_time = data.time;
if (last_time < stamp)
last_time = stamp;
} }
if (sample_type & PERF_SAMPLE_ADDR)
addr = event->sample.array[cursor++];
if (sample_type & PERF_SAMPLE_ID)
cursor++;
if (sample_type & PERF_SAMPLE_STREAM_ID)
cursor++;
if (sample_type & PERF_SAMPLE_CPU)
cpu = event->sample.array[cursor++] & 0xFFFFFFFF;
if (sample_type & PERF_SAMPLE_PERIOD)
cursor++;
size_ptr = (void *)&event->sample.array[cursor]; te = (void *)data.raw_data;
if (sample_type & PERF_SAMPLE_RAW && data.raw_size > 0) {
size = *size_ptr;
size_ptr++;
te = (void *)size_ptr;
if (sample_type & PERF_SAMPLE_RAW && size > 0) {
char *event_str; char *event_str;
struct power_entry *pe; struct power_entry *pe;
@ -536,19 +510,19 @@ process_sample_event(event_t *event)
return 0; return 0;
if (strcmp(event_str, "power:power_start") == 0) if (strcmp(event_str, "power:power_start") == 0)
c_state_start(cpu, stamp, pe->value); c_state_start(data.cpu, data.time, pe->value);
if (strcmp(event_str, "power:power_end") == 0) if (strcmp(event_str, "power:power_end") == 0)
c_state_end(cpu, stamp); c_state_end(data.cpu, data.time);
if (strcmp(event_str, "power:power_frequency") == 0) if (strcmp(event_str, "power:power_frequency") == 0)
p_state_change(cpu, stamp, pe->value); p_state_change(data.cpu, data.time, pe->value);
if (strcmp(event_str, "sched:sched_wakeup") == 0) if (strcmp(event_str, "sched:sched_wakeup") == 0)
sched_wakeup(cpu, stamp, pid, te); sched_wakeup(data.cpu, data.time, data.pid, te);
if (strcmp(event_str, "sched:sched_switch") == 0) if (strcmp(event_str, "sched:sched_switch") == 0)
sched_switch(cpu, stamp, te); sched_switch(data.cpu, data.time, te);
} }
return 0; return 0;
} }

View File

@ -66,58 +66,40 @@ static u64 sample_type;
static int process_sample_event(event_t *event) static int process_sample_event(event_t *event)
{ {
u64 ip = event->ip.ip; struct sample_data data;
u64 timestamp = -1; struct thread *thread;
u32 cpu = -1;
u64 period = 1;
void *more_data = event->ip.__more_data;
struct thread *thread = threads__findnew(event->ip.pid);
if (sample_type & PERF_SAMPLE_TIME) { memset(&data, 0, sizeof(data));
timestamp = *(u64 *)more_data; data.time = -1;
more_data += sizeof(u64); data.cpu = -1;
} data.period = 1;
if (sample_type & PERF_SAMPLE_CPU) { event__parse_sample(event, sample_type, &data);
cpu = *(u32 *)more_data;
more_data += sizeof(u32);
more_data += sizeof(u32); /* reserved */
}
if (sample_type & PERF_SAMPLE_PERIOD) {
period = *(u64 *)more_data;
more_data += sizeof(u64);
}
dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", dump_printf("(IP, %d): %d/%d: %p period: %Ld\n",
event->header.misc, event->header.misc,
event->ip.pid, event->ip.tid, data.pid, data.tid,
(void *)(long)ip, (void *)(long)data.ip,
(long long)period); (long long)data.period);
thread = threads__findnew(event->ip.pid);
if (thread == NULL) { if (thread == NULL) {
pr_debug("problem processing %d event, skipping it.\n", pr_debug("problem processing %d event, skipping it.\n",
event->header.type); event->header.type);
return -1; return -1;
} }
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
if (sample_type & PERF_SAMPLE_RAW) { if (sample_type & PERF_SAMPLE_RAW) {
struct {
u32 size;
char data[0];
} *raw = more_data;
/* /*
* FIXME: better resolve from pid from the struct trace_entry * FIXME: better resolve from pid from the struct trace_entry
* field, although it should be the same than this perf * field, although it should be the same than this perf
* event pid * event pid
*/ */
scripting_ops->process_event(cpu, raw->data, raw->size, scripting_ops->process_event(data.cpu, data.raw_data,
timestamp, thread->comm); data.raw_size,
data.time, thread->comm);
} }
event__stats.total += period; event__stats.total += data.period;
return 0; return 0;
} }

View File

@ -310,3 +310,70 @@ int event__preprocess_sample(const event_t *self, struct addr_location *al,
al->level == 'H' ? "[hypervisor]" : "<not found>"); al->level == 'H' ? "[hypervisor]" : "<not found>");
return 0; return 0;
} }
int event__parse_sample(event_t *event, u64 type, struct sample_data *data)
{
u64 *array = event->sample.array;
if (type & PERF_SAMPLE_IP) {
data->ip = event->ip.ip;
array++;
}
if (type & PERF_SAMPLE_TID) {
u32 *p = (u32 *)array;
data->pid = p[0];
data->tid = p[1];
array++;
}
if (type & PERF_SAMPLE_TIME) {
data->time = *array;
array++;
}
if (type & PERF_SAMPLE_ADDR) {
data->addr = *array;
array++;
}
if (type & PERF_SAMPLE_ID) {
data->id = *array;
array++;
}
if (type & PERF_SAMPLE_STREAM_ID) {
data->stream_id = *array;
array++;
}
if (type & PERF_SAMPLE_CPU) {
u32 *p = (u32 *)array;
data->cpu = *p;
array++;
}
if (type & PERF_SAMPLE_PERIOD) {
data->period = *array;
array++;
}
if (type & PERF_SAMPLE_READ) {
pr_debug("PERF_SAMPLE_READ is unsuported for now\n");
return -1;
}
if (type & PERF_SAMPLE_CALLCHAIN) {
data->callchain = (struct ip_callchain *)array;
array += 1 + data->callchain->nr;
}
if (type & PERF_SAMPLE_RAW) {
u32 *p = (u32 *)array;
data->raw_size = *p;
p++;
data->raw_data = p;
}
return 0;
}

View File

@ -56,11 +56,25 @@ struct read_event {
u64 id; u64 id;
}; };
struct sample_event{ struct sample_event {
struct perf_event_header header; struct perf_event_header header;
u64 array[]; u64 array[];
}; };
struct sample_data {
u64 ip;
u32 pid, tid;
u64 time;
u64 addr;
u64 id;
u64 stream_id;
u32 cpu;
u64 period;
struct ip_callchain *callchain;
u32 raw_size;
void *raw_data;
};
#define BUILD_ID_SIZE 20 #define BUILD_ID_SIZE 20
struct build_id_event { struct build_id_event {
@ -155,5 +169,6 @@ int event__process_task(event_t *self);
struct addr_location; struct addr_location;
int event__preprocess_sample(const event_t *self, struct addr_location *al, int event__preprocess_sample(const event_t *self, struct addr_location *al,
symbol_filter_t filter); symbol_filter_t filter);
int event__parse_sample(event_t *event, u64 type, struct sample_data *data);
#endif /* __PERF_RECORD_H */ #endif /* __PERF_RECORD_H */