perf_counter: Rework the sample ABI
The PERF_EVENT_READ implementation made me realize we don't actually need the sample_type int the output sample, since we already have that in the perf_counter_attr information. Therefore, remove the PERF_EVENT_MISC_OVERFLOW bit and the event->type overloading, and imply put counter overflow samples in a PERF_EVENT_SAMPLE type. This also fixes the issue that event->type was only 32-bit and sample_type had 64 usable bits. Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> LKML-Reference: <new-submission> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
parent
bfbd3381e6
commit
e6e18ec79b
@ -262,7 +262,6 @@ struct perf_counter_mmap_page {
|
|||||||
#define PERF_EVENT_MISC_KERNEL (1 << 0)
|
#define PERF_EVENT_MISC_KERNEL (1 << 0)
|
||||||
#define PERF_EVENT_MISC_USER (2 << 0)
|
#define PERF_EVENT_MISC_USER (2 << 0)
|
||||||
#define PERF_EVENT_MISC_HYPERVISOR (3 << 0)
|
#define PERF_EVENT_MISC_HYPERVISOR (3 << 0)
|
||||||
#define PERF_EVENT_MISC_OVERFLOW (1 << 2)
|
|
||||||
|
|
||||||
struct perf_event_header {
|
struct perf_event_header {
|
||||||
__u32 type;
|
__u32 type;
|
||||||
@ -348,9 +347,6 @@ enum perf_event_type {
|
|||||||
PERF_EVENT_READ = 8,
|
PERF_EVENT_READ = 8,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When header.misc & PERF_EVENT_MISC_OVERFLOW the event_type field
|
|
||||||
* will be PERF_SAMPLE_*
|
|
||||||
*
|
|
||||||
* struct {
|
* struct {
|
||||||
* struct perf_event_header header;
|
* struct perf_event_header header;
|
||||||
*
|
*
|
||||||
@ -358,8 +354,9 @@ enum perf_event_type {
|
|||||||
* { u32 pid, tid; } && PERF_SAMPLE_TID
|
* { u32 pid, tid; } && PERF_SAMPLE_TID
|
||||||
* { u64 time; } && PERF_SAMPLE_TIME
|
* { u64 time; } && PERF_SAMPLE_TIME
|
||||||
* { u64 addr; } && PERF_SAMPLE_ADDR
|
* { u64 addr; } && PERF_SAMPLE_ADDR
|
||||||
* { u64 config; } && PERF_SAMPLE_CONFIG
|
* { u64 id; } && PERF_SAMPLE_ID
|
||||||
* { u32 cpu, res; } && PERF_SAMPLE_CPU
|
* { u32 cpu, res; } && PERF_SAMPLE_CPU
|
||||||
|
* { u64 period; } && PERF_SAMPLE_PERIOD
|
||||||
*
|
*
|
||||||
* { u64 nr;
|
* { u64 nr;
|
||||||
* { u64 id, val; } cnt[nr]; } && PERF_SAMPLE_GROUP
|
* { u64 id, val; } cnt[nr]; } && PERF_SAMPLE_GROUP
|
||||||
@ -368,6 +365,9 @@ enum perf_event_type {
|
|||||||
* u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
|
* u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
|
||||||
* };
|
* };
|
||||||
*/
|
*/
|
||||||
|
PERF_EVENT_SAMPLE = 9,
|
||||||
|
|
||||||
|
PERF_EVENT_MAX, /* non-ABI */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum perf_callchain_context {
|
enum perf_callchain_context {
|
||||||
|
@ -2575,15 +2575,14 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
|
|||||||
u32 cpu, reserved;
|
u32 cpu, reserved;
|
||||||
} cpu_entry;
|
} cpu_entry;
|
||||||
|
|
||||||
header.type = 0;
|
header.type = PERF_EVENT_SAMPLE;
|
||||||
header.size = sizeof(header);
|
header.size = sizeof(header);
|
||||||
|
|
||||||
header.misc = PERF_EVENT_MISC_OVERFLOW;
|
header.misc = 0;
|
||||||
header.misc |= perf_misc_flags(data->regs);
|
header.misc |= perf_misc_flags(data->regs);
|
||||||
|
|
||||||
if (sample_type & PERF_SAMPLE_IP) {
|
if (sample_type & PERF_SAMPLE_IP) {
|
||||||
ip = perf_instruction_pointer(data->regs);
|
ip = perf_instruction_pointer(data->regs);
|
||||||
header.type |= PERF_SAMPLE_IP;
|
|
||||||
header.size += sizeof(ip);
|
header.size += sizeof(ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2592,7 +2591,6 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
|
|||||||
tid_entry.pid = perf_counter_pid(counter, current);
|
tid_entry.pid = perf_counter_pid(counter, current);
|
||||||
tid_entry.tid = perf_counter_tid(counter, current);
|
tid_entry.tid = perf_counter_tid(counter, current);
|
||||||
|
|
||||||
header.type |= PERF_SAMPLE_TID;
|
|
||||||
header.size += sizeof(tid_entry);
|
header.size += sizeof(tid_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2602,34 +2600,25 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
|
|||||||
*/
|
*/
|
||||||
time = sched_clock();
|
time = sched_clock();
|
||||||
|
|
||||||
header.type |= PERF_SAMPLE_TIME;
|
|
||||||
header.size += sizeof(u64);
|
header.size += sizeof(u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sample_type & PERF_SAMPLE_ADDR) {
|
if (sample_type & PERF_SAMPLE_ADDR)
|
||||||
header.type |= PERF_SAMPLE_ADDR;
|
|
||||||
header.size += sizeof(u64);
|
header.size += sizeof(u64);
|
||||||
}
|
|
||||||
|
|
||||||
if (sample_type & PERF_SAMPLE_ID) {
|
if (sample_type & PERF_SAMPLE_ID)
|
||||||
header.type |= PERF_SAMPLE_ID;
|
|
||||||
header.size += sizeof(u64);
|
header.size += sizeof(u64);
|
||||||
}
|
|
||||||
|
|
||||||
if (sample_type & PERF_SAMPLE_CPU) {
|
if (sample_type & PERF_SAMPLE_CPU) {
|
||||||
header.type |= PERF_SAMPLE_CPU;
|
|
||||||
header.size += sizeof(cpu_entry);
|
header.size += sizeof(cpu_entry);
|
||||||
|
|
||||||
cpu_entry.cpu = raw_smp_processor_id();
|
cpu_entry.cpu = raw_smp_processor_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sample_type & PERF_SAMPLE_PERIOD) {
|
if (sample_type & PERF_SAMPLE_PERIOD)
|
||||||
header.type |= PERF_SAMPLE_PERIOD;
|
|
||||||
header.size += sizeof(u64);
|
header.size += sizeof(u64);
|
||||||
}
|
|
||||||
|
|
||||||
if (sample_type & PERF_SAMPLE_GROUP) {
|
if (sample_type & PERF_SAMPLE_GROUP) {
|
||||||
header.type |= PERF_SAMPLE_GROUP;
|
|
||||||
header.size += sizeof(u64) +
|
header.size += sizeof(u64) +
|
||||||
counter->nr_siblings * sizeof(group_entry);
|
counter->nr_siblings * sizeof(group_entry);
|
||||||
}
|
}
|
||||||
@ -2639,10 +2628,9 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
|
|||||||
|
|
||||||
if (callchain) {
|
if (callchain) {
|
||||||
callchain_size = (1 + callchain->nr) * sizeof(u64);
|
callchain_size = (1 + callchain->nr) * sizeof(u64);
|
||||||
|
|
||||||
header.type |= PERF_SAMPLE_CALLCHAIN;
|
|
||||||
header.size += callchain_size;
|
header.size += callchain_size;
|
||||||
}
|
} else
|
||||||
|
header.size += sizeof(u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = perf_output_begin(&handle, counter, header.size, nmi, 1);
|
ret = perf_output_begin(&handle, counter, header.size, nmi, 1);
|
||||||
@ -2693,8 +2681,14 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callchain)
|
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
|
||||||
perf_output_copy(&handle, callchain, callchain_size);
|
if (callchain)
|
||||||
|
perf_output_copy(&handle, callchain, callchain_size);
|
||||||
|
else {
|
||||||
|
u64 nr = 0;
|
||||||
|
perf_output_put(&handle, nr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
perf_output_end(&handle);
|
perf_output_end(&handle);
|
||||||
}
|
}
|
||||||
|
@ -855,7 +855,7 @@ static unsigned long total = 0,
|
|||||||
total_unknown = 0;
|
total_unknown = 0;
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
|
process_sample_event(event_t *event, unsigned long offset, unsigned long head)
|
||||||
{
|
{
|
||||||
char level;
|
char level;
|
||||||
int show = 0;
|
int show = 0;
|
||||||
@ -1013,10 +1013,10 @@ process_period_event(event_t *event, unsigned long offset, unsigned long head)
|
|||||||
static int
|
static int
|
||||||
process_event(event_t *event, unsigned long offset, unsigned long head)
|
process_event(event_t *event, unsigned long offset, unsigned long head)
|
||||||
{
|
{
|
||||||
if (event->header.misc & PERF_EVENT_MISC_OVERFLOW)
|
|
||||||
return process_overflow_event(event, offset, head);
|
|
||||||
|
|
||||||
switch (event->header.type) {
|
switch (event->header.type) {
|
||||||
|
case PERF_EVENT_SAMPLE:
|
||||||
|
return process_sample_event(event, offset, head);
|
||||||
|
|
||||||
case PERF_EVENT_MMAP:
|
case PERF_EVENT_MMAP:
|
||||||
return process_mmap_event(event, offset, head);
|
return process_mmap_event(event, offset, head);
|
||||||
|
|
||||||
|
@ -53,6 +53,8 @@ static regex_t parent_regex;
|
|||||||
|
|
||||||
static int exclude_other = 1;
|
static int exclude_other = 1;
|
||||||
|
|
||||||
|
static u64 sample_type;
|
||||||
|
|
||||||
struct ip_event {
|
struct ip_event {
|
||||||
struct perf_event_header header;
|
struct perf_event_header header;
|
||||||
u64 ip;
|
u64 ip;
|
||||||
@ -1135,7 +1137,7 @@ static int validate_chain(struct ip_callchain *chain, event_t *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
|
process_sample_event(event_t *event, unsigned long offset, unsigned long head)
|
||||||
{
|
{
|
||||||
char level;
|
char level;
|
||||||
int show = 0;
|
int show = 0;
|
||||||
@ -1147,12 +1149,12 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
|
|||||||
void *more_data = event->ip.__more_data;
|
void *more_data = event->ip.__more_data;
|
||||||
struct ip_callchain *chain = NULL;
|
struct ip_callchain *chain = NULL;
|
||||||
|
|
||||||
if (event->header.type & PERF_SAMPLE_PERIOD) {
|
if (sample_type & PERF_SAMPLE_PERIOD) {
|
||||||
period = *(u64 *)more_data;
|
period = *(u64 *)more_data;
|
||||||
more_data += sizeof(u64);
|
more_data += sizeof(u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p period: %Ld\n",
|
dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n",
|
||||||
(void *)(offset + head),
|
(void *)(offset + head),
|
||||||
(void *)(long)(event->header.size),
|
(void *)(long)(event->header.size),
|
||||||
event->header.misc,
|
event->header.misc,
|
||||||
@ -1160,7 +1162,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
|
|||||||
(void *)(long)ip,
|
(void *)(long)ip,
|
||||||
(long long)period);
|
(long long)period);
|
||||||
|
|
||||||
if (event->header.type & PERF_SAMPLE_CALLCHAIN) {
|
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
chain = (void *)more_data;
|
chain = (void *)more_data;
|
||||||
@ -1352,10 +1354,10 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
|
|||||||
{
|
{
|
||||||
trace_event(event);
|
trace_event(event);
|
||||||
|
|
||||||
if (event->header.misc & PERF_EVENT_MISC_OVERFLOW)
|
|
||||||
return process_overflow_event(event, offset, head);
|
|
||||||
|
|
||||||
switch (event->header.type) {
|
switch (event->header.type) {
|
||||||
|
case PERF_EVENT_SAMPLE:
|
||||||
|
return process_sample_event(event, offset, head);
|
||||||
|
|
||||||
case PERF_EVENT_MMAP:
|
case PERF_EVENT_MMAP:
|
||||||
return process_mmap_event(event, offset, head);
|
return process_mmap_event(event, offset, head);
|
||||||
|
|
||||||
@ -1388,18 +1390,21 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
|
|||||||
|
|
||||||
static struct perf_header *header;
|
static struct perf_header *header;
|
||||||
|
|
||||||
static int perf_header__has_sample(u64 sample_mask)
|
static u64 perf_header__sample_type(void)
|
||||||
{
|
{
|
||||||
|
u64 sample_type = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < header->attrs; i++) {
|
for (i = 0; i < header->attrs; i++) {
|
||||||
struct perf_header_attr *attr = header->attr[i];
|
struct perf_header_attr *attr = header->attr[i];
|
||||||
|
|
||||||
if (!(attr->attr.sample_type & sample_mask))
|
if (!sample_type)
|
||||||
return 0;
|
sample_type = attr->attr.sample_type;
|
||||||
|
else if (sample_type != attr->attr.sample_type)
|
||||||
|
die("non matching sample_type");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return sample_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __cmd_report(void)
|
static int __cmd_report(void)
|
||||||
@ -1437,8 +1442,9 @@ static int __cmd_report(void)
|
|||||||
header = perf_header__read(input);
|
header = perf_header__read(input);
|
||||||
head = header->data_offset;
|
head = header->data_offset;
|
||||||
|
|
||||||
if (sort__has_parent &&
|
sample_type = perf_header__sample_type();
|
||||||
!perf_header__has_sample(PERF_SAMPLE_CALLCHAIN)) {
|
|
||||||
|
if (sort__has_parent && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
|
||||||
fprintf(stderr, "selected --sort parent, but no callchain data\n");
|
fprintf(stderr, "selected --sort parent, but no callchain data\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
@ -392,11 +392,11 @@ static void record_ip(u64 ip, int counter)
|
|||||||
samples--;
|
samples--;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_event(u64 ip, int counter)
|
static void process_event(u64 ip, int counter, int user)
|
||||||
{
|
{
|
||||||
samples++;
|
samples++;
|
||||||
|
|
||||||
if (ip < min_ip || ip > max_ip) {
|
if (user) {
|
||||||
userspace_samples++;
|
userspace_samples++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -509,9 +509,10 @@ static void mmap_read_counter(struct mmap_data *md)
|
|||||||
|
|
||||||
old += size;
|
old += size;
|
||||||
|
|
||||||
if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
|
if (event->header.type == PERF_EVENT_SAMPLE) {
|
||||||
if (event->header.type & PERF_SAMPLE_IP)
|
int user =
|
||||||
process_event(event->ip.ip, md->counter);
|
(event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK) == PERF_EVENT_MISC_USER;
|
||||||
|
process_event(event->ip.ip, md->counter, user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user