Blame src/components/perf_event/perf_helpers.h

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