0001
0002
0003 #include <subcmd/parse-options.h>
0004 #include <linux/hw_breakpoint.h>
0005 #include <linux/perf_event.h>
0006 #include <linux/time64.h>
0007 #include <sys/syscall.h>
0008 #include <sys/ioctl.h>
0009 #include <sys/time.h>
0010 #include <pthread.h>
0011 #include <stddef.h>
0012 #include <stdlib.h>
0013 #include <unistd.h>
0014 #include <stdio.h>
0015 #include <errno.h>
0016 #include "bench.h"
0017 #include "futex.h"
0018
0019 struct {
0020 unsigned int nbreakpoints;
0021 unsigned int nparallel;
0022 unsigned int nthreads;
0023 } thread_params = {
0024 .nbreakpoints = 1,
0025 .nparallel = 1,
0026 .nthreads = 1,
0027 };
0028
0029 static const struct option thread_options[] = {
0030 OPT_UINTEGER('b', "breakpoints", &thread_params.nbreakpoints,
0031 "Specify amount of breakpoints"),
0032 OPT_UINTEGER('p', "parallelism", &thread_params.nparallel, "Specify amount of parallelism"),
0033 OPT_UINTEGER('t', "threads", &thread_params.nthreads, "Specify amount of threads"),
0034 OPT_END()
0035 };
0036
0037 static const char * const thread_usage[] = {
0038 "perf bench breakpoint thread <options>",
0039 NULL
0040 };
0041
0042 struct breakpoint {
0043 int fd;
0044 char watched;
0045 };
0046
0047 static int breakpoint_setup(void *addr)
0048 {
0049 struct perf_event_attr attr = { .size = 0, };
0050
0051 attr.type = PERF_TYPE_BREAKPOINT;
0052 attr.size = sizeof(attr);
0053 attr.inherit = 1;
0054 attr.exclude_kernel = 1;
0055 attr.exclude_hv = 1;
0056 attr.bp_addr = (unsigned long)addr;
0057 attr.bp_type = HW_BREAKPOINT_RW;
0058 attr.bp_len = HW_BREAKPOINT_LEN_1;
0059 return syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0);
0060 }
0061
0062 static void *passive_thread(void *arg)
0063 {
0064 unsigned int *done = (unsigned int *)arg;
0065
0066 while (!__atomic_load_n(done, __ATOMIC_RELAXED))
0067 futex_wait(done, 0, NULL, 0);
0068 return NULL;
0069 }
0070
0071 static void *active_thread(void *arg)
0072 {
0073 unsigned int *done = (unsigned int *)arg;
0074
0075 while (!__atomic_load_n(done, __ATOMIC_RELAXED));
0076 return NULL;
0077 }
0078
0079 static void *breakpoint_thread(void *arg)
0080 {
0081 unsigned int i, done;
0082 int *repeat = (int *)arg;
0083 pthread_t *threads;
0084
0085 threads = calloc(thread_params.nthreads, sizeof(threads[0]));
0086 if (!threads)
0087 exit((perror("calloc"), EXIT_FAILURE));
0088
0089 while (__atomic_fetch_sub(repeat, 1, __ATOMIC_RELAXED) > 0) {
0090 done = 0;
0091 for (i = 0; i < thread_params.nthreads; i++) {
0092 if (pthread_create(&threads[i], NULL, passive_thread, &done))
0093 exit((perror("pthread_create"), EXIT_FAILURE));
0094 }
0095 __atomic_store_n(&done, 1, __ATOMIC_RELAXED);
0096 futex_wake(&done, thread_params.nthreads, 0);
0097 for (i = 0; i < thread_params.nthreads; i++)
0098 pthread_join(threads[i], NULL);
0099 }
0100 free(threads);
0101 return NULL;
0102 }
0103
0104
0105
0106 int bench_breakpoint_thread(int argc, const char **argv)
0107 {
0108 unsigned int i, result_usec;
0109 int repeat = bench_repeat;
0110 struct breakpoint *breakpoints;
0111 pthread_t *parallel;
0112 struct timeval start, stop, diff;
0113
0114 if (parse_options(argc, argv, thread_options, thread_usage, 0)) {
0115 usage_with_options(thread_usage, thread_options);
0116 exit(EXIT_FAILURE);
0117 }
0118 breakpoints = calloc(thread_params.nbreakpoints, sizeof(breakpoints[0]));
0119 parallel = calloc(thread_params.nparallel, sizeof(parallel[0]));
0120 if (!breakpoints || !parallel)
0121 exit((perror("calloc"), EXIT_FAILURE));
0122
0123 for (i = 0; i < thread_params.nbreakpoints; i++) {
0124 breakpoints[i].fd = breakpoint_setup(&breakpoints[i].watched);
0125 if (breakpoints[i].fd == -1)
0126 exit((perror("perf_event_open"), EXIT_FAILURE));
0127 }
0128 gettimeofday(&start, NULL);
0129 for (i = 0; i < thread_params.nparallel; i++) {
0130 if (pthread_create(¶llel[i], NULL, breakpoint_thread, &repeat))
0131 exit((perror("pthread_create"), EXIT_FAILURE));
0132 }
0133 for (i = 0; i < thread_params.nparallel; i++)
0134 pthread_join(parallel[i], NULL);
0135 gettimeofday(&stop, NULL);
0136 timersub(&stop, &start, &diff);
0137 for (i = 0; i < thread_params.nbreakpoints; i++)
0138 close(breakpoints[i].fd);
0139 free(parallel);
0140 free(breakpoints);
0141 switch (bench_format) {
0142 case BENCH_FORMAT_DEFAULT:
0143 printf("# Created/joined %d threads with %d breakpoints and %d parallelism\n",
0144 bench_repeat, thread_params.nbreakpoints, thread_params.nparallel);
0145 printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
0146 (long)diff.tv_sec, (long)(diff.tv_usec / USEC_PER_MSEC));
0147 result_usec = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
0148 printf(" %14lf usecs/op\n",
0149 (double)result_usec / bench_repeat / thread_params.nthreads);
0150 printf(" %14lf usecs/op/cpu\n",
0151 (double)result_usec / bench_repeat /
0152 thread_params.nthreads * thread_params.nparallel);
0153 break;
0154 case BENCH_FORMAT_SIMPLE:
0155 printf("%lu.%03lu\n", (long)diff.tv_sec, (long)(diff.tv_usec / USEC_PER_MSEC));
0156 break;
0157 default:
0158 fprintf(stderr, "Unknown format: %d\n", bench_format);
0159 exit(EXIT_FAILURE);
0160 }
0161 return 0;
0162 }
0163
0164 struct {
0165 unsigned int npassive;
0166 unsigned int nactive;
0167 } enable_params = {
0168 .nactive = 0,
0169 .npassive = 0,
0170 };
0171
0172 static const struct option enable_options[] = {
0173 OPT_UINTEGER('p', "passive", &enable_params.npassive, "Specify amount of passive threads"),
0174 OPT_UINTEGER('a', "active", &enable_params.nactive, "Specify amount of active threads"),
0175 OPT_END()
0176 };
0177
0178 static const char * const enable_usage[] = {
0179 "perf bench breakpoint enable <options>",
0180 NULL
0181 };
0182
0183
0184
0185
0186 int bench_breakpoint_enable(int argc, const char **argv)
0187 {
0188 unsigned int i, nthreads, result_usec, done = 0;
0189 char watched;
0190 int fd;
0191 pthread_t *threads;
0192 struct timeval start, stop, diff;
0193
0194 if (parse_options(argc, argv, enable_options, enable_usage, 0)) {
0195 usage_with_options(enable_usage, enable_options);
0196 exit(EXIT_FAILURE);
0197 }
0198 fd = breakpoint_setup(&watched);
0199 if (fd == -1)
0200 exit((perror("perf_event_open"), EXIT_FAILURE));
0201 nthreads = enable_params.npassive + enable_params.nactive;
0202 threads = calloc(nthreads, sizeof(threads[0]));
0203 if (!threads)
0204 exit((perror("calloc"), EXIT_FAILURE));
0205
0206 for (i = 0; i < nthreads; i++) {
0207 if (pthread_create(&threads[i], NULL,
0208 i < enable_params.npassive ? passive_thread : active_thread, &done))
0209 exit((perror("pthread_create"), EXIT_FAILURE));
0210 }
0211 usleep(10000);
0212 gettimeofday(&start, NULL);
0213 for (i = 0; i < bench_repeat; i++) {
0214 if (ioctl(fd, PERF_EVENT_IOC_DISABLE, 0))
0215 exit((perror("ioctl(PERF_EVENT_IOC_DISABLE)"), EXIT_FAILURE));
0216 if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0))
0217 exit((perror("ioctl(PERF_EVENT_IOC_ENABLE)"), EXIT_FAILURE));
0218 }
0219 gettimeofday(&stop, NULL);
0220 timersub(&stop, &start, &diff);
0221 __atomic_store_n(&done, 1, __ATOMIC_RELAXED);
0222 futex_wake(&done, enable_params.npassive, 0);
0223 for (i = 0; i < nthreads; i++)
0224 pthread_join(threads[i], NULL);
0225 free(threads);
0226 close(fd);
0227 switch (bench_format) {
0228 case BENCH_FORMAT_DEFAULT:
0229 printf("# Enabled/disabled breakpoint %d time with %d passive and %d active threads\n",
0230 bench_repeat, enable_params.npassive, enable_params.nactive);
0231 printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
0232 (long)diff.tv_sec, (long)(diff.tv_usec / USEC_PER_MSEC));
0233 result_usec = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
0234 printf(" %14lf usecs/op\n", (double)result_usec / bench_repeat);
0235 break;
0236 case BENCH_FORMAT_SIMPLE:
0237 printf("%lu.%03lu\n", (long)diff.tv_sec, (long)(diff.tv_usec / USEC_PER_MSEC));
0238 break;
0239 default:
0240 fprintf(stderr, "Unknown format: %d\n", bench_format);
0241 exit(EXIT_FAILURE);
0242 }
0243 return 0;
0244 }