Files in pending/sandbox/perfect/ of tip

Files in directory pending/sandbox/perfect from the latest check-in


perfect is a single-header wrapper around linux kernel perf trace events. It aims to encapsulate the redundant complexity these interfaces push onto users while minimally restricting configurability and performance.


Linux kernel perf trace events provides a low-level API for accessing many interesting performance statistics for your software. These are used by Linux' own perf to do performance analysis. In order to collect these events you must generally run an application with root or elevated privileges.

Counter events

Event counters provides sums of the total number of various kinds of events (branch misses, cache misses, etc.).

Counter API

struct Perfect_Counter
  long long value;     /* value read from counter after it is stopped. */
  bool      has_error; /* did any errors occur during counting? */

bool perfect_counter_init(
  Perfect_Counter* p_counter,
  struct perf_event_attr* p_attr,
  pid_t pid,
  int cpu,
  int group_fd,
  unsigned long flags);

void perfect_counter_destroy(Perfect_Counter* p_counter);

void perfect_counter_start(Perfect_Counter* p_counter);

void perfect_counter_stop(Perfect_Counter* p_counter);

bool perfect_counter_is_valid(Perfect_Counter* p_counter);

Counter example

Below is a simple example showing how to read the branch misses for a given section of code using perfect:

#include "perfect.h"

struct perf_event_attr pe;
memset(&pe, 0, sizeof(struct perf_event_attr));
pe.size = sizeof(struct perf_event_attr);
pe.disabled = 1;
pe.inherit = 1;
pe.exclude_kernel = 1;
pe.exclude_guest = 1;
pe.exclude_hv = 1;

Perfect_Counter branch_miss_counter;
if (perfect_counter_init(&branch_miss_counter, &pe, -1, 0, -1, 0))
    // ... do something ...
  printf("branch misses: %lld\n", branch_miss_counter.value);


Sampling events

Sampling events provide access to specific events as they happen in real time. They require root privileges. Events are written to an mmaped ring buffer shared between the kernel and user space and can be read by iterating over the samples in that ring buffer.

Since the structure of the data written into the ring buffer varies by attributes and specific event an iteration API is provided with the ability to read and skip arbitrary data in this buffer.

Sampling API

struct Perfect_Sampler;
struct Perfect_Sample_Iterator;

bool perfect_sampler_init(
  Perfect_Sampler* p_sampler,
  size_t ring_buffer_pages,
  struct perf_event_attr* p_attr,
  pid_t pid,
  int cpu,
  int group_fd,
  unsigned long flags);

void perfect_sampler_destroy(Perfect_Sampler* p_sampler);

void perfect_sampler_start(Perfect_Sampler* p_sampler);

void perfect_sampler_stop(Perfect_Sampler* p_sampler);

bool perfect_sampler_has_samples(Perfect_Sampler* p_sampler);

Perfect_Sample_Iterator perfect_sample_iterator(Perfect_Sampler* p_sampler);

bool perfect_sample_iterator_is_valid(Perfect_Sample_Iterator* p_iter);

void perfect_sample_iterator_next(Perfect_Sample_Iterator* p_iter);

void perfect_sample_iterator_read(Perfect_Sample_Iterator* p_iter, void* p_dest, size_t size);

void perfect_sample_iterator_skip(Perfect_Sample_Iterator* p_iter, size_t offset);

Sampling example

Below is a simple example showing how you might read sampling events from a thread:

#include "perfect.h"

struct perf_event_attr pe;
//.. configure perf event ...

Perfect_Sampler sampler;
if (perfect_sampler_init(&sampler, 64, &pe, -1, 0, -1, PERF_FLAG_FD_CLOEXEC))

  Perfect_Sample_Iterator iter = perfect_sample_iterator(&sampler);
  while (perfect_sample_iterator_is_valid(&iter))
    perfect_sample_iterator_skip(&iter, sizeof(uint64_t)); // skip time

    uint64_t count;
    perfect_sample_iterator_read(&iter, &count, sizeof(uint64_t));
    // ... read more from sample ...

    // ... persist sample ...