/tools/perf/bench/breakpoint.c

https://gitlab.com/deepcypher/linux · C · 244 lines · 215 code · 23 blank · 6 comment · 29 complexity · 1664a3cdd5b07ae7dc72cb86d626db58 MD5 · raw file

  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <subcmd/parse-options.h>
  3. #include <linux/hw_breakpoint.h>
  4. #include <linux/perf_event.h>
  5. #include <linux/time64.h>
  6. #include <sys/syscall.h>
  7. #include <sys/ioctl.h>
  8. #include <sys/time.h>
  9. #include <pthread.h>
  10. #include <stddef.h>
  11. #include <stdlib.h>
  12. #include <unistd.h>
  13. #include <stdio.h>
  14. #include <errno.h>
  15. #include "bench.h"
  16. #include "futex.h"
  17. struct {
  18. unsigned int nbreakpoints;
  19. unsigned int nparallel;
  20. unsigned int nthreads;
  21. } thread_params = {
  22. .nbreakpoints = 1,
  23. .nparallel = 1,
  24. .nthreads = 1,
  25. };
  26. static const struct option thread_options[] = {
  27. OPT_UINTEGER('b', "breakpoints", &thread_params.nbreakpoints,
  28. "Specify amount of breakpoints"),
  29. OPT_UINTEGER('p', "parallelism", &thread_params.nparallel, "Specify amount of parallelism"),
  30. OPT_UINTEGER('t', "threads", &thread_params.nthreads, "Specify amount of threads"),
  31. OPT_END()
  32. };
  33. static const char * const thread_usage[] = {
  34. "perf bench breakpoint thread <options>",
  35. NULL
  36. };
  37. struct breakpoint {
  38. int fd;
  39. char watched;
  40. };
  41. static int breakpoint_setup(void *addr)
  42. {
  43. struct perf_event_attr attr = { .size = 0, };
  44. attr.type = PERF_TYPE_BREAKPOINT;
  45. attr.size = sizeof(attr);
  46. attr.inherit = 1;
  47. attr.exclude_kernel = 1;
  48. attr.exclude_hv = 1;
  49. attr.bp_addr = (unsigned long)addr;
  50. attr.bp_type = HW_BREAKPOINT_RW;
  51. attr.bp_len = HW_BREAKPOINT_LEN_1;
  52. return syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0);
  53. }
  54. static void *passive_thread(void *arg)
  55. {
  56. unsigned int *done = (unsigned int *)arg;
  57. while (!__atomic_load_n(done, __ATOMIC_RELAXED))
  58. futex_wait(done, 0, NULL, 0);
  59. return NULL;
  60. }
  61. static void *active_thread(void *arg)
  62. {
  63. unsigned int *done = (unsigned int *)arg;
  64. while (!__atomic_load_n(done, __ATOMIC_RELAXED));
  65. return NULL;
  66. }
  67. static void *breakpoint_thread(void *arg)
  68. {
  69. unsigned int i, done;
  70. int *repeat = (int *)arg;
  71. pthread_t *threads;
  72. threads = calloc(thread_params.nthreads, sizeof(threads[0]));
  73. if (!threads)
  74. exit((perror("calloc"), EXIT_FAILURE));
  75. while (__atomic_fetch_sub(repeat, 1, __ATOMIC_RELAXED) > 0) {
  76. done = 0;
  77. for (i = 0; i < thread_params.nthreads; i++) {
  78. if (pthread_create(&threads[i], NULL, passive_thread, &done))
  79. exit((perror("pthread_create"), EXIT_FAILURE));
  80. }
  81. __atomic_store_n(&done, 1, __ATOMIC_RELAXED);
  82. futex_wake(&done, thread_params.nthreads, 0);
  83. for (i = 0; i < thread_params.nthreads; i++)
  84. pthread_join(threads[i], NULL);
  85. }
  86. free(threads);
  87. return NULL;
  88. }
  89. // The benchmark creates nbreakpoints inheritable breakpoints,
  90. // then starts nparallel threads which create and join bench_repeat batches of nthreads threads.
  91. int bench_breakpoint_thread(int argc, const char **argv)
  92. {
  93. unsigned int i, result_usec;
  94. int repeat = bench_repeat;
  95. struct breakpoint *breakpoints;
  96. pthread_t *parallel;
  97. struct timeval start, stop, diff;
  98. if (parse_options(argc, argv, thread_options, thread_usage, 0)) {
  99. usage_with_options(thread_usage, thread_options);
  100. exit(EXIT_FAILURE);
  101. }
  102. breakpoints = calloc(thread_params.nbreakpoints, sizeof(breakpoints[0]));
  103. parallel = calloc(thread_params.nparallel, sizeof(parallel[0]));
  104. if (!breakpoints || !parallel)
  105. exit((perror("calloc"), EXIT_FAILURE));
  106. for (i = 0; i < thread_params.nbreakpoints; i++) {
  107. breakpoints[i].fd = breakpoint_setup(&breakpoints[i].watched);
  108. if (breakpoints[i].fd == -1)
  109. exit((perror("perf_event_open"), EXIT_FAILURE));
  110. }
  111. gettimeofday(&start, NULL);
  112. for (i = 0; i < thread_params.nparallel; i++) {
  113. if (pthread_create(&parallel[i], NULL, breakpoint_thread, &repeat))
  114. exit((perror("pthread_create"), EXIT_FAILURE));
  115. }
  116. for (i = 0; i < thread_params.nparallel; i++)
  117. pthread_join(parallel[i], NULL);
  118. gettimeofday(&stop, NULL);
  119. timersub(&stop, &start, &diff);
  120. for (i = 0; i < thread_params.nbreakpoints; i++)
  121. close(breakpoints[i].fd);
  122. free(parallel);
  123. free(breakpoints);
  124. switch (bench_format) {
  125. case BENCH_FORMAT_DEFAULT:
  126. printf("# Created/joined %d threads with %d breakpoints and %d parallelism\n",
  127. bench_repeat, thread_params.nbreakpoints, thread_params.nparallel);
  128. printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
  129. (long)diff.tv_sec, (long)(diff.tv_usec / USEC_PER_MSEC));
  130. result_usec = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
  131. printf(" %14lf usecs/op\n",
  132. (double)result_usec / bench_repeat / thread_params.nthreads);
  133. printf(" %14lf usecs/op/cpu\n",
  134. (double)result_usec / bench_repeat /
  135. thread_params.nthreads * thread_params.nparallel);
  136. break;
  137. case BENCH_FORMAT_SIMPLE:
  138. printf("%lu.%03lu\n", (long)diff.tv_sec, (long)(diff.tv_usec / USEC_PER_MSEC));
  139. break;
  140. default:
  141. fprintf(stderr, "Unknown format: %d\n", bench_format);
  142. exit(EXIT_FAILURE);
  143. }
  144. return 0;
  145. }
  146. struct {
  147. unsigned int npassive;
  148. unsigned int nactive;
  149. } enable_params = {
  150. .nactive = 0,
  151. .npassive = 0,
  152. };
  153. static const struct option enable_options[] = {
  154. OPT_UINTEGER('p', "passive", &enable_params.npassive, "Specify amount of passive threads"),
  155. OPT_UINTEGER('a', "active", &enable_params.nactive, "Specify amount of active threads"),
  156. OPT_END()
  157. };
  158. static const char * const enable_usage[] = {
  159. "perf bench breakpoint enable <options>",
  160. NULL
  161. };
  162. // The benchmark creates an inheritable breakpoint,
  163. // then starts npassive threads that block and nactive threads that actively spin
  164. // and then disables and enables the breakpoint bench_repeat times.
  165. int bench_breakpoint_enable(int argc, const char **argv)
  166. {
  167. unsigned int i, nthreads, result_usec, done = 0;
  168. char watched;
  169. int fd;
  170. pthread_t *threads;
  171. struct timeval start, stop, diff;
  172. if (parse_options(argc, argv, enable_options, enable_usage, 0)) {
  173. usage_with_options(enable_usage, enable_options);
  174. exit(EXIT_FAILURE);
  175. }
  176. fd = breakpoint_setup(&watched);
  177. if (fd == -1)
  178. exit((perror("perf_event_open"), EXIT_FAILURE));
  179. nthreads = enable_params.npassive + enable_params.nactive;
  180. threads = calloc(nthreads, sizeof(threads[0]));
  181. if (!threads)
  182. exit((perror("calloc"), EXIT_FAILURE));
  183. for (i = 0; i < nthreads; i++) {
  184. if (pthread_create(&threads[i], NULL,
  185. i < enable_params.npassive ? passive_thread : active_thread, &done))
  186. exit((perror("pthread_create"), EXIT_FAILURE));
  187. }
  188. usleep(10000); // let the threads block
  189. gettimeofday(&start, NULL);
  190. for (i = 0; i < bench_repeat; i++) {
  191. if (ioctl(fd, PERF_EVENT_IOC_DISABLE, 0))
  192. exit((perror("ioctl(PERF_EVENT_IOC_DISABLE)"), EXIT_FAILURE));
  193. if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0))
  194. exit((perror("ioctl(PERF_EVENT_IOC_ENABLE)"), EXIT_FAILURE));
  195. }
  196. gettimeofday(&stop, NULL);
  197. timersub(&stop, &start, &diff);
  198. __atomic_store_n(&done, 1, __ATOMIC_RELAXED);
  199. futex_wake(&done, enable_params.npassive, 0);
  200. for (i = 0; i < nthreads; i++)
  201. pthread_join(threads[i], NULL);
  202. free(threads);
  203. close(fd);
  204. switch (bench_format) {
  205. case BENCH_FORMAT_DEFAULT:
  206. printf("# Enabled/disabled breakpoint %d time with %d passive and %d active threads\n",
  207. bench_repeat, enable_params.npassive, enable_params.nactive);
  208. printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
  209. (long)diff.tv_sec, (long)(diff.tv_usec / USEC_PER_MSEC));
  210. result_usec = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
  211. printf(" %14lf usecs/op\n", (double)result_usec / bench_repeat);
  212. break;
  213. case BENCH_FORMAT_SIMPLE:
  214. printf("%lu.%03lu\n", (long)diff.tv_sec, (long)(diff.tv_usec / USEC_PER_MSEC));
  215. break;
  216. default:
  217. fprintf(stderr, "Unknown format: %d\n", bench_format);
  218. exit(EXIT_FAILURE);
  219. }
  220. return 0;
  221. }