Skip to content

ISTEQ-BV/tracing

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tracing

Minimal tracing library in C for Linux generating traces in Chrome Trace Event format.

Example

(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");
}

Building

cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build

To run the example:

TRACING_ENABLE=TRUE ./build/test_tracing_threads

Including into your project

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.

API

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__)

Environment variables

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

Performance

  • 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 and glibc, 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
  • 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

Viewing traces

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:

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).

Compatibility and requirements

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.

Alternatives

The closest alternative is https://github.com/google/chrometracing (includes C++ and Go, plus unreleased Python implementation)

License

MIT license

Copyright (c) 2024 Ilya Popov

About

Minimal tracing library in C for Chrome Tracing Event format.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published