| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- /*
- * Prof
- * ====
- *
- * Self-contained C/C++ profiler library for Linux.
- *
- * Prof offers a quick way to measure performance events (CPU clock cycles,
- * cache misses, branch mispredictions, etc.) of C/C++ code snippets. Prof is
- * just a wrapper around the `perf_event_open` system call, its main goal is to
- * be easy to setup and painless to use for targeted optimizations, namely, when
- * the hot spot has already been identified. In no way Prof is a replacement for
- * a fully-fledged profiler like perf, gprof, callgrind, etc.
- *
- * Please be aware that Prof uses `__attribute__((constructor))` to be as more
- * straightforward to setup as possible, so it cannot be included more than
- * once.
- *
- * Examples
- * --------
- *
- * ### Minimal
- *
- * The following snippet prints the rough number of CPU clock cycles spent in
- * executing the code between the two Prof calls:
- *
- * ```c
- * #include "prof.h"
- *
- * int main()
- * {
- * PROF_START();
- * // slow code goes here...
- * PROF_STDOUT();
- * }
- * ```
- *
- * ### Custom options
- *
- * The following snippet instead counts both read and write faults of the level
- * 1 data cache that occur in the userland code between the two Prof calls:
- *
- * ```c
- * #include <stdio.h>
- *
- * #define PROF_USER_EVENTS_ONLY
- * #define PROF_EVENT_LIST \
- * PROF_EVENT_CACHE(L1D, READ, MISS) \
- * PROF_EVENT_CACHE(L1D, WRITE, MISS)
- * #include "prof.h"
- *
- * int main()
- * {
- * uint64_t faults[2] = { 0 };
- *
- * PROF_START();
- * // slow code goes here...
- * PROF_DO(faults[index] += counter);
- *
- * // fast or uninteresting code goes here...
- *
- * PROF_START();
- * // slow code goes here...
- * PROF_DO(faults[index] += counter);
- *
- * printf("Total L1 faults: R = %lu; W = %lu\n", faults[0], faults[1]);
- * }
- * ```
- *
- * Installation
- * ------------
- *
- * Just include `prof.h`. Here is a quick way to fetch the latest version:
- *
- * wget -q https://raw.githubusercontent.com/cyrus-and/prof/master/prof.h
- */
- #ifndef PROF_H
- #define PROF_H
- #include <errno.h>
- #include <linux/perf_event.h>
- #include <stdarg.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ioctl.h>
- #include <sys/syscall.h>
- #include <unistd.h>
- /*
- * API
- * ---
- */
- /*
- * Reset the counters and (re)start counting the events.
- *
- * The events to be monitored are specified by setting the `PROF_EVENT_LIST`
- * macro before including this file to a list of `PROF_EVENT_*` invocations;
- * defaults to counting the number CPU clock cycles.
- *
- * If the `PROF_USER_EVENTS_ONLY` macro is defined before including this file
- * then kernel and hypervisor events are excluded from the count.
- */
- #define PROF_START() \
- do { \
- PROF_IOCTL_(ENABLE); \
- PROF_IOCTL_(RESET); \
- } while (0)
- /*
- * Specify an event to be monitored, `type` and `config` are defined in the
- * documentation of the `perf_event_open` system call.
- */
- #define PROF_EVENT(type, config) \
- (uint32_t)(type), (uint64_t)(config),
- /*
- * Same as `PROF_EVENT` but for hardware events; prefix `PERF_COUNT_HW_` must be
- * omitted from `config`.
- */
- #define PROF_EVENT_HW(config) \
- PROF_EVENT(PERF_TYPE_HARDWARE, PERF_COUNT_HW_ ## config)
- /*
- * Same as `PROF_EVENT` but for software events; prefix `PERF_COUNT_SW_` must be
- * omitted from `config`.
- */
- #define PROF_EVENT_SW(config) \
- PROF_EVENT(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ ## config)
- /*
- * Same as `PROF_EVENT` but for cache events; prefixes `PERF_COUNT_HW_CACHE_`,
- * `PERF_COUNT_HW_CACHE_OP_` and `PERF_COUNT_HW_CACHE_RESULT_` must be omitted
- * from `cache`, `op` and `result`, respectively. Again `cache`, `op` and
- * `result` are defined in the documentation of the `perf_event_open` system
- * call.
- */
- #define PROF_EVENT_CACHE(cache, op, result) \
- PROF_EVENT(PERF_TYPE_HW_CACHE, \
- (PERF_COUNT_HW_CACHE_ ## cache) | \
- (PERF_COUNT_HW_CACHE_OP_ ## op << 8) | \
- (PERF_COUNT_HW_CACHE_RESULT_ ## result << 16))
- /*
- * Stop counting the events. The counter array can then be accessed with
- * `PROF_COUNTERS`.
- */
- #define PROF_STOP() \
- do { \
- PROF_IOCTL_(DISABLE); \
- PROF_READ_COUNTERS_(prof_event_buf_); \
- } while (0)
- /*
- * Access the counter array. The order of counters is the same of the events
- * defined in `PROF_EVENT_LIST`. Elements of this array are 64 bit unsigned
- * integers.
- */
- #define PROF_COUNTERS \
- (prof_event_buf_ + 1)
- /*
- * Stop counting the events and execute the code provided by `block` for each
- * event. Within `code`: `index` refers to the event position index in the
- * counter array defined by `PROF_COUNTERS`; `counter` is the actual value of
- * the counter. `index` is a 64 bit unsigned integer.
- */
- #define PROF_DO(block) \
- do { \
- uint64_t i_; \
- PROF_STOP(); \
- for (i_ = 0; i_ < prof_event_cnt_; i_++) { \
- uint64_t index = i_; \
- uint64_t counter = prof_event_buf_[i_ + 1]; \
- (void)index; \
- (void)counter; \
- block; \
- } \
- } while (0)
- /*
- * Same as `PROF_DO` except that `callback` is the name of a *callable* object
- * (e.g. a function) which, for each event, is be called with the two parameters
- * `index` and `counter`.
- */
- #define PROF_CALL(callback) \
- PROF_DO(callback(index, counter))
- /*
- * Stop counting the events and write to `file` (a stdio.h `FILE *`) as many
- * lines as are events in `PROF_EVENT_LIST`. Each line contains `index` and
- * `counter` (as defined by `PROF_DO`) separated by a tabulation character. If
- * there is only one event then `index` is omitted.
- */
- #define PROF_FILE(file) \
- PROF_DO(if (prof_event_cnt_ > 1) { \
- fprintf((file), "%lu\t%lu\n", index, counter); \
- } else { \
- fprintf((file), "%lu\n", counter); \
- } \
- )
- /*
- * Same as `PROF_LOG_FILE` except that `file` is `stdout`.
- */
- #define PROF_STDOUT() \
- PROF_FILE(stdout)
- /*
- * Same as `PROF_LOG_FILE` except that `file` is `stderr`.
- */
- #define PROF_STDERR() \
- PROF_FILE(stderr)
- /* DEFAULTS ----------------------------------------------------------------- */
- #ifndef PROF_EVENT_LIST
- #ifdef PERF_COUNT_HW_REF_CPU_CYCLES /* since Linux 3.3 */
- #define PROF_EVENT_LIST PROF_EVENT_HW(REF_CPU_CYCLES)
- #else
- #define PROF_EVENT_LIST PROF_EVENT_HW(CPU_CYCLES)
- #endif
- #endif
- /* UTILITY ------------------------------------------------------------------ */
- #define PROF_ASSERT_(x) \
- do { \
- if (!(x)) { \
- fprintf(stderr, "# %s:%d: PROF error", __FILE__, __LINE__); \
- if (errno) { \
- fprintf(stderr, " (%s)", strerror(errno)); \
- } \
- printf("\n"); \
- abort(); \
- } \
- } while (0)
- #define PROF_IOCTL_(mode) \
- do { \
- PROF_ASSERT_(ioctl(prof_fd_, \
- PERF_EVENT_IOC_ ## mode, \
- PERF_IOC_FLAG_GROUP) != -1); \
- } while (0)
- #define PROF_READ_COUNTERS_(buffer) \
- do { \
- const ssize_t to_read = sizeof(uint64_t) * (prof_event_cnt_ + 1); \
- PROF_ASSERT_(read(prof_fd_, buffer, to_read) == to_read); \
- } while (0)
- /* SETUP -------------------------------------------------------------------- */
- static int prof_fd_;
- static uint64_t prof_event_cnt_;
- static uint64_t *prof_event_buf_;
- static void prof_init_(uint64_t dummy, ...) {
- uint32_t type;
- va_list ap;
- prof_fd_ = -1;
- prof_event_cnt_ = 0;
- va_start(ap, dummy);
- while (type = va_arg(ap, uint32_t), type != (uint32_t)-1) {
- struct perf_event_attr pe;
- uint64_t config;
- int fd;
- config = va_arg(ap, uint64_t);
- memset(&pe, 0, sizeof(struct perf_event_attr));
- pe.size = sizeof(struct perf_event_attr);
- pe.read_format = PERF_FORMAT_GROUP;
- pe.type = type;
- pe.config = config;
- #ifdef PROF_USER_EVENTS_ONLY
- pe.exclude_kernel = 1;
- pe.exclude_hv = 1;
- #endif
- fd = syscall(__NR_perf_event_open, &pe, 0, -1, prof_fd_, 0);
- PROF_ASSERT_(fd != -1);
- if (prof_fd_ == -1) {
- prof_fd_ = fd;
- }
- prof_event_cnt_++;
- }
- va_end(ap);
- prof_event_buf_ = (uint64_t *)malloc((prof_event_cnt_ + 1) *
- sizeof(uint64_t));
- }
- void __attribute__((constructor)) prof_init()
- {
- prof_init_(0, PROF_EVENT_LIST /*,*/ (uint32_t)-1);
- }
- void __attribute__((destructor)) prof_fini()
- {
- PROF_ASSERT_(close(prof_fd_) != -1);
- free(prof_event_buf_);
- }
- #endif
- /*
- * License
- * -------
- *
- * Copyright (c) 2017 Andrea Cardaci <cyrus.and@gmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
|