Using Tracy for Profiling and Observability

2022-03-25

Tracy is an amazing profiler and observability tool for C, C++11, and Lua.

Note

Although Rust and Zig are not in the list of officially supported languages, Tracy has been successfully integrated with them using the C API.

It is fairly well documented barring the process for integrating it with languages other than C++. This article is my attempt towards filling that hole in Tracy's documentation. I'll document my Tracy usage for C, Rust, and Zig and provide examples which can act as jumping points for others. Hopefully by the end I'll be able to demonstrate how incredibly useful a tool it really is.

Building the server

First we need to build the Tracy server, on Unix systems that entails:

  1. Installing some external libraries - instructions are distro specific so I can't be of much help here. Kindly refer to section '2.3 Building the server' of the Tracy user manual for details.
$ git clone https://github.com/wolfpld/tracy.git
$ cd tracy/profiler/build/unix
$ make

Tip

If you are using the nix package manager, you just need to install the package named tracy to install the Tracy server.

Apologies

Windows users would have to refer to section '2.3 Building the server' of the Tracy user manual and figure this out for themselves.

Client setup and markup (Rust)

Note

Client setup is language dependent so refer to the section pertaining to the language you are working in. Though client markup is language dependent too, the API for it is similar for all languages so we are going to cover it in detail for Rust only.

Client setup

Client setup for Rust is the easiest, all you need to do is add the following line to your Cargo.toml file:

tracy-client = { version = "0.12.6", default-features = false, features = ["enable"] }

Note

Make sure that you are using a supported version of the Tracy server (v0.7.8 at the time of writing this article) as described here.

Client markup

  1. Begin a span region, which will be terminated once _span goes out of scope (function, file, and line are determined automatically):
let _span = tracy_client::span!("some span");

we can also use Span::new to do the same:

let span = Span::new(&format!("fib({})", i), "fib", file!(), line!(), 100);

Marked spans are then displayed in Tracy:

spans

emit_value and emit_text can be used to add more context to the span:

span.emit_value(42);
span.emit_text("sleeping first time");

span context

  1. Output a message on the timeline:
message("starting t6", 10);

message

  1. Create an instance of plot that can plot arbitrary f64 values:
static TEMPERATURE: Plot = create_plot!("temperature");
TEMPERATURE.point(37.0);

plot

  1. Profile memory use:
#[global_allocator]
static GLOBAL: ProfiledAllocator<std::alloc::System> =
    ProfiledAllocator::new(std::alloc::System, 100);

memory usage

Note

This feature is much more useful in C, C++11, and Zig.

  1. Mark a frame:
finish_continuous_frame!("T1");
  1. Analyze runtime stats:

stats

Tracy is chock-full of features, above we covered few of the major ones. Play around with the rust example project and refer to the documentation of rust_tracy_client to continue your journey!

tracy

Client setup and markup (Zig)

We are going to demonstrate the process for Zig a bit differently by integrating Tracy with a game, more specifically the Pacman clone by floooh. Integrating Tracy with any other Zig project would then be just a matter of replicating the demonstrated steps.

Client setup

  1. Make the necessary changes in build.zig [commit for reference]
  2. Add tracy.zig to project [commit for reference]
  3. Add tracy repo to project [commit for reference]

Client markup

  1. tracy.zig contains all the functions required for client markup. They are similar to the ones covered for Rust above so refer to this commit and start playing around!
  2. Build project with:
$ zig build -Dtracy=./tracy -Dtracy-allocation -Dtracy-callstack

Complete code for the Pacman clone integrated with Tracy can be found here.

Client setup and markup (C)

First thing needed is a build system for C, meson is the one we are going forward with. Feel free to adapt the instructions to the build system of your choice.

Client setup

  1. Add the Tracy repository to your project directory
  2. Add project/tracy/TracyClient.cpp as a source file
  3. Add project/tracy as an include directory

Note

For reference meson.build that performs steps 2 and 3 can be found here.

  1. Place trace.h in your include path

Note

trace.h contains macros that wrap the Tracy C API, feel free to write your own abstraction layer, or use the Tracy C API itself.

Client markup

For client markup just use the macros in trace.h:

#include "trace.h"

int main(int argc, char** argv) {
  START_ZONE;
  END_ZONE;
  return 0;
}

Complete code for the C example project can be found here.

Fin

Tracy is an invaluable tool, I hope you find this article helpful in adding it to your tool-belt.

This work is licensed under CC BY-NC-SA 4.0.