diff options
Diffstat (limited to 'tools/lib/perf/mmap.c')
| -rw-r--r-- | tools/lib/perf/mmap.c | 88 | 
1 files changed, 88 insertions, 0 deletions
| diff --git a/tools/lib/perf/mmap.c b/tools/lib/perf/mmap.c index 79d5ed6c38cc..c89dfa5f67b3 100644 --- a/tools/lib/perf/mmap.c +++ b/tools/lib/perf/mmap.c @@ -8,9 +8,11 @@  #include <linux/perf_event.h>  #include <perf/mmap.h>  #include <perf/event.h> +#include <perf/evsel.h>  #include <internal/mmap.h>  #include <internal/lib.h>  #include <linux/kernel.h> +#include <linux/math64.h>  #include "internal.h"  void perf_mmap__init(struct perf_mmap *map, struct perf_mmap *prev, @@ -273,3 +275,89 @@ union perf_event *perf_mmap__read_event(struct perf_mmap *map)  	return event;  } + +#if defined(__i386__) || defined(__x86_64__) +static u64 read_perf_counter(unsigned int counter) +{ +	unsigned int low, high; + +	asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)); + +	return low | ((u64)high) << 32; +} + +static u64 read_timestamp(void) +{ +	unsigned int low, high; + +	asm volatile("rdtsc" : "=a" (low), "=d" (high)); + +	return low | ((u64)high) << 32; +} +#else +static u64 read_perf_counter(unsigned int counter __maybe_unused) { return 0; } +static u64 read_timestamp(void) { return 0; } +#endif + +int perf_mmap__read_self(struct perf_mmap *map, struct perf_counts_values *count) +{ +	struct perf_event_mmap_page *pc = map->base; +	u32 seq, idx, time_mult = 0, time_shift = 0; +	u64 cnt, cyc = 0, time_offset = 0, time_cycles = 0, time_mask = ~0ULL; + +	if (!pc || !pc->cap_user_rdpmc) +		return -1; + +	do { +		seq = READ_ONCE(pc->lock); +		barrier(); + +		count->ena = READ_ONCE(pc->time_enabled); +		count->run = READ_ONCE(pc->time_running); + +		if (pc->cap_user_time && count->ena != count->run) { +			cyc = read_timestamp(); +			time_mult = READ_ONCE(pc->time_mult); +			time_shift = READ_ONCE(pc->time_shift); +			time_offset = READ_ONCE(pc->time_offset); + +			if (pc->cap_user_time_short) { +				time_cycles = READ_ONCE(pc->time_cycles); +				time_mask = READ_ONCE(pc->time_mask); +			} +		} + +		idx = READ_ONCE(pc->index); +		cnt = READ_ONCE(pc->offset); +		if (pc->cap_user_rdpmc && idx) { +			s64 evcnt = read_perf_counter(idx - 1); +			u16 width = READ_ONCE(pc->pmc_width); + +			evcnt <<= 64 - width; +			evcnt >>= 64 - width; +			cnt += evcnt; +		} else +			return -1; + +		barrier(); +	} while (READ_ONCE(pc->lock) != seq); + +	if (count->ena != count->run) { +		u64 delta; + +		/* Adjust for cap_usr_time_short, a nop if not */ +		cyc = time_cycles + ((cyc - time_cycles) & time_mask); + +		delta = time_offset + mul_u64_u32_shr(cyc, time_mult, time_shift); + +		count->ena += delta; +		if (idx) +			count->run += delta; + +		cnt = mul_u64_u64_div64(cnt, count->ena, count->run); +	} + +	count->val = cnt; + +	return 0; +} | 
