Minimal tracing library in C for Linux generating traces in Chrome Trace Event format.
(Example program is in C++ for ease of thread creation. The library itself is C.)
#include "tracing.h"
#include <string>
#include <thread>
#include <vector>
int main()
{
trace_init();
trace_set_process_name("test_tracing_threads");
TRACE_FUNC();
std::vector<std::thread> threads;
trace_begin("Create threads");
for (int i = 0; i < 8; ++i)
{
TRACE_SCOPE("Create thread");
threads.emplace_back([i](){
TRACE_SCOPE("Worker thread");
std::string name = "Worker thread " + std::to_string(i);
trace_set_thread_name(name.c_str());
for (int i = 0; i < 100; ++i) {
TRACE_SCOPE("Empty scope with contention");
}
//std::this_thread::sleep_for(std::chrono::milliseconds(10));
});
}
trace_end("Create threads");
trace_begin("Wait for threads to finish");
for (auto& thread : threads) {
thread.join();
}
trace_end("Wait for threads to finish");
trace_begin("Test performance");
for (int i = 0; i < 1000; ++i) {
TRACE_SCOPE("Empty scope");
}
trace_end("Test performance");
}
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
To run the example:
TRACING_ENABLE=TRUE ./build/test_tracing_threads
Option one: use cmake add_subdirectory
assuming you have downloaded tracing source code into
external/tracing
:
add_subdirectory(external/tracing)
add_executable(my_executable)
target_link_library(my_executable PRIVATE tracing)
Option two: just add files tracing.c
and tracing.h
to your build using any build system.
Synopsis:
void trace_init();
void trace_close();
void trace_set_process_name(const char* name);
void trace_set_thread_name(const char* name);
void trace_begin(const char* name);
void trace_end(const char* name);
// Macros:
TRACE_SCOPE()
TRACE_FUNC()
Description:
void trace_init();
Initialize the tracing library.
This must be called before other calls to the library and before creating the threads.
Opens the trace json file (either specified in the TRACE_FILE_NAME
environment
variable, or, if that is not set, with format trace-<pid>-<random>.json
).
It also registers trace_close()
to be called at exit (with atexit()
).
void trace_close();
Close trace file.
Must be called after threads have finished.
void trace_begin(const char* name);
Begin event with name name
.
This emits event 'B' in the Chrome Event format.
void trace_end(const char* name);
End event with name name
. Name must match a previous begin event.
This emits event 'E' in the Chrome Event format.
void trace_set_process_name(const char* name);
void trace_set_thread_name(const char* name);
Set current process name and set current thread name. This emits event 'M' (metadata) in the Chrome Event format.
TRACE_SCOPE(name);
This macro makes a call to trace_begin(name)
and registers trace_end(name)
to be called at scope end, using __attribute__((cleanup))
.
This attribute is supported by GCC and Clang (and supposedly Intel).
String name
must still be available at the time trace_end
is called (scope exit).
TRACE_FUNC();
The same as above but uses current function name as scope name (using __func__
)
TRACING_FILE_NAME
Specify trace file name (this also enables tracing).
If file name is not specified, but
TRACING_ENABLE
is set instead, the filename is generated automatically in the form
trace-<pid>-<random>.json
- To preserve maximum information in a crash, the library writes events immediately to the file. This also allows watching the trace file while the program still runs.
- Writing to the file is synchronised by virtue of using C functions, such as
fprintf
. - On recent
linux
andglibc
, the library makes one or two syscalls per every event:- possibly one
futex
call if tracing from multiple threads with contention - and always one
write
call
- possibly one
- On older systems, it may also make a
clock_gettime
syscall per event.
Therefore:
- Do not use this library in the inner loops. Only use it to trace some larger sections of the code.
- Be especially careful when using it from many threads.
Overhead was measured using the example program above. Overhead of an empty scope (two events) is ~ 5 us.
Benchmark system: Ubuntu 24.04, Linux 6.8.0-44, GCC 13.2.0, AMD Ryzen 5700G
Chrome Trace Event format was chosen as one of the most widely supported trace formats.
To view the file, options are:
- QtCreator To open use menu item "Analyze" -> "Chrome Trace Format Viewer" -> "Load JSON file". Does not show event names by default, use a toolbar button to enable showing information on hover without a click.
- Speedscope The most friendly visualization, but only shows one thread at a time.
- Perfetto
Viewers that do not work:
- Firefox profiler - cannot load generic Trace Event file. firefox-devtools/profiler#4915
Note: Some viewers can have issues with traces longer than ~30 min, seemingly due to integer overflow (time is saved in the file as integer microseconds).
Scoped macros require a GCC or Clang compiler for __attribute__((cleanup))
The library uses non-standard calls gettid()
and getrandom()
and therefore is Linux-only.
The closest alternative is https://github.com/google/chrometracing (includes C++ and Go, plus unreleased Python implementation)
MIT license
Copyright (c) 2024 Ilya Popov