|
Packit |
577717 |
/*****************************************************************/
|
|
Packit |
577717 |
/********* Begin perf_event low-level code ***********************/
|
|
Packit |
577717 |
/*****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* In case headers aren't new enough to have __NR_perf_event_open */
|
|
Packit |
577717 |
#ifndef __NR_perf_event_open
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifdef __powerpc__
|
|
Packit |
577717 |
#define __NR_perf_event_open 319
|
|
Packit |
577717 |
#elif defined(__x86_64__)
|
|
Packit |
577717 |
#define __NR_perf_event_open 298
|
|
Packit |
577717 |
#elif defined(__i386__)
|
|
Packit |
577717 |
#define __NR_perf_event_open 336
|
|
Packit |
577717 |
#elif defined(__arm__)
|
|
Packit |
577717 |
#define __NR_perf_event_open 364
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static long
|
|
Packit |
577717 |
sys_perf_event_open( struct perf_event_attr *hw_event,
|
|
Packit |
577717 |
pid_t pid, int cpu, int group_fd, unsigned long flags )
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = syscall( __NR_perf_event_open,
|
|
Packit |
577717 |
hw_event, pid, cpu, group_fd, flags );
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return ret;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if defined(__x86_64__) || defined(__i386__)
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline unsigned long long rdtsc(void) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
unsigned a,d;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
__asm__ volatile("rdtsc" : "=a" (a), "=d" (d));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return ((unsigned long long)a) | (((unsigned long long)d) << 32);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline unsigned long long rdpmc(unsigned int counter) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
unsigned int low, high;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
__asm__ volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return (unsigned long long)low | ((unsigned long long)high) <<32;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define barrier() __asm__ volatile("" ::: "memory")
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* based on the code in include/uapi/linux/perf_event.h */
|
|
Packit |
577717 |
static inline unsigned long long mmap_read_self(void *addr,
|
|
Packit |
577717 |
unsigned long long *en,
|
|
Packit |
577717 |
unsigned long long *ru) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct perf_event_mmap_page *pc = addr;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
uint32_t seq, time_mult, time_shift, index, width;
|
|
Packit |
577717 |
int64_t count;
|
|
Packit |
577717 |
uint64_t enabled, running;
|
|
Packit |
577717 |
uint64_t cyc, time_offset;
|
|
Packit |
577717 |
int64_t pmc = 0;
|
|
Packit |
577717 |
uint64_t quot, rem;
|
|
Packit |
577717 |
uint64_t delta = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
do {
|
|
Packit |
577717 |
/* The kernel increments pc->lock any time */
|
|
Packit |
577717 |
/* perf_event_update_userpage() is called */
|
|
Packit |
577717 |
/* So by checking now, and the end, we */
|
|
Packit |
577717 |
/* can see if an update happened while we */
|
|
Packit |
577717 |
/* were trying to read things, and re-try */
|
|
Packit |
577717 |
/* if something changed */
|
|
Packit |
577717 |
/* The barrier ensures we get the most up to date */
|
|
Packit |
577717 |
/* version of the pc->lock variable */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
seq=pc->lock;
|
|
Packit |
577717 |
barrier();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* For multiplexing */
|
|
Packit |
577717 |
/* time_enabled is time the event was enabled */
|
|
Packit |
577717 |
enabled = pc->time_enabled;
|
|
Packit |
577717 |
/* time_running is time the event was actually running */
|
|
Packit |
577717 |
running = pc->time_running;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* if cap_user_time is set, we can use rdtsc */
|
|
Packit |
577717 |
/* to calculate more exact enabled/running time */
|
|
Packit |
577717 |
/* for more accurate multiplex calculations */
|
|
Packit |
577717 |
if ( (pc->cap_user_time) && (enabled != running)) {
|
|
Packit |
577717 |
cyc = rdtsc();
|
|
Packit |
577717 |
time_offset = pc->time_offset;
|
|
Packit |
577717 |
time_mult = pc->time_mult;
|
|
Packit |
577717 |
time_shift = pc->time_shift;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
quot=(cyc>>time_shift);
|
|
Packit |
577717 |
rem = cyc & (((uint64_t)1 << time_shift) - 1);
|
|
Packit |
577717 |
delta = time_offset + (quot * time_mult) +
|
|
Packit |
577717 |
((rem * time_mult) >> time_shift);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
enabled+=delta;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* actually do the measurement */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Index of register to read */
|
|
Packit |
577717 |
/* 0 means stopped/not-active */
|
|
Packit |
577717 |
/* Need to subtract 1 to get actual index to rdpmc() */
|
|
Packit |
577717 |
index = pc->index;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* count is the value of the counter the last time */
|
|
Packit |
577717 |
/* the kernel read it */
|
|
Packit |
577717 |
/* If we don't sign extend it, we get large negative */
|
|
Packit |
577717 |
/* numbers which break if an IOC_RESET is done */
|
|
Packit |
577717 |
width = pc->pmc_width;
|
|
Packit |
577717 |
count = pc->offset;
|
|
Packit |
577717 |
count<<=(64-width);
|
|
Packit |
577717 |
count>>=(64-width);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Ugh, libpfm4 perf_event.h has cap_usr_rdpmc */
|
|
Packit |
577717 |
/* while actual perf_event.h has cap_user_rdpmc */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Only read if rdpmc enabled and event index valid */
|
|
Packit |
577717 |
/* Otherwise return the older (out of date?) count value */
|
|
Packit |
577717 |
if (pc->cap_usr_rdpmc && index) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Read counter value */
|
|
Packit |
577717 |
pmc = rdpmc(index-1);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* sign extend result */
|
|
Packit |
577717 |
pmc<<=(64-width);
|
|
Packit |
577717 |
pmc>>=(64-width);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* add current count into the existing kernel count */
|
|
Packit |
577717 |
count+=pmc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Only adjust if index is valid */
|
|
Packit |
577717 |
running+=delta;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
barrier();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
} while (pc->lock != seq);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (en) *en=enabled;
|
|
Packit |
577717 |
if (ru) *ru=running;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return count;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
static inline unsigned long long mmap_read_self(void *addr,
|
|
Packit |
577717 |
unsigned long long *en,
|
|
Packit |
577717 |
unsigned long long *ru) {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
(void)addr;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
*en=0;
|
|
Packit |
577717 |
*ru=0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return (unsigned long long)(-1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* These functions are based on builtin-record.c in the */
|
|
Packit |
577717 |
/* kernel's tools/perf directory. */
|
|
Packit |
577717 |
/* This code is from a really ancient version of perf */
|
|
Packit |
577717 |
/* And should be updated/commented properly */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static uint64_t
|
|
Packit |
577717 |
mmap_read_head( pe_event_info_t *pe )
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct perf_event_mmap_page *pc = pe->mmap_buf;
|
|
Packit |
577717 |
int head;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if ( pc == NULL ) {
|
|
Packit |
577717 |
PAPIERROR( "perf_event_mmap_page is NULL" );
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
head = pc->data_head;
|
|
Packit |
577717 |
rmb();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return head;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
mmap_write_tail( pe_event_info_t *pe, uint64_t tail )
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct perf_event_mmap_page *pc = pe->mmap_buf;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* ensure all reads are done before we write the tail out. */
|
|
Packit |
577717 |
pc->data_tail = tail;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Does the kernel define these somewhere? */
|
|
Packit |
577717 |
struct ip_event {
|
|
Packit |
577717 |
struct perf_event_header header;
|
|
Packit |
577717 |
uint64_t ip;
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
struct lost_event {
|
|
Packit |
577717 |
struct perf_event_header header;
|
|
Packit |
577717 |
uint64_t id;
|
|
Packit |
577717 |
uint64_t lost;
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
typedef union event_union {
|
|
Packit |
577717 |
struct perf_event_header header;
|
|
Packit |
577717 |
struct ip_event ip;
|
|
Packit |
577717 |
struct lost_event lost;
|
|
Packit |
577717 |
} perf_sample_event_t;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Should re-write with comments if we ever figure out what's */
|
|
Packit |
577717 |
/* going on here. */
|
|
Packit |
577717 |
static void
|
|
Packit |
577717 |
mmap_read( int cidx, ThreadInfo_t **thr, pe_event_info_t *pe,
|
|
Packit |
577717 |
int profile_index )
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
uint64_t head = mmap_read_head( pe );
|
|
Packit |
577717 |
uint64_t old = pe->tail;
|
|
Packit |
577717 |
unsigned char *data = ((unsigned char*)pe->mmap_buf) + getpagesize();
|
|
Packit |
577717 |
int diff;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
diff = head - old;
|
|
Packit |
577717 |
if ( diff < 0 ) {
|
|
Packit |
577717 |
SUBDBG( "WARNING: failed to keep up with mmap data. head = %" PRIu64
|
|
Packit |
577717 |
", tail = %" PRIu64 ". Discarding samples.\n", head, old );
|
|
Packit |
577717 |
/* head points to a known good entry, start there. */
|
|
Packit |
577717 |
old = head;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for( ; old != head; ) {
|
|
Packit |
577717 |
perf_sample_event_t *event = ( perf_sample_event_t * )& data[old & pe->mask];
|
|
Packit |
577717 |
perf_sample_event_t event_copy;
|
|
Packit |
577717 |
size_t size = event->header.size;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Event straddles the mmap boundary -- header should always */
|
|
Packit |
577717 |
/* be inside due to u64 alignment of output. */
|
|
Packit |
577717 |
if ( ( old & pe->mask ) + size != ( ( old + size ) & pe->mask ) ) {
|
|
Packit |
577717 |
uint64_t offset = old;
|
|
Packit |
577717 |
uint64_t len = min( sizeof ( *event ), size ), cpy;
|
|
Packit |
577717 |
void *dst = &event_copy;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
do {
|
|
Packit |
577717 |
cpy = min( pe->mask + 1 - ( offset & pe->mask ), len );
|
|
Packit |
577717 |
memcpy( dst, &data[offset & pe->mask], cpy );
|
|
Packit |
577717 |
offset += cpy;
|
|
Packit |
577717 |
dst = ((unsigned char*)dst) + cpy;
|
|
Packit |
577717 |
len -= cpy;
|
|
Packit |
577717 |
} while ( len );
|
|
Packit |
577717 |
|
|
Packit |
577717 |
event = &event_copy;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
old += size;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
SUBDBG( "event->type = %08x\n", event->header.type );
|
|
Packit |
577717 |
SUBDBG( "event->size = %d\n", event->header.size );
|
|
Packit |
577717 |
|
|
Packit |
577717 |
switch ( event->header.type ) {
|
|
Packit |
577717 |
case PERF_RECORD_SAMPLE:
|
|
Packit |
577717 |
_papi_hwi_dispatch_profile( ( *thr )->running_eventset[cidx],
|
|
Packit |
577717 |
( caddr_t ) ( unsigned long ) event->ip.ip,
|
|
Packit |
577717 |
0, profile_index );
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
case PERF_RECORD_LOST:
|
|
Packit |
577717 |
SUBDBG( "Warning: because of a mmap buffer overrun, %" PRId64
|
|
Packit |
577717 |
" events were lost.\n"
|
|
Packit |
577717 |
"Loss was recorded when counter id %#"PRIx64
|
|
Packit |
577717 |
" overflowed.\n", event->lost.lost, event->lost.id );
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
SUBDBG( "Error: unexpected header type - %d\n",
|
|
Packit |
577717 |
event->header.type );
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pe->tail = old;
|
|
Packit |
577717 |
mmap_write_tail( pe, old );
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|