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