cuticle/hi/minitrace.h

277 lines
11 KiB
C
Raw Permalink Normal View History

2024-06-30 14:43:13 +03:00
// Minitrace
//
// Copyright 2014 by Henrik Rydgård
// http://www.github.com/hrydgard/minitrace
// Released under the MIT license.
//
// Ultra-light dependency free library for performance tracing C/C++ applications.
// Produces traces compatible with Google Chrome's trace viewer.
// Simply open "about:tracing" in Chrome and load the produced JSON.
//
// This contains far less template magic than the original libraries from Chrome
// because this is meant to be usable from C.
//
// See README.md for a tutorial.
//
// The trace format is documented here:
// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit
// More:
// http://www.altdevblogaday.com/2012/08/21/using-chrometracing-to-view-your-inline-profiling-data/
#ifndef MINITRACE_H
#define MINITRACE_H
#include <inttypes.h>
#ifdef MTR_BUILDING_WITH_CMAKE
#include "minitrace_export.h"
#else
#define MINITRACE_EXPORT
#endif
// If MTR_ENABLED is not defined, Minitrace does nothing and has near zero overhead.
// Preferably, set this flag in your build system. If you can't just uncomment this line.
// #define MTR_ENABLED
// By default, will collect up to 1000000 events, then you must flush.
// It's recommended that you simply call mtr_flush on a background thread
// occasionally. It's safe...ish.
#define INTERNAL_MINITRACE_BUFFER_SIZE 1000000
#ifdef __cplusplus
extern "C" {
#endif
// Initializes Minitrace. Must be called very early during startup of your executable,
// before any MTR_ statements.
MINITRACE_EXPORT void mtr_init(const char *json_file);
// Same as above, but allows passing in a custom stream (FILE *), as returned by
// fopen(). It should be opened for writing, preferably in binary mode to avoid
// processing of line endings (i.e. the "wb" mode).
MINITRACE_EXPORT void mtr_init_from_stream(void *stream);
// Shuts down minitrace cleanly, flushing the trace buffer.
MINITRACE_EXPORT void mtr_shutdown(void);
// Lets you enable and disable Minitrace at runtime.
// May cause strange discontinuities in the output.
// Minitrace is enabled on startup by default.
MINITRACE_EXPORT void mtr_start(void);
MINITRACE_EXPORT void mtr_stop(void);
// Flushes the collected data to disk, clearing the buffer for new data.
MINITRACE_EXPORT void mtr_flush(void);
// Returns the current time in seconds. Used internally by Minitrace. No caching.
MINITRACE_EXPORT double mtr_time_s(void);
// Registers a handler that will flush the trace on Ctrl+C.
// Works on Linux and MacOSX, and in Win32 console applications.
MINITRACE_EXPORT void mtr_register_sigint_handler(void);
// Utility function that should rarely be used.
// If str is semi dynamic, store it permanently in a small pool so we don't need to malloc it.
// The pool fills up fast though and performance isn't great.
// Returns a fixed string if the pool is full.
MINITRACE_EXPORT const char *mtr_pool_string(const char *str);
// Commented-out types will be supported in the future.
typedef enum {
MTR_ARG_TYPE_NONE = 0,
MTR_ARG_TYPE_INT = 1, // I
// MTR_ARG_TYPE_FLOAT = 2, // TODO
// MTR_ARG_TYPE_DOUBLE = 3, // TODO
MTR_ARG_TYPE_STRING_CONST = 8, // C
MTR_ARG_TYPE_STRING_COPY = 9,
// MTR_ARG_TYPE_JSON_COPY = 10,
} mtr_arg_type;
// TODO: Add support for more than one argument (metadata) per event
// Having more costs speed and memory.
#define MTR_MAX_ARGS 1
// Only use the macros to call these.
MINITRACE_EXPORT void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id);
MINITRACE_EXPORT void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value);
#ifdef MTR_ENABLED
// c - category. Can be filtered by in trace viewer (or at least that's the intention).
// A good use is to pass __FILE__, there are macros further below that will do it for you.
// n - name. Pass __FUNCTION__ in most cases, unless you are marking up parts of one.
// Scopes. In C++, use MTR_SCOPE. In C, always match them within the same scope.
#define MTR_BEGIN(c, n) internal_mtr_raw_event(c, n, 'B', 0)
#define MTR_END(c, n) internal_mtr_raw_event(c, n, 'E', 0)
#define MTR_SCOPE(c, n) MTRScopedTrace ____mtr_scope(c, n)
#define MTR_SCOPE_LIMIT(c, n, l) MTRScopedTraceLimit ____mtr_scope(c, n, l)
// Async events. Can span threads. ID identifies which events to connect in the view.
#define MTR_START(c, n, id) internal_mtr_raw_event(c, n, 'S', (void *)(id))
#define MTR_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 'T', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step))
#define MTR_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'F', (void *)(id))
// Flow events. Like async events, but displayed in a more fancy way in the viewer.
#define MTR_FLOW_START(c, n, id) internal_mtr_raw_event(c, n, 's', (void *)(id))
#define MTR_FLOW_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 't', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step))
#define MTR_FLOW_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'f', (void *)(id))
// The same macros, but with a single named argument which shows up as metadata in the viewer.
// _I for int.
// _C is for a const string arg.
// _S will copy the string, freeing on flush (expensive but sometimes necessary).
// but required if the string was generated dynamically.
// Note that it's fine to match BEGIN_S with END and BEGIN with END_S, etc.
#define MTR_BEGIN_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
#define MTR_END_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
#define MTR_SCOPE_C(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
#define MTR_BEGIN_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
#define MTR_END_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
#define MTR_SCOPE_S(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
#define MTR_BEGIN_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
#define MTR_END_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
#define MTR_SCOPE_I(c, n, aname, aintval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
// Instant events. For things with no duration.
#define MTR_INSTANT(c, n) internal_mtr_raw_event(c, n, 'I', 0)
#define MTR_INSTANT_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
#define MTR_INSTANT_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_INT, aname, (void *)(aintval))
// Counters (can't do multi-value counters yet)
#define MTR_COUNTER(c, n, val) internal_mtr_raw_event_arg(c, n, 'C', 0, MTR_ARG_TYPE_INT, n, (void *)(intptr_t)(val))
// Metadata. Call at the start preferably. Must be const strings.
#define MTR_META_PROCESS_NAME(n) internal_mtr_raw_event_arg("", "process_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n))
#define MTR_META_THREAD_NAME(n) internal_mtr_raw_event_arg("", "thread_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n))
#define MTR_META_THREAD_SORT_INDEX(i) internal_mtr_raw_event_arg("", "thread_sort_index", 'M', 0, MTR_ARG_TYPE_INT, "sort_index", (void *)(i))
#else
#define MTR_BEGIN(c, n)
#define MTR_END(c, n)
#define MTR_SCOPE(c, n)
#define MTR_START(c, n, id)
#define MTR_STEP(c, n, id, step)
#define MTR_FINISH(c, n, id)
#define MTR_FLOW_START(c, n, id)
#define MTR_FLOW_STEP(c, n, id, step)
#define MTR_FLOW_FINISH(c, n, id)
#define MTR_INSTANT(c, n)
#define MTR_BEGIN_C(c, n, aname, astrval)
#define MTR_END_C(c, n, aname, astrval)
#define MTR_SCOPE_C(c, n, aname, astrval)
#define MTR_BEGIN_S(c, n, aname, astrval)
#define MTR_END_S(c, n, aname, astrval)
#define MTR_SCOPE_S(c, n, aname, astrval)
#define MTR_BEGIN_I(c, n, aname, aintval)
#define MTR_END_I(c, n, aname, aintval)
#define MTR_SCOPE_I(c, n, aname, aintval)
#define MTR_INSTANT(c, n)
#define MTR_INSTANT_C(c, n, aname, astrval)
#define MTR_INSTANT_I(c, n, aname, aintval)
// Counters (can't do multi-value counters yet)
#define MTR_COUNTER(c, n, val)
// Metadata. Call at the start preferably. Must be const strings.
#define MTR_META_PROCESS_NAME(n)
#define MTR_META_THREAD_NAME(n)
#define MTR_META_THREAD_SORT_INDEX(i)
#endif
// Shortcuts for simple function timing with automatic categories and names.
#define MTR_BEGIN_FUNC() MTR_BEGIN(__FILE__, __FUNCTION__)
#define MTR_END_FUNC() MTR_END(__FILE__, __FUNCTION__)
#define MTR_SCOPE_FUNC() MTR_SCOPE(__FILE__, __FUNCTION__)
#define MTR_INSTANT_FUNC() MTR_INSTANT(__FILE__, __FUNCTION__)
#define MTR_SCOPE_FUNC_LIMIT_S(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, l)
#define MTR_SCOPE_FUNC_LIMIT_MS(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, (double)l * 0.000001)
// Same, but with a single argument of the usual types.
#define MTR_BEGIN_FUNC_S(aname, arg) MTR_BEGIN_S(__FILE__, __FUNCTION__, aname, arg)
#define MTR_END_FUNC_S(aname, arg) MTR_END_S(__FILE__, __FUNCTION__, aname, arg)
#define MTR_SCOPE_FUNC_S(aname, arg) MTR_SCOPE_S(__FILE__, __FUNCTION__, aname, arg)
#define MTR_BEGIN_FUNC_C(aname, arg) MTR_BEGIN_C(__FILE__, __FUNCTION__, aname, arg)
#define MTR_END_FUNC_C(aname, arg) MTR_END_C(__FILE__, __FUNCTION__, aname, arg)
#define MTR_SCOPE_FUNC_C(aname, arg) MTR_SCOPE_C(__FILE__, __FUNCTION__, aname, arg)
#define MTR_BEGIN_FUNC_I(aname, arg) MTR_BEGIN_I(__FILE__, __FUNCTION__, aname, arg)
#define MTR_END_FUNC_I(aname, arg) MTR_END_I(__FILE__, __FUNCTION__, aname, arg)
#define MTR_SCOPE_FUNC_I(aname, arg) MTR_SCOPE_I(__FILE__, __FUNCTION__, aname, arg)
#ifdef __cplusplus
}
#ifdef MTR_ENABLED
// These are optimized to use X events (combined B and E). Much easier to do in C++ than in C.
class MTRScopedTrace {
public:
MTRScopedTrace(const char *category, const char *name)
: category_(category), name_(name) {
start_time_ = mtr_time_s();
}
~MTRScopedTrace() {
internal_mtr_raw_event(category_, name_, 'X', &start_time_);
}
private:
const char *category_;
const char *name_;
double start_time_;
};
// Only outputs a block if execution time exceeded the limit.
// TODO: This will effectively call mtr_time_s twice at the end, which is bad.
class MTRScopedTraceLimit {
public:
MTRScopedTraceLimit(const char *category, const char *name, double limit_s)
: category_(category), name_(name), limit_(limit_s) {
start_time_ = mtr_time_s();
}
~MTRScopedTraceLimit() {
double end_time = mtr_time_s();
if (end_time - start_time_ >= limit_) {
internal_mtr_raw_event(category_, name_, 'X', &start_time_);
}
}
private:
const char *category_;
const char *name_;
double start_time_;
double limit_;
};
class MTRScopedTraceArg {
public:
MTRScopedTraceArg(const char *category, const char *name, mtr_arg_type arg_type, const char *arg_name, void *arg_value)
: category_(category), name_(name) {
internal_mtr_raw_event_arg(category, name, 'B', 0, arg_type, arg_name, arg_value);
}
~MTRScopedTraceArg() {
internal_mtr_raw_event(category_, name_, 'E', 0);
}
private:
const char *category_;
const char *name_;
};
#endif
#endif
#endif