18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * perf events self profiling example test case for hw breakpoints.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This tests perf PERF_TYPE_BREAKPOINT parameters
68c2ecf20Sopenharmony_ci * 1) tests all variants of the break on read/write flags
78c2ecf20Sopenharmony_ci * 2) tests exclude_user == 0 and 1
88c2ecf20Sopenharmony_ci * 3) test array matches (if DAWR is supported))
98c2ecf20Sopenharmony_ci * 4) test different numbers of breakpoints matches
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Configure this breakpoint, then read and write the data a number of
128c2ecf20Sopenharmony_ci * times. Then check the output count from perf is as expected.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Based on:
158c2ecf20Sopenharmony_ci *   http://ozlabs.org/~anton/junkcode/perf_events_example1.c
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Copyright (C) 2018 Michael Neuling, IBM Corporation.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <unistd.h>
218c2ecf20Sopenharmony_ci#include <assert.h>
228c2ecf20Sopenharmony_ci#include <stdio.h>
238c2ecf20Sopenharmony_ci#include <stdlib.h>
248c2ecf20Sopenharmony_ci#include <string.h>
258c2ecf20Sopenharmony_ci#include <sys/ioctl.h>
268c2ecf20Sopenharmony_ci#include <elf.h>
278c2ecf20Sopenharmony_ci#include <pthread.h>
288c2ecf20Sopenharmony_ci#include <sys/syscall.h>
298c2ecf20Sopenharmony_ci#include <linux/perf_event.h>
308c2ecf20Sopenharmony_ci#include <linux/hw_breakpoint.h>
318c2ecf20Sopenharmony_ci#include "utils.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define MAX_LOOPS 10000
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define DAWR_LENGTH_MAX ((0x3f + 1) * 8)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic inline int sys_perf_event_open(struct perf_event_attr *attr, pid_t pid,
388c2ecf20Sopenharmony_ci				      int cpu, int group_fd,
398c2ecf20Sopenharmony_ci				      unsigned long flags)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	attr->size = sizeof(*attr);
428c2ecf20Sopenharmony_ci	return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic inline bool breakpoint_test(int len)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct perf_event_attr attr;
488c2ecf20Sopenharmony_ci	int fd;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/* setup counters */
518c2ecf20Sopenharmony_ci	memset(&attr, 0, sizeof(attr));
528c2ecf20Sopenharmony_ci	attr.disabled = 1;
538c2ecf20Sopenharmony_ci	attr.type = PERF_TYPE_BREAKPOINT;
548c2ecf20Sopenharmony_ci	attr.bp_type = HW_BREAKPOINT_R;
558c2ecf20Sopenharmony_ci	/* bp_addr can point anywhere but needs to be aligned */
568c2ecf20Sopenharmony_ci	attr.bp_addr = (__u64)(&attr) & 0xfffffffffffff800;
578c2ecf20Sopenharmony_ci	attr.bp_len = len;
588c2ecf20Sopenharmony_ci	fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
598c2ecf20Sopenharmony_ci	if (fd < 0)
608c2ecf20Sopenharmony_ci		return false;
618c2ecf20Sopenharmony_ci	close(fd);
628c2ecf20Sopenharmony_ci	return true;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic inline bool perf_breakpoint_supported(void)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	return breakpoint_test(4);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic inline bool dawr_supported(void)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	return breakpoint_test(DAWR_LENGTH_MAX);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic int runtestsingle(int readwriteflag, int exclude_user, int arraytest)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	int i,j;
788c2ecf20Sopenharmony_ci	struct perf_event_attr attr;
798c2ecf20Sopenharmony_ci	size_t res;
808c2ecf20Sopenharmony_ci	unsigned long long breaks, needed;
818c2ecf20Sopenharmony_ci	int readint;
828c2ecf20Sopenharmony_ci	int readintarraybig[2*DAWR_LENGTH_MAX/sizeof(int)];
838c2ecf20Sopenharmony_ci	int *readintalign;
848c2ecf20Sopenharmony_ci	volatile int *ptr;
858c2ecf20Sopenharmony_ci	int break_fd;
868c2ecf20Sopenharmony_ci	int loop_num = MAX_LOOPS - (rand() % 100); /* provide some variability */
878c2ecf20Sopenharmony_ci	volatile int *k;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* align to 0x400 boundary as required by DAWR */
908c2ecf20Sopenharmony_ci	readintalign = (int *)(((unsigned long)readintarraybig + 0x7ff) &
918c2ecf20Sopenharmony_ci			       0xfffffffffffff800);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	ptr = &readint;
948c2ecf20Sopenharmony_ci	if (arraytest)
958c2ecf20Sopenharmony_ci		ptr = &readintalign[0];
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* setup counters */
988c2ecf20Sopenharmony_ci	memset(&attr, 0, sizeof(attr));
998c2ecf20Sopenharmony_ci	attr.disabled = 1;
1008c2ecf20Sopenharmony_ci	attr.type = PERF_TYPE_BREAKPOINT;
1018c2ecf20Sopenharmony_ci	attr.bp_type = readwriteflag;
1028c2ecf20Sopenharmony_ci	attr.bp_addr = (__u64)ptr;
1038c2ecf20Sopenharmony_ci	attr.bp_len = sizeof(int);
1048c2ecf20Sopenharmony_ci	if (arraytest)
1058c2ecf20Sopenharmony_ci		attr.bp_len = DAWR_LENGTH_MAX;
1068c2ecf20Sopenharmony_ci	attr.exclude_user = exclude_user;
1078c2ecf20Sopenharmony_ci	break_fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
1088c2ecf20Sopenharmony_ci	if (break_fd < 0) {
1098c2ecf20Sopenharmony_ci		perror("sys_perf_event_open");
1108c2ecf20Sopenharmony_ci		exit(1);
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/* start counters */
1148c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* Test a bunch of reads and writes */
1178c2ecf20Sopenharmony_ci	k = &readint;
1188c2ecf20Sopenharmony_ci	for (i = 0; i < loop_num; i++) {
1198c2ecf20Sopenharmony_ci		if (arraytest)
1208c2ecf20Sopenharmony_ci			k = &(readintalign[i % (DAWR_LENGTH_MAX/sizeof(int))]);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		j = *k;
1238c2ecf20Sopenharmony_ci		*k = j;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* stop counters */
1278c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* read and check counters */
1308c2ecf20Sopenharmony_ci	res = read(break_fd, &breaks, sizeof(unsigned long long));
1318c2ecf20Sopenharmony_ci	assert(res == sizeof(unsigned long long));
1328c2ecf20Sopenharmony_ci	/* we read and write each loop, so subtract the ones we are counting */
1338c2ecf20Sopenharmony_ci	needed = 0;
1348c2ecf20Sopenharmony_ci	if (readwriteflag & HW_BREAKPOINT_R)
1358c2ecf20Sopenharmony_ci		needed += loop_num;
1368c2ecf20Sopenharmony_ci	if (readwriteflag & HW_BREAKPOINT_W)
1378c2ecf20Sopenharmony_ci		needed += loop_num;
1388c2ecf20Sopenharmony_ci	needed = needed * (1 - exclude_user);
1398c2ecf20Sopenharmony_ci	printf("TESTED: addr:0x%lx brks:% 8lld loops:% 8i rw:%i !user:%i array:%i\n",
1408c2ecf20Sopenharmony_ci	       (unsigned long int)ptr, breaks, loop_num, readwriteflag, exclude_user, arraytest);
1418c2ecf20Sopenharmony_ci	if (breaks != needed) {
1428c2ecf20Sopenharmony_ci		printf("FAILED: 0x%lx brks:%lld needed:%lli %i %i %i\n\n",
1438c2ecf20Sopenharmony_ci		       (unsigned long int)ptr, breaks, needed, loop_num, readwriteflag, exclude_user);
1448c2ecf20Sopenharmony_ci		return 1;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci	close(break_fd);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return 0;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int runtest_dar_outside(void)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	void *target;
1548c2ecf20Sopenharmony_ci	volatile __u16 temp16;
1558c2ecf20Sopenharmony_ci	volatile __u64 temp64;
1568c2ecf20Sopenharmony_ci	struct perf_event_attr attr;
1578c2ecf20Sopenharmony_ci	int break_fd;
1588c2ecf20Sopenharmony_ci	unsigned long long breaks;
1598c2ecf20Sopenharmony_ci	int fail = 0;
1608c2ecf20Sopenharmony_ci	size_t res;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	target = malloc(8);
1638c2ecf20Sopenharmony_ci	if (!target) {
1648c2ecf20Sopenharmony_ci		perror("malloc failed");
1658c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	/* setup counters */
1698c2ecf20Sopenharmony_ci	memset(&attr, 0, sizeof(attr));
1708c2ecf20Sopenharmony_ci	attr.disabled = 1;
1718c2ecf20Sopenharmony_ci	attr.type = PERF_TYPE_BREAKPOINT;
1728c2ecf20Sopenharmony_ci	attr.exclude_kernel = 1;
1738c2ecf20Sopenharmony_ci	attr.exclude_hv = 1;
1748c2ecf20Sopenharmony_ci	attr.exclude_guest = 1;
1758c2ecf20Sopenharmony_ci	attr.bp_type = HW_BREAKPOINT_RW;
1768c2ecf20Sopenharmony_ci	/* watch middle half of target array */
1778c2ecf20Sopenharmony_ci	attr.bp_addr = (__u64)(target + 2);
1788c2ecf20Sopenharmony_ci	attr.bp_len = 4;
1798c2ecf20Sopenharmony_ci	break_fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
1808c2ecf20Sopenharmony_ci	if (break_fd < 0) {
1818c2ecf20Sopenharmony_ci		free(target);
1828c2ecf20Sopenharmony_ci		perror("sys_perf_event_open");
1838c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* Shouldn't hit. */
1878c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_RESET);
1888c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
1898c2ecf20Sopenharmony_ci	temp16 = *((__u16 *)target);
1908c2ecf20Sopenharmony_ci	*((__u16 *)target) = temp16;
1918c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
1928c2ecf20Sopenharmony_ci	res = read(break_fd, &breaks, sizeof(unsigned long long));
1938c2ecf20Sopenharmony_ci	assert(res == sizeof(unsigned long long));
1948c2ecf20Sopenharmony_ci	if (breaks == 0) {
1958c2ecf20Sopenharmony_ci		printf("TESTED: No overlap\n");
1968c2ecf20Sopenharmony_ci	} else {
1978c2ecf20Sopenharmony_ci		printf("FAILED: No overlap: %lld != 0\n", breaks);
1988c2ecf20Sopenharmony_ci		fail = 1;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* Hit */
2028c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_RESET);
2038c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
2048c2ecf20Sopenharmony_ci	temp16 = *((__u16 *)(target + 1));
2058c2ecf20Sopenharmony_ci	*((__u16 *)(target + 1)) = temp16;
2068c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
2078c2ecf20Sopenharmony_ci	res = read(break_fd, &breaks, sizeof(unsigned long long));
2088c2ecf20Sopenharmony_ci	assert(res == sizeof(unsigned long long));
2098c2ecf20Sopenharmony_ci	if (breaks == 2) {
2108c2ecf20Sopenharmony_ci		printf("TESTED: Partial overlap\n");
2118c2ecf20Sopenharmony_ci	} else {
2128c2ecf20Sopenharmony_ci		printf("FAILED: Partial overlap: %lld != 2\n", breaks);
2138c2ecf20Sopenharmony_ci		fail = 1;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/* Hit */
2178c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_RESET);
2188c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
2198c2ecf20Sopenharmony_ci	temp16 = *((__u16 *)(target + 5));
2208c2ecf20Sopenharmony_ci	*((__u16 *)(target + 5)) = temp16;
2218c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
2228c2ecf20Sopenharmony_ci	res = read(break_fd, &breaks, sizeof(unsigned long long));
2238c2ecf20Sopenharmony_ci	assert(res == sizeof(unsigned long long));
2248c2ecf20Sopenharmony_ci	if (breaks == 2) {
2258c2ecf20Sopenharmony_ci		printf("TESTED: Partial overlap\n");
2268c2ecf20Sopenharmony_ci	} else {
2278c2ecf20Sopenharmony_ci		printf("FAILED: Partial overlap: %lld != 2\n", breaks);
2288c2ecf20Sopenharmony_ci		fail = 1;
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/* Shouldn't Hit */
2328c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_RESET);
2338c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
2348c2ecf20Sopenharmony_ci	temp16 = *((__u16 *)(target + 6));
2358c2ecf20Sopenharmony_ci	*((__u16 *)(target + 6)) = temp16;
2368c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
2378c2ecf20Sopenharmony_ci	res = read(break_fd, &breaks, sizeof(unsigned long long));
2388c2ecf20Sopenharmony_ci	assert(res == sizeof(unsigned long long));
2398c2ecf20Sopenharmony_ci	if (breaks == 0) {
2408c2ecf20Sopenharmony_ci		printf("TESTED: No overlap\n");
2418c2ecf20Sopenharmony_ci	} else {
2428c2ecf20Sopenharmony_ci		printf("FAILED: No overlap: %lld != 0\n", breaks);
2438c2ecf20Sopenharmony_ci		fail = 1;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/* Hit */
2478c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_RESET);
2488c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_ENABLE);
2498c2ecf20Sopenharmony_ci	temp64 = *((__u64 *)target);
2508c2ecf20Sopenharmony_ci	*((__u64 *)target) = temp64;
2518c2ecf20Sopenharmony_ci	ioctl(break_fd, PERF_EVENT_IOC_DISABLE);
2528c2ecf20Sopenharmony_ci	res = read(break_fd, &breaks, sizeof(unsigned long long));
2538c2ecf20Sopenharmony_ci	assert(res == sizeof(unsigned long long));
2548c2ecf20Sopenharmony_ci	if (breaks == 2) {
2558c2ecf20Sopenharmony_ci		printf("TESTED: Full overlap\n");
2568c2ecf20Sopenharmony_ci	} else {
2578c2ecf20Sopenharmony_ci		printf("FAILED: Full overlap: %lld != 2\n", breaks);
2588c2ecf20Sopenharmony_ci		fail = 1;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	free(target);
2628c2ecf20Sopenharmony_ci	close(break_fd);
2638c2ecf20Sopenharmony_ci	return fail;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic int runtest(void)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	int rwflag;
2698c2ecf20Sopenharmony_ci	int exclude_user;
2708c2ecf20Sopenharmony_ci	int ret;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	/*
2738c2ecf20Sopenharmony_ci	 * perf defines rwflag as two bits read and write and at least
2748c2ecf20Sopenharmony_ci	 * one must be set.  So range 1-3.
2758c2ecf20Sopenharmony_ci	 */
2768c2ecf20Sopenharmony_ci	for (rwflag = 1 ; rwflag < 4; rwflag++) {
2778c2ecf20Sopenharmony_ci		for (exclude_user = 0 ; exclude_user < 2; exclude_user++) {
2788c2ecf20Sopenharmony_ci			ret = runtestsingle(rwflag, exclude_user, 0);
2798c2ecf20Sopenharmony_ci			if (ret)
2808c2ecf20Sopenharmony_ci				return ret;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci			/* if we have the dawr, we can do an array test */
2838c2ecf20Sopenharmony_ci			if (!dawr_supported())
2848c2ecf20Sopenharmony_ci				continue;
2858c2ecf20Sopenharmony_ci			ret = runtestsingle(rwflag, exclude_user, 1);
2868c2ecf20Sopenharmony_ci			if (ret)
2878c2ecf20Sopenharmony_ci				return ret;
2888c2ecf20Sopenharmony_ci		}
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	ret = runtest_dar_outside();
2928c2ecf20Sopenharmony_ci	return ret;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic int perf_hwbreak(void)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	srand ( time(NULL) );
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	SKIP_IF(!perf_breakpoint_supported());
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	return runtest();
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ciint main(int argc, char *argv[], char **envp)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	return test_harness(perf_hwbreak, "perf_hwbreak");
3088c2ecf20Sopenharmony_ci}
309