@hackage tracy-profiler0.1.2.1

Haskell bindings for Tracy frame profiler

  • Installation

  • Dependencies (5)

  • Dependents (0)

  • Package Flags

      enable
       (off by default)

      Enable to actually call the Tracy functions and produce data. Otherwise the wrappers will be no-ops.

      fibers
       (off by default)

      Enable if your libtracyclient has this enabled.

      has_callstack
       (off by default)

      Enable if your libtracyclient has this enabled.

      manual_lifetime
       (off by default)

      Manually manage profiler lifetime. Enable if your libtracyclient has this enabled.

      on_demand
       (off by default)

      Don't generate and keep events if there's no connection. Enable if your libtracyclient has this enabled.

      zones_pedantic
       (on by default)

      Crash when a zone is starting on an unbound thread.

      zones_unsafe
       (off by default)

      Skip the bound-thread check for zones. This may crash the profile server with "zone ended twice" error.

tracy-profiler

Haskell bindings for Tracy frame profiler.

Setup

Installing

You can install the prebuilt package from your distribution if it has one:

sudo apt install libtracy-dev tracy-capture tracy-profiler

Or you can download and build one yourself and point your project to it:

# stack.yaml
extra-lib-dirs:
- upstream/_build/library
extra-include-dirs:
- upstream/tracy/public/tracy

You can use the provided Makefile that will do the thing:

make all # fetch and build stuff
make test # run the example
_build/profiler/tracy-profiler & disown # launch the profiler GUI

This way you can customize configuration. Make sure you update package flags to match.

Either way, you have to ensure that tracy-capture/tracy-profiler is built with the same version. Otherwise it will connect and immediately refuse to record anything.

Flags

The flags must match whatever the library has been built with.

By default all the instrumentation wrappers do nothing. That means you don't have to ifdef your code to remove the wrappers when they're not needed.

You have to set the enable flag in your project for the data to be collected.

flags:
  tracy-profiler:
    enable: true
    # manual_lifetime: false
    # fibers: false

Instrumentation

Use the functions from System.Tracy and System.Tracy.Zone to collect data:

{-# LANGUAGE MagicHash #-}         -- "static strings"#
{-# LANGUAGE OverloadedLabels #-}  -- #fuchsia colors
{-# LANGUAGE OverloadedStrings #-} -- "yes"

module Main where

import qualified System.Tracy as Tracy
import qualified Data.Text as Text

main :: IO ()
main = Tracy.withProfiler $ do
  Tracy.waitConnected_       -- wait for the tracy-capture to connect
  Tracy.withSrcLoc "main.nowhereInParticular" #f0fa do
    Tracy.messageL "hi there"# -- static strings require no copying to be logged
    mapM_ runFrame [0..600]

runFrame :: Int -> IO ()
runFrame ix = Tracy.withSrcLoc "runFrame" #fcc do
  Tracy.frameMark_
  let factorial = product [1 .. toInteger ix]
  let !digits = length (show factorial)
  Tracy.message . Text.pack $
    -- runtime strings will require some memcpy'ng around, use sparingly on hot paths
    "!" <> show ix <> " has " <> show digits <> " digits"
  Tracy.plotInt "digits"# digits

Collecting and viewing

Start tracy-capture before running the test to avoid empty areas where nothing happens:

tracy-capture -fo output.tracy &

Run the code, then upload the collected data to the viewer.

You'll see something like this:

screenshot

Are those are GC pauses we're looking at? 🤔

RTFM

You really should go read the official manual.

Yes, BEFORE you run into corrupted memory, surprising grouppings, or otherwise botched profiling runs.