prof.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. * Prof
  3. * ====
  4. *
  5. * Self-contained C/C++ profiler library for Linux.
  6. *
  7. * Prof offers a quick way to measure performance events (CPU clock cycles,
  8. * cache misses, branch mispredictions, etc.) of C/C++ code snippets. Prof is
  9. * just a wrapper around the `perf_event_open` system call, its main goal is to
  10. * be easy to setup and painless to use for targeted optimizations, namely, when
  11. * the hot spot has already been identified. In no way Prof is a replacement for
  12. * a fully-fledged profiler like perf, gprof, callgrind, etc.
  13. *
  14. * Please be aware that Prof uses `__attribute__((constructor))` to be as more
  15. * straightforward to setup as possible, so it cannot be included more than
  16. * once.
  17. *
  18. * Examples
  19. * --------
  20. *
  21. * ### Minimal
  22. *
  23. * The following snippet prints the rough number of CPU clock cycles spent in
  24. * executing the code between the two Prof calls:
  25. *
  26. * ```c
  27. * #include "prof.h"
  28. *
  29. * int main()
  30. * {
  31. * PROF_START();
  32. * // slow code goes here...
  33. * PROF_STDOUT();
  34. * }
  35. * ```
  36. *
  37. * ### Custom options
  38. *
  39. * The following snippet instead counts both read and write faults of the level
  40. * 1 data cache that occur in the userland code between the two Prof calls:
  41. *
  42. * ```c
  43. * #include <stdio.h>
  44. *
  45. * #define PROF_USER_EVENTS_ONLY
  46. * #define PROF_EVENT_LIST \
  47. * PROF_EVENT_CACHE(L1D, READ, MISS) \
  48. * PROF_EVENT_CACHE(L1D, WRITE, MISS)
  49. * #include "prof.h"
  50. *
  51. * int main()
  52. * {
  53. * uint64_t faults[2] = { 0 };
  54. *
  55. * PROF_START();
  56. * // slow code goes here...
  57. * PROF_DO(faults[index] += counter);
  58. *
  59. * // fast or uninteresting code goes here...
  60. *
  61. * PROF_START();
  62. * // slow code goes here...
  63. * PROF_DO(faults[index] += counter);
  64. *
  65. * printf("Total L1 faults: R = %lu; W = %lu\n", faults[0], faults[1]);
  66. * }
  67. * ```
  68. *
  69. * Installation
  70. * ------------
  71. *
  72. * Just include `prof.h`. Here is a quick way to fetch the latest version:
  73. *
  74. * wget -q https://raw.githubusercontent.com/cyrus-and/prof/master/prof.h
  75. */
  76. #ifndef PROF_H
  77. #define PROF_H
  78. #include <errno.h>
  79. #include <linux/perf_event.h>
  80. #include <stdarg.h>
  81. #include <stdint.h>
  82. #include <stdio.h>
  83. #include <stdlib.h>
  84. #include <string.h>
  85. #include <sys/ioctl.h>
  86. #include <sys/syscall.h>
  87. #include <unistd.h>
  88. /*
  89. * API
  90. * ---
  91. */
  92. /*
  93. * Reset the counters and (re)start counting the events.
  94. *
  95. * The events to be monitored are specified by setting the `PROF_EVENT_LIST`
  96. * macro before including this file to a list of `PROF_EVENT_*` invocations;
  97. * defaults to counting the number CPU clock cycles.
  98. *
  99. * If the `PROF_USER_EVENTS_ONLY` macro is defined before including this file
  100. * then kernel and hypervisor events are excluded from the count.
  101. */
  102. #define PROF_START() \
  103. do { \
  104. PROF_IOCTL_(ENABLE); \
  105. PROF_IOCTL_(RESET); \
  106. } while (0)
  107. /*
  108. * Specify an event to be monitored, `type` and `config` are defined in the
  109. * documentation of the `perf_event_open` system call.
  110. */
  111. #define PROF_EVENT(type, config) \
  112. (uint32_t)(type), (uint64_t)(config),
  113. /*
  114. * Same as `PROF_EVENT` but for hardware events; prefix `PERF_COUNT_HW_` must be
  115. * omitted from `config`.
  116. */
  117. #define PROF_EVENT_HW(config) \
  118. PROF_EVENT(PERF_TYPE_HARDWARE, PERF_COUNT_HW_ ## config)
  119. /*
  120. * Same as `PROF_EVENT` but for software events; prefix `PERF_COUNT_SW_` must be
  121. * omitted from `config`.
  122. */
  123. #define PROF_EVENT_SW(config) \
  124. PROF_EVENT(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ ## config)
  125. /*
  126. * Same as `PROF_EVENT` but for cache events; prefixes `PERF_COUNT_HW_CACHE_`,
  127. * `PERF_COUNT_HW_CACHE_OP_` and `PERF_COUNT_HW_CACHE_RESULT_` must be omitted
  128. * from `cache`, `op` and `result`, respectively. Again `cache`, `op` and
  129. * `result` are defined in the documentation of the `perf_event_open` system
  130. * call.
  131. */
  132. #define PROF_EVENT_CACHE(cache, op, result) \
  133. PROF_EVENT(PERF_TYPE_HW_CACHE, \
  134. (PERF_COUNT_HW_CACHE_ ## cache) | \
  135. (PERF_COUNT_HW_CACHE_OP_ ## op << 8) | \
  136. (PERF_COUNT_HW_CACHE_RESULT_ ## result << 16))
  137. /*
  138. * Stop counting the events. The counter array can then be accessed with
  139. * `PROF_COUNTERS`.
  140. */
  141. #define PROF_STOP() \
  142. do { \
  143. PROF_IOCTL_(DISABLE); \
  144. PROF_READ_COUNTERS_(prof_event_buf_); \
  145. } while (0)
  146. /*
  147. * Access the counter array. The order of counters is the same of the events
  148. * defined in `PROF_EVENT_LIST`. Elements of this array are 64 bit unsigned
  149. * integers.
  150. */
  151. #define PROF_COUNTERS \
  152. (prof_event_buf_ + 1)
  153. /*
  154. * Stop counting the events and execute the code provided by `block` for each
  155. * event. Within `code`: `index` refers to the event position index in the
  156. * counter array defined by `PROF_COUNTERS`; `counter` is the actual value of
  157. * the counter. `index` is a 64 bit unsigned integer.
  158. */
  159. #define PROF_DO(block) \
  160. do { \
  161. uint64_t i_; \
  162. PROF_STOP(); \
  163. for (i_ = 0; i_ < prof_event_cnt_; i_++) { \
  164. uint64_t index = i_; \
  165. uint64_t counter = prof_event_buf_[i_ + 1]; \
  166. (void)index; \
  167. (void)counter; \
  168. block; \
  169. } \
  170. } while (0)
  171. /*
  172. * Same as `PROF_DO` except that `callback` is the name of a *callable* object
  173. * (e.g. a function) which, for each event, is be called with the two parameters
  174. * `index` and `counter`.
  175. */
  176. #define PROF_CALL(callback) \
  177. PROF_DO(callback(index, counter))
  178. /*
  179. * Stop counting the events and write to `file` (a stdio.h `FILE *`) as many
  180. * lines as are events in `PROF_EVENT_LIST`. Each line contains `index` and
  181. * `counter` (as defined by `PROF_DO`) separated by a tabulation character. If
  182. * there is only one event then `index` is omitted.
  183. */
  184. #define PROF_FILE(file) \
  185. PROF_DO(if (prof_event_cnt_ > 1) { \
  186. fprintf((file), "%lu\t%lu\n", index, counter); \
  187. } else { \
  188. fprintf((file), "%lu\n", counter); \
  189. } \
  190. )
  191. /*
  192. * Same as `PROF_LOG_FILE` except that `file` is `stdout`.
  193. */
  194. #define PROF_STDOUT() \
  195. PROF_FILE(stdout)
  196. /*
  197. * Same as `PROF_LOG_FILE` except that `file` is `stderr`.
  198. */
  199. #define PROF_STDERR() \
  200. PROF_FILE(stderr)
  201. /* DEFAULTS ----------------------------------------------------------------- */
  202. #ifndef PROF_EVENT_LIST
  203. #ifdef PERF_COUNT_HW_REF_CPU_CYCLES /* since Linux 3.3 */
  204. #define PROF_EVENT_LIST PROF_EVENT_HW(REF_CPU_CYCLES)
  205. #else
  206. #define PROF_EVENT_LIST PROF_EVENT_HW(CPU_CYCLES)
  207. #endif
  208. #endif
  209. /* UTILITY ------------------------------------------------------------------ */
  210. #define PROF_ASSERT_(x) \
  211. do { \
  212. if (!(x)) { \
  213. fprintf(stderr, "# %s:%d: PROF error", __FILE__, __LINE__); \
  214. if (errno) { \
  215. fprintf(stderr, " (%s)", strerror(errno)); \
  216. } \
  217. printf("\n"); \
  218. abort(); \
  219. } \
  220. } while (0)
  221. #define PROF_IOCTL_(mode) \
  222. do { \
  223. PROF_ASSERT_(ioctl(prof_fd_, \
  224. PERF_EVENT_IOC_ ## mode, \
  225. PERF_IOC_FLAG_GROUP) != -1); \
  226. } while (0)
  227. #define PROF_READ_COUNTERS_(buffer) \
  228. do { \
  229. const ssize_t to_read = sizeof(uint64_t) * (prof_event_cnt_ + 1); \
  230. PROF_ASSERT_(read(prof_fd_, buffer, to_read) == to_read); \
  231. } while (0)
  232. /* SETUP -------------------------------------------------------------------- */
  233. static int prof_fd_;
  234. static uint64_t prof_event_cnt_;
  235. static uint64_t *prof_event_buf_;
  236. static void prof_init_(uint64_t dummy, ...) {
  237. uint32_t type;
  238. va_list ap;
  239. prof_fd_ = -1;
  240. prof_event_cnt_ = 0;
  241. va_start(ap, dummy);
  242. while (type = va_arg(ap, uint32_t), type != (uint32_t)-1) {
  243. struct perf_event_attr pe;
  244. uint64_t config;
  245. int fd;
  246. config = va_arg(ap, uint64_t);
  247. memset(&pe, 0, sizeof(struct perf_event_attr));
  248. pe.size = sizeof(struct perf_event_attr);
  249. pe.read_format = PERF_FORMAT_GROUP;
  250. pe.type = type;
  251. pe.config = config;
  252. #ifdef PROF_USER_EVENTS_ONLY
  253. pe.exclude_kernel = 1;
  254. pe.exclude_hv = 1;
  255. #endif
  256. fd = syscall(__NR_perf_event_open, &pe, 0, -1, prof_fd_, 0);
  257. PROF_ASSERT_(fd != -1);
  258. if (prof_fd_ == -1) {
  259. prof_fd_ = fd;
  260. }
  261. prof_event_cnt_++;
  262. }
  263. va_end(ap);
  264. prof_event_buf_ = (uint64_t *)malloc((prof_event_cnt_ + 1) *
  265. sizeof(uint64_t));
  266. }
  267. void __attribute__((constructor)) prof_init()
  268. {
  269. prof_init_(0, PROF_EVENT_LIST /*,*/ (uint32_t)-1);
  270. }
  271. void __attribute__((destructor)) prof_fini()
  272. {
  273. PROF_ASSERT_(close(prof_fd_) != -1);
  274. free(prof_event_buf_);
  275. }
  276. #endif
  277. /*
  278. * License
  279. * -------
  280. *
  281. * Copyright (c) 2017 Andrea Cardaci <cyrus.and@gmail.com>
  282. *
  283. * Permission is hereby granted, free of charge, to any person obtaining a copy
  284. * of this software and associated documentation files (the "Software"), to deal
  285. * in the Software without restriction, including without limitation the rights
  286. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  287. * copies of the Software, and to permit persons to whom the Software is
  288. * furnished to do so, subject to the following conditions:
  289. *
  290. * The above copyright notice and this permission notice shall be included in
  291. * all copies or substantial portions of the Software.
  292. *
  293. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  294. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  295. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  296. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  297. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  298. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  299. * SOFTWARE.
  300. */