Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
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 // The benchmark creates nbreakpoints inheritable breakpoints,
0105 // then starts nparallel threads which create and join bench_repeat batches of nthreads threads.
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(&parallel[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 // The benchmark creates an inheritable breakpoint,
0184 // then starts npassive threads that block and nactive threads that actively spin
0185 // and then disables and enables the breakpoint bench_repeat times.
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);  // let the threads block
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 }