Star64_linux/tools/perf/arch/x86/tests/rdpmc.c
Arnaldo Carvalho de Melo c8b5f2c96d tools: Introduce str_error_r()
The tools so far have been using the strerror_r() GNU variant, that
returns a string, be it the buffer passed or something else.

But that, besides being tricky in cases where we expect that the
function using strerror_r() returns the error formatted in a provided
buffer (we have to check if it returned something else and copy that
instead), breaks the build on systems not using glibc, like Alpine
Linux, where musl libc is used.

So, introduce yet another wrapper, str_error_r(), that has the GNU
interface, but uses the portable XSI variant of strerror_r(), so that
users rest asured that the provided buffer is used and it is what is
returned.

Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-d4t42fnf48ytlk8rjxs822tf@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-07-12 15:19:47 -03:00

175 lines
3.2 KiB
C

#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/mman.h>
#include <linux/types.h>
#include "perf.h"
#include "debug.h"
#include "tests/tests.h"
#include "cloexec.h"
#include "arch-tests.h"
static u64 rdpmc(unsigned int counter)
{
unsigned int low, high;
asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter));
return low | ((u64)high) << 32;
}
static u64 rdtsc(void)
{
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
return low | ((u64)high) << 32;
}
static u64 mmap_read_self(void *addr)
{
struct perf_event_mmap_page *pc = addr;
u32 seq, idx, time_mult = 0, time_shift = 0;
u64 count, cyc = 0, time_offset = 0, enabled, running, delta;
do {
seq = pc->lock;
barrier();
enabled = pc->time_enabled;
running = pc->time_running;
if (enabled != running) {
cyc = rdtsc();
time_mult = pc->time_mult;
time_shift = pc->time_shift;
time_offset = pc->time_offset;
}
idx = pc->index;
count = pc->offset;
if (idx)
count += rdpmc(idx - 1);
barrier();
} while (pc->lock != seq);
if (enabled != running) {
u64 quot, rem;
quot = (cyc >> time_shift);
rem = cyc & (((u64)1 << time_shift) - 1);
delta = time_offset + quot * time_mult +
((rem * time_mult) >> time_shift);
enabled += delta;
if (idx)
running += delta;
quot = count / running;
rem = count % running;
count = quot * enabled + (rem * enabled) / running;
}
return count;
}
/*
* If the RDPMC instruction faults then signal this back to the test parent task:
*/
static void segfault_handler(int sig __maybe_unused,
siginfo_t *info __maybe_unused,
void *uc __maybe_unused)
{
exit(-1);
}
static int __test__rdpmc(void)
{
volatile int tmp = 0;
u64 i, loops = 1000;
int n;
int fd;
void *addr;
struct perf_event_attr attr = {
.type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_INSTRUCTIONS,
.exclude_kernel = 1,
};
u64 delta_sum = 0;
struct sigaction sa;
char sbuf[STRERR_BUFSIZE];
sigfillset(&sa.sa_mask);
sa.sa_sigaction = segfault_handler;
sa.sa_flags = 0;
sigaction(SIGSEGV, &sa, NULL);
fd = sys_perf_event_open(&attr, 0, -1, -1,
perf_event_open_cloexec_flag());
if (fd < 0) {
pr_err("Error: sys_perf_event_open() syscall returned "
"with %d (%s)\n", fd,
str_error_r(errno, sbuf, sizeof(sbuf)));
return -1;
}
addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
if (addr == (void *)(-1)) {
pr_err("Error: mmap() syscall returned with (%s)\n",
str_error_r(errno, sbuf, sizeof(sbuf)));
goto out_close;
}
for (n = 0; n < 6; n++) {
u64 stamp, now, delta;
stamp = mmap_read_self(addr);
for (i = 0; i < loops; i++)
tmp++;
now = mmap_read_self(addr);
loops *= 10;
delta = now - stamp;
pr_debug("%14d: %14Lu\n", n, (long long)delta);
delta_sum += delta;
}
munmap(addr, page_size);
pr_debug(" ");
out_close:
close(fd);
if (!delta_sum)
return -1;
return 0;
}
int test__rdpmc(int subtest __maybe_unused)
{
int status = 0;
int wret = 0;
int ret;
int pid;
pid = fork();
if (pid < 0)
return -1;
if (!pid) {
ret = __test__rdpmc();
exit(ret);
}
wret = waitpid(pid, &status, 0);
if (wret < 0 || status)
return -1;
return 0;
}