1 // SPDX-License-Identifier: GPL-2.0
3 #include <subcmd/parse-options.h>
4 #include <linux/hw_breakpoint.h>
5 #include <linux/perf_event.h>
6 #include <linux/time64.h>
7 #include <sys/syscall.h>
20 unsigned int nbreakpoints
;
21 unsigned int nparallel
;
22 unsigned int nthreads
;
29 static const struct option thread_options
[] = {
30 OPT_UINTEGER('b', "breakpoints", &thread_params
.nbreakpoints
,
31 "Specify amount of breakpoints"),
32 OPT_UINTEGER('p', "parallelism", &thread_params
.nparallel
, "Specify amount of parallelism"),
33 OPT_UINTEGER('t', "threads", &thread_params
.nthreads
, "Specify amount of threads"),
37 static const char * const thread_usage
[] = {
38 "perf bench breakpoint thread <options>",
47 static int breakpoint_setup(void *addr
)
49 struct perf_event_attr attr
= { .size
= 0, };
52 attr
.type
= PERF_TYPE_BREAKPOINT
;
53 attr
.size
= sizeof(attr
);
55 attr
.exclude_kernel
= 1;
57 attr
.bp_addr
= (unsigned long)addr
;
58 attr
.bp_type
= HW_BREAKPOINT_RW
;
59 attr
.bp_len
= HW_BREAKPOINT_LEN_1
;
60 fd
= syscall(SYS_perf_event_open
, &attr
, 0, -1, -1, 0);
68 static void *passive_thread(void *arg
)
70 unsigned int *done
= (unsigned int *)arg
;
72 while (!__atomic_load_n(done
, __ATOMIC_RELAXED
))
73 futex_wait(done
, 0, NULL
, 0);
77 static void *active_thread(void *arg
)
79 unsigned int *done
= (unsigned int *)arg
;
81 while (!__atomic_load_n(done
, __ATOMIC_RELAXED
));
85 static void *breakpoint_thread(void *arg
)
88 int *repeat
= (int *)arg
;
91 threads
= calloc(thread_params
.nthreads
, sizeof(threads
[0]));
93 exit((perror("calloc"), EXIT_FAILURE
));
95 while (__atomic_fetch_sub(repeat
, 1, __ATOMIC_RELAXED
) > 0) {
97 for (i
= 0; i
< thread_params
.nthreads
; i
++) {
98 if (pthread_create(&threads
[i
], NULL
, passive_thread
, &done
))
99 exit((perror("pthread_create"), EXIT_FAILURE
));
101 __atomic_store_n(&done
, 1, __ATOMIC_RELAXED
);
102 futex_wake(&done
, thread_params
.nthreads
, 0);
103 for (i
= 0; i
< thread_params
.nthreads
; i
++)
104 pthread_join(threads
[i
], NULL
);
110 // The benchmark creates nbreakpoints inheritable breakpoints,
111 // then starts nparallel threads which create and join bench_repeat batches of nthreads threads.
112 int bench_breakpoint_thread(int argc
, const char **argv
)
114 unsigned int i
, result_usec
;
115 int repeat
= bench_repeat
;
116 struct breakpoint
*breakpoints
;
118 struct timeval start
, stop
, diff
;
120 if (parse_options(argc
, argv
, thread_options
, thread_usage
, 0)) {
121 usage_with_options(thread_usage
, thread_options
);
124 breakpoints
= calloc(thread_params
.nbreakpoints
, sizeof(breakpoints
[0]));
125 parallel
= calloc(thread_params
.nparallel
, sizeof(parallel
[0]));
126 if (!breakpoints
|| !parallel
)
127 exit((perror("calloc"), EXIT_FAILURE
));
129 for (i
= 0; i
< thread_params
.nbreakpoints
; i
++) {
130 breakpoints
[i
].fd
= breakpoint_setup(&breakpoints
[i
].watched
);
132 if (breakpoints
[i
].fd
< 0) {
133 if (breakpoints
[i
].fd
== -ENODEV
) {
134 printf("Skipping perf bench breakpoint thread: No hardware support\n");
137 exit((perror("perf_event_open"), EXIT_FAILURE
));
140 gettimeofday(&start
, NULL
);
141 for (i
= 0; i
< thread_params
.nparallel
; i
++) {
142 if (pthread_create(¶llel
[i
], NULL
, breakpoint_thread
, &repeat
))
143 exit((perror("pthread_create"), EXIT_FAILURE
));
145 for (i
= 0; i
< thread_params
.nparallel
; i
++)
146 pthread_join(parallel
[i
], NULL
);
147 gettimeofday(&stop
, NULL
);
148 timersub(&stop
, &start
, &diff
);
149 for (i
= 0; i
< thread_params
.nbreakpoints
; i
++)
150 close(breakpoints
[i
].fd
);
153 switch (bench_format
) {
154 case BENCH_FORMAT_DEFAULT
:
155 printf("# Created/joined %d threads with %d breakpoints and %d parallelism\n",
156 bench_repeat
, thread_params
.nbreakpoints
, thread_params
.nparallel
);
157 printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
158 (long)diff
.tv_sec
, (long)(diff
.tv_usec
/ USEC_PER_MSEC
));
159 result_usec
= diff
.tv_sec
* USEC_PER_SEC
+ diff
.tv_usec
;
160 printf(" %14lf usecs/op\n",
161 (double)result_usec
/ bench_repeat
/ thread_params
.nthreads
);
162 printf(" %14lf usecs/op/cpu\n",
163 (double)result_usec
/ bench_repeat
/
164 thread_params
.nthreads
* thread_params
.nparallel
);
166 case BENCH_FORMAT_SIMPLE
:
167 printf("%lu.%03lu\n", (long)diff
.tv_sec
, (long)(diff
.tv_usec
/ USEC_PER_MSEC
));
170 fprintf(stderr
, "Unknown format: %d\n", bench_format
);
177 unsigned int npassive
;
178 unsigned int nactive
;
184 static const struct option enable_options
[] = {
185 OPT_UINTEGER('p', "passive", &enable_params
.npassive
, "Specify amount of passive threads"),
186 OPT_UINTEGER('a', "active", &enable_params
.nactive
, "Specify amount of active threads"),
190 static const char * const enable_usage
[] = {
191 "perf bench breakpoint enable <options>",
195 // The benchmark creates an inheritable breakpoint,
196 // then starts npassive threads that block and nactive threads that actively spin
197 // and then disables and enables the breakpoint bench_repeat times.
198 int bench_breakpoint_enable(int argc
, const char **argv
)
200 unsigned int i
, nthreads
, result_usec
, done
= 0;
204 struct timeval start
, stop
, diff
;
206 if (parse_options(argc
, argv
, enable_options
, enable_usage
, 0)) {
207 usage_with_options(enable_usage
, enable_options
);
210 fd
= breakpoint_setup(&watched
);
214 printf("Skipping perf bench breakpoint enable: No hardware support\n");
217 exit((perror("perf_event_open"), EXIT_FAILURE
));
219 nthreads
= enable_params
.npassive
+ enable_params
.nactive
;
220 threads
= calloc(nthreads
, sizeof(threads
[0]));
222 exit((perror("calloc"), EXIT_FAILURE
));
224 for (i
= 0; i
< nthreads
; i
++) {
225 if (pthread_create(&threads
[i
], NULL
,
226 i
< enable_params
.npassive
? passive_thread
: active_thread
, &done
))
227 exit((perror("pthread_create"), EXIT_FAILURE
));
229 usleep(10000); // let the threads block
230 gettimeofday(&start
, NULL
);
231 for (i
= 0; i
< bench_repeat
; i
++) {
232 if (ioctl(fd
, PERF_EVENT_IOC_DISABLE
, 0))
233 exit((perror("ioctl(PERF_EVENT_IOC_DISABLE)"), EXIT_FAILURE
));
234 if (ioctl(fd
, PERF_EVENT_IOC_ENABLE
, 0))
235 exit((perror("ioctl(PERF_EVENT_IOC_ENABLE)"), EXIT_FAILURE
));
237 gettimeofday(&stop
, NULL
);
238 timersub(&stop
, &start
, &diff
);
239 __atomic_store_n(&done
, 1, __ATOMIC_RELAXED
);
240 futex_wake(&done
, enable_params
.npassive
, 0);
241 for (i
= 0; i
< nthreads
; i
++)
242 pthread_join(threads
[i
], NULL
);
245 switch (bench_format
) {
246 case BENCH_FORMAT_DEFAULT
:
247 printf("# Enabled/disabled breakpoint %d time with %d passive and %d active threads\n",
248 bench_repeat
, enable_params
.npassive
, enable_params
.nactive
);
249 printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
250 (long)diff
.tv_sec
, (long)(diff
.tv_usec
/ USEC_PER_MSEC
));
251 result_usec
= diff
.tv_sec
* USEC_PER_SEC
+ diff
.tv_usec
;
252 printf(" %14lf usecs/op\n", (double)result_usec
/ bench_repeat
);
254 case BENCH_FORMAT_SIMPLE
:
255 printf("%lu.%03lu\n", (long)diff
.tv_sec
, (long)(diff
.tv_usec
/ USEC_PER_MSEC
));
258 fprintf(stderr
, "Unknown format: %d\n", bench_format
);