perf_counter: move the event overflow output bits to record_type

Per suggestion from Paul, move the event overflow bits to record_type
and sanitize the enums a bit.

Breaks the ABI -- again ;-)

Suggested-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Orig-LKML-Reference: <20090402091319.151921176@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Peter Zijlstra 2009-04-02 11:11:59 +02:00 committed by Ingo Molnar
parent 9dd499889b
commit 8a057d8491
2 changed files with 71 additions and 86 deletions

View File

@ -73,15 +73,6 @@ enum sw_event_ids {
PERF_SW_EVENTS_MAX = 7, PERF_SW_EVENTS_MAX = 7,
}; };
/*
* IRQ-notification data record type:
*/
enum perf_counter_record_type {
PERF_RECORD_SIMPLE = 0,
PERF_RECORD_IRQ = 1,
PERF_RECORD_GROUP = 2,
};
#define __PERF_COUNTER_MASK(name) \ #define __PERF_COUNTER_MASK(name) \
(((1ULL << PERF_COUNTER_##name##_BITS) - 1) << \ (((1ULL << PERF_COUNTER_##name##_BITS) - 1) << \
PERF_COUNTER_##name##_SHIFT) PERF_COUNTER_##name##_SHIFT)
@ -102,6 +93,17 @@ enum perf_counter_record_type {
#define PERF_COUNTER_EVENT_SHIFT 0 #define PERF_COUNTER_EVENT_SHIFT 0
#define PERF_COUNTER_EVENT_MASK __PERF_COUNTER_MASK(EVENT) #define PERF_COUNTER_EVENT_MASK __PERF_COUNTER_MASK(EVENT)
/*
* Bits that can be set in hw_event.record_type to request information
* in the overflow packets.
*/
enum perf_counter_record_format {
PERF_RECORD_IP = 1U << 0,
PERF_RECORD_TID = 1U << 1,
PERF_RECORD_GROUP = 1U << 2,
PERF_RECORD_CALLCHAIN = 1U << 3,
};
/* /*
* Bits that can be set in hw_event.read_format to request that * Bits that can be set in hw_event.read_format to request that
* reads on the counter should return the indicated quantities, * reads on the counter should return the indicated quantities,
@ -125,8 +127,8 @@ struct perf_counter_hw_event {
__u64 config; __u64 config;
__u64 irq_period; __u64 irq_period;
__u64 record_type; __u32 record_type;
__u64 read_format; __u32 read_format;
__u64 disabled : 1, /* off by default */ __u64 disabled : 1, /* off by default */
nmi : 1, /* NMI sampling */ nmi : 1, /* NMI sampling */
@ -137,12 +139,10 @@ struct perf_counter_hw_event {
exclude_kernel : 1, /* ditto kernel */ exclude_kernel : 1, /* ditto kernel */
exclude_hv : 1, /* ditto hypervisor */ exclude_hv : 1, /* ditto hypervisor */
exclude_idle : 1, /* don't count when idle */ exclude_idle : 1, /* don't count when idle */
include_tid : 1, /* include the tid */
mmap : 1, /* include mmap data */ mmap : 1, /* include mmap data */
munmap : 1, /* include munmap data */ munmap : 1, /* include munmap data */
callchain : 1, /* add callchain data */
__reserved_1 : 51; __reserved_1 : 53;
__u32 extra_config_len; __u32 extra_config_len;
__u32 __reserved_4; __u32 __reserved_4;
@ -212,15 +212,21 @@ struct perf_event_header {
enum perf_event_type { enum perf_event_type {
PERF_EVENT_GROUP = 1, PERF_EVENT_MMAP = 1,
PERF_EVENT_MUNMAP = 2,
PERF_EVENT_MMAP = 2, /*
PERF_EVENT_MUNMAP = 3, * Half the event type space is reserved for the counter overflow
* bitfields, as found in hw_event.record_type.
PERF_EVENT_OVERFLOW = 1UL << 31, *
__PERF_EVENT_IP = 1UL << 30, * These events will have types of the form:
__PERF_EVENT_TID = 1UL << 29, * PERF_EVENT_COUNTER_OVERFLOW { | __PERF_EVENT_* } *
__PERF_EVENT_CALLCHAIN = 1UL << 28, */
PERF_EVENT_COUNTER_OVERFLOW = 1UL << 31,
__PERF_EVENT_IP = PERF_RECORD_IP,
__PERF_EVENT_TID = PERF_RECORD_TID,
__PERF_EVENT_GROUP = PERF_RECORD_GROUP,
__PERF_EVENT_CALLCHAIN = PERF_RECORD_CALLCHAIN,
}; };
#ifdef __KERNEL__ #ifdef __KERNEL__

View File

@ -1765,27 +1765,34 @@ static void perf_output_end(struct perf_output_handle *handle)
rcu_read_unlock(); rcu_read_unlock();
} }
static void perf_output_simple(struct perf_counter *counter, void perf_counter_output(struct perf_counter *counter,
int nmi, struct pt_regs *regs) int nmi, struct pt_regs *regs)
{ {
int ret; int ret;
u64 record_type = counter->hw_event.record_type;
struct perf_output_handle handle; struct perf_output_handle handle;
struct perf_event_header header; struct perf_event_header header;
u64 ip; u64 ip;
struct { struct {
u32 pid, tid; u32 pid, tid;
} tid_entry; } tid_entry;
struct {
u64 event;
u64 counter;
} group_entry;
struct perf_callchain_entry *callchain = NULL; struct perf_callchain_entry *callchain = NULL;
int callchain_size = 0; int callchain_size = 0;
header.type = PERF_EVENT_OVERFLOW; header.type = PERF_EVENT_COUNTER_OVERFLOW;
header.size = sizeof(header); header.size = sizeof(header);
ip = instruction_pointer(regs); if (record_type & PERF_RECORD_IP) {
header.type |= __PERF_EVENT_IP; ip = instruction_pointer(regs);
header.size += sizeof(ip); header.type |= __PERF_EVENT_IP;
header.size += sizeof(ip);
}
if (counter->hw_event.include_tid) { if (record_type & PERF_RECORD_TID) {
/* namespace issues */ /* namespace issues */
tid_entry.pid = current->group_leader->pid; tid_entry.pid = current->group_leader->pid;
tid_entry.tid = current->pid; tid_entry.tid = current->pid;
@ -1794,7 +1801,13 @@ static void perf_output_simple(struct perf_counter *counter,
header.size += sizeof(tid_entry); header.size += sizeof(tid_entry);
} }
if (counter->hw_event.callchain) { if (record_type & PERF_RECORD_GROUP) {
header.type |= __PERF_EVENT_GROUP;
header.size += sizeof(u64) +
counter->nr_siblings * sizeof(group_entry);
}
if (record_type & PERF_RECORD_CALLCHAIN) {
callchain = perf_callchain(regs); callchain = perf_callchain(regs);
if (callchain) { if (callchain) {
@ -1810,71 +1823,37 @@ static void perf_output_simple(struct perf_counter *counter,
return; return;
perf_output_put(&handle, header); perf_output_put(&handle, header);
perf_output_put(&handle, ip);
if (counter->hw_event.include_tid) if (record_type & PERF_RECORD_IP)
perf_output_put(&handle, ip);
if (record_type & PERF_RECORD_TID)
perf_output_put(&handle, tid_entry); perf_output_put(&handle, tid_entry);
if (record_type & PERF_RECORD_GROUP) {
struct perf_counter *leader, *sub;
u64 nr = counter->nr_siblings;
perf_output_put(&handle, nr);
leader = counter->group_leader;
list_for_each_entry(sub, &leader->sibling_list, list_entry) {
if (sub != counter)
sub->hw_ops->read(sub);
group_entry.event = sub->hw_event.config;
group_entry.counter = atomic64_read(&sub->count);
perf_output_put(&handle, group_entry);
}
}
if (callchain) if (callchain)
perf_output_copy(&handle, callchain, callchain_size); perf_output_copy(&handle, callchain, callchain_size);
perf_output_end(&handle); perf_output_end(&handle);
} }
static void perf_output_group(struct perf_counter *counter, int nmi)
{
struct perf_output_handle handle;
struct perf_event_header header;
struct perf_counter *leader, *sub;
unsigned int size;
struct {
u64 event;
u64 counter;
} entry;
int ret;
size = sizeof(header) + counter->nr_siblings * sizeof(entry);
ret = perf_output_begin(&handle, counter, size, nmi);
if (ret)
return;
header.type = PERF_EVENT_GROUP;
header.size = size;
perf_output_put(&handle, header);
leader = counter->group_leader;
list_for_each_entry(sub, &leader->sibling_list, list_entry) {
if (sub != counter)
sub->hw_ops->read(sub);
entry.event = sub->hw_event.config;
entry.counter = atomic64_read(&sub->count);
perf_output_put(&handle, entry);
}
perf_output_end(&handle);
}
void perf_counter_output(struct perf_counter *counter,
int nmi, struct pt_regs *regs)
{
switch (counter->hw_event.record_type) {
case PERF_RECORD_SIMPLE:
return;
case PERF_RECORD_IRQ:
perf_output_simple(counter, nmi, regs);
break;
case PERF_RECORD_GROUP:
perf_output_group(counter, nmi);
break;
}
}
/* /*
* mmap tracking * mmap tracking
*/ */