# profiler.h ## Overview
Collects profiling data. The gathered data can later be processed in various ways. The data model is based on [Chrome tracing](http://www.gamasutra.com/view/news/176420/Indepth_Using_Chrometracing_to_view_your_inline_profiling_data.php).
## Index
`enum tm_profiler_event_type`
`struct tm_profiler_event_t`
`struct tm_profiler_buffer_t`

`struct tm_profiler_api`

## API
### `enum tm_profiler_event_type`
Type of sampled profile event. ~~~c enum tm_profiler_event_type { // Start of a time interval. (Corresponds to `B` in Chrome tracing.) TM_PROFILER_EVENT_TYPE_BEGIN, // End of a time interval. (Corresponds to `E`.) TM_PROFILER_EVENT_TYPE_END, // Instant event. (Corresponds to `I`.) TM_PROFILER_EVENT_TYPE_INSTANT, // Async event start. (Corresponds to `S`.) TM_PROFILER_EVENT_TYPE_START, // Async event finish. (Corresponds to `F`.) TM_PROFILER_EVENT_TYPE_FINISH, // A thread switched from running one fiber to another. TM_PROFILER_EVENT_TYPE_FIBER_SWITCH, }; ~~~
### `struct tm_profiler_event_t`
Represents a sampled profile event. ~~~c typedef struct tm_profiler_event_t { // [[enum tm_profiler_event_type]] type of event. (Corresponds to `ph` in Chrome tracing.) uint32_t type; // ID of thread that spawned the event (`tid`). uint32_t thread_id; // Timestamp of when event was generated (`ts`). uint64_t time_stamp; union { // Data for BEGIN, END, INSTANT, START, and FINISH events. struct { // ID used to match begin/end events. uint32_t id; TM_PAD(4); // Event category (`cat`). This must point to a string with global lifetime. const char *category; // Name of event (`name`). This must point to a string with global lifetime. const char *name; // Object that is being processed, or NULL. Note that since object names typically do // not have global life time, you should call [[intern()]] on your strings before passing // them as object names. const char *object; }; // Data for FIBER_SWITCH events. struct { // Index of fiber that is being switched out. uint32_t from_fiber; // Index of fiber that is being switched in. uint32_t to_fiber; }; }; } tm_profiler_event_t; ~~~
### `struct tm_profiler_buffer_t`
Represents the internal buffer of profiler events that the profiler keeps. Profiler events are internally stored in a ring buffer, so to avoid creating a copy of the data, we return it in two parts. The first part (`[0]`) is the data before the ring buffer wraps around and the second part (`[1]`) is the data after the wraparound. ~~~c typedef struct tm_profiler_buffer_t { // The total number of profiler events that have been recorded. Note that not all of these may // be retained in the buffer, since the buffer is fixed sized. To track all events, the buffer // must be regularly polled and flushed to permanent storage (memory/disk/network). uint64_t total_recorded; // Starting event index of each of the two buffer segments. uint64_t start[2]; // Pointer to events for each of the two buffer segments. const tm_profiler_event_t *events[2]; // Number of events in each segment. uint32_t count[2]; } tm_profiler_buffer_t; ~~~
### `struct tm_profiler_api`
`tm_profiler_api` is used to collect and retain profiling events. Other systems can query the profiler for these events and take appropriate action. The profiler is global and thread-safe. #### `init()` ~~~c void (*init)(struct tm_allocator_i *allocator, uint32_t event_buffer_size); ~~~ Initializes the global profiler for data recording. #### `shutdown()` ~~~c void (*shutdown)(void); ~~~ Shuts down the global profiler. #### `enabled` ~~~c bool *enabled; ~~~ Flag that controls whether profiling is enabled or not. Profiling events are only recorded when the profiler is enabled. #### `begin()` ~~~c uint64_t (*begin)(const char *name, const char *category, const char *object); ~~~ Records a `BEGIN` event and returns its ID. Calls to `begin()` must be matched by calls to `end()` with the same ID. `name` is typically the name of the function that is being profiled (or a description of a sub-part of a function). `category` is the name of the file that the function appears in (i.e. `__FILE__`). `object` can either be `NULL` or the name of the object that is being operated on, where that makes sense. !!! WARNING The profiler does not make copies of the string arguments, so you can only pass static strings (strings with global lifetime). If you want to use a string that doesn't have global lifetime (for example for the `object` parameter), convert it to a permanent string by calling `intern()` on it first. #### `end()` ~~~c void (*end)(uint64_t begin_id); ~~~ Records an `END` event. This must match a corresponding `begin()` call. #### `instant()` ~~~c void (*instant)(const char *name, const char *category, const char *object); ~~~ Records an `INSTANT` event. #### `start()` ~~~c uint64_t (*start)(const char *name, const char *category, const char *object); ~~~ Records a `START` event. Calls to `start()` must be matched by calls to `finish()` with the same ID. #### `finish()` ~~~c void (*finish)(uint64_t start_id); ~~~ Records a `FINISH` event. This must match a corresponding `start()` call. #### `intern()` ~~~c const char *(*intern)(const char *s); ~~~ Copies the string `s` into a "permanent" string (a string with the same lifetime as the profiler itself) and returns the result. If you have strings with short lifetimes that you want to use for profiler events, you must call `intern()` on them before passing them to `begin()`, `instant()`, or `start()`. Note that `intern()` uses a hash lookup to convert the string, so there is a performance hit. If you can, hoist the `intern()` call out of any hot loops. #### `fiber_switch()` ~~~c void (*fiber_switch)(uint32_t from_fiber, uint32_t to_fiber); ~~~ Records a fiber switch on the thread, from the fiber with index `from_fiber` to the fiber with index `to_fiber`. A fiber switch swaps out the callstack of the thread (and hence the profiler stack too). #### `submit()` ~~~c void (*submit)(tm_profiler_event_t *events, uint32_t count); ~~~ Submit a range of profiler events from an external source (for example the GPU). #### `copy()` ~~~c void (*copy)(tm_profiler_event_t *dest, uint64_t start, uint32_t count, uint64_t *actual_start, uint32_t *actual_count); ~~~ Requests to copy `count` events from the profiler buffer, starting at the event with index `start` into the `dest` buffer. The function will return the actual start of the copied events in the `actual_start` parameter and the actual count of copied events in the `actual_count` parameter. `actual_start` might differ from `start` if the internal event buffer ran out of space, so the events at `start` are no longer available. `actual_count` might differ from `count` if there are fewer than `count` items available in the internal buffer. #### `buffer()` ~~~c tm_profiler_buffer_t (*buffer)(void); ~~~ Provides direct access to the internal buffer without copying the data out. This is faster than using `copy`, but a bit more complicated and dangerous (buffer data may be overwritten by another thread). However, since the profiler can generate a lot of data it might be necessary for some applications.
### `tm_profiler_api_version`
~~~c #define tm_profiler_api_version ~~~
~~~c #define TM_PROFILER_BEGIN_FUNC_SCOPE() ~~~ Starts a profiling scope for the current function. You should match this with a call to `TM_PROFILER_END_FUNC_SCOPE()`.
~~~c #define TM_PROFILER_BEGIN_FUNC_SCOPE_WITH_OBJECT(o) ~~~ Starts a profiling scope for the current function, marking the scope as related to the object `o`. You should match this with a call to `TM_PROFILER_END_FUNC_SCOPE()`.
~~~c #define TM_PROFILER_END_FUNC_SCOPE() ~~~ Ends a profiler scope started with `TM_PROFILER_BEGIN_FUNC_SCOPE()`.
~~~c #define TM_PROFILER_END_FUNC_SCOPE_WITH(x) ~~~ As `TM_PROFILER_END_FUNC_SCOPE()` but also returns the value `x`. This can be used to end a profiler scope and at the same time return a value: ~~~c return TM_PROFILER_END_FUNC_SCOPE_WITH(-1); ~~~
~~~c #define TM_PROFILER_BEGIN_LOCAL_SCOPE(tag) ~~~ Starts a local profiler scope, tagged with the naked word `tag` (it gets stringified by the macro). Use a local profiler scope if you need to profile parts of a function. You should match this with a call to `TM_PROFILER_END_LOCAL_SCOPE()`. ~~~c void f() { TM_PROFILER_BEGIN_FUNC_SCOPE(); // ... { TM_PROFILER_BEGIN_LOCAL_SCOPE(stuff); // ... TM_PROFILER_END_LOCAL_SCOPE(stuff); } // ... TM_PROFILER_END_FUNC_SCOPE(); } ~~~
~~~c #define TM_PROFILER_END_LOCAL_SCOPE(tag) ~~~ Ends a local profiler scope started with `TM_PROFILER_BEGIN_LOCAL_SCOPE()`.
~~~c #define TM_PROFILER_INSTANT(name) ~~~ Logs an instant with the `name` in the profiler system.