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:
parent
9dd499889b
commit
8a057d8491
|
@ -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__
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue