1. Introduction

The PopVision trace instrumentation library (libpvti) provides functions to control the capture of profiling information for the host-code of your IPU application. This data can then be explored with the PopVision System Analyser.

When your application runs with tracing enabled (see Section 1.2, Configuring trace options) it will create a single trace file, which is named using the current date, time and process number. The name of the trace file can be retrieved as follows:

>>> import libpvti as pvti
>>> pvti.getCurrentTraceFilename()
'Thu_Aug_27_09:46:55_2020_BST_2341.pvti'

1.1. Tracepoints

The trace file will contain an event for each entry logged using pvti::Tracepoint::begin(), pvti::Tracepoint::end() or pvti::Tracepoint::event()

>>> pvti.Tracepoint.begin(pvti.traceDrivers, "begin event")

1.2. Configuring trace options

You can enable trace globally by setting the PVTI_OPTIONS environment variable:

$ export PVTI_OPTIONS='{"enable":"true"}'

If the environment variable is not set, no trace file is created and no events are logged. If the environment variable is set but cannot be parsed, trace will be enabled with all default options.

You can further configure trace behaviour using the following options:

  • enable : Enable trace: true or false. Defaults to true if not set.

  • directory : Directory where trace files should be stored. Defaults to the current directory if not set.

  • channels : List of trace channels to enable. If empty or not set, all channels are enabled.

1.3. Channels

You can create your own trace channels and send trace events to them:

>>> traceCustom = createTraceChannel("Custom channel")

You can list the trace channels and whether they are enabled or not:

>>> pvti.listTraceChannel()
[['Drivers', True], ['Poplar', True], ['Framework', True]]

1.4. Enabling and disabling trace

You can enable individual trace channels at runtime. Events logged to disabled channels will not appear in the trace file

>>> pvti.disableTraceChannel(traceCustom)
>>> pvti.Tracepoint.event(traceCustom, "channel disabled: this event should not appear")

When you re-enable the channel, events will again be logged to the trace file as normal:

>>> pvti.enableTraceChannel(traceCustom)
>>> pvti.Tracepoint.end(traceCustom, "now events will be logged as normal")

1.5. Data series

The trace file will contain a value for each entry logged using pvti::Series::add(). You can name the Series object when you call the constructor Series. Each Series object belongs to a parent Graph.

>>> temps = pvti.Graph("Temperature", "Celsius", True)
>>> ipu1_temp = temps.addSeries("IPU1") # or ipu1_temp = pvti.Series("IPU1", temps)
>>> ...
>>> ipu1_temp.add(ipu1Sensor.reading())

1.6. Data series configuration

You can configure data series at runtime and/or compile time in the following ways:

  • Runtime configuration: setting “plots” in the PVTI_OPTIONS environment variable, example:

    { "enabled": "true",
        "plots": {   "Temperatures" : true,
                    "Frequency/IPU1": true,
                    "Frequency/IPU2": false }
    }
    

    Setting graph or series configuration in the environment variable overrides any hardcoded configuration below.

  • Hardcoded configuration. You can set the default configuration in the graph and series constructors which can be altered at runtime via enable/disable API calls:

    >>> series1 = graph.addSeries("Series1", False) # Series is initially disabled
    >>> ...
    >>> series1.enable() # Series1 is enabled during runtime due to a changed system condition
    

1.7. Metadata

You can add optional metadata to any event. Metadata must be a valid JSON string. You can use a library to create JSON or you can create it manually.

>>> import libpvti as pvti
>>> import json
>>> j = {}
>>> j["Latency_ms"] = 123.45
>>> pvti.Tracepoint.begin(pvti.traceDrivers, "Label1", pvti.createJsonMetadata(json.dumps(j)))
>>> error_message = "error string"
>>> json_string = '{"errorMessage"} : "' + error_message + '"}'
>>> pvti.Tracepoint.end(pvti.traceDrivers, "Label2", pvti.createJsonMetadata(json_string))
>>> pvti.Tracepoint.event(pvti.traceDrivers, "Label3")

1.8. Closing the trace file

The trace file will be closed when the program ends.

You can also call pvti::closeTrace() to close the trace file during execution. The program should not log any further events after this.

1.9. Python

Python has two additional approaches to instrument code. You can use a context manager or a decorator, as shown below.

Context manager:

>>> with pvti.Tracepoint(channel, "flowers"):
>>>    ...

Decorator:

>>> @pvti.instrument_fn(channel)
>>> def test_fn():
>>>    pass

1.10. C++ API

The C++ API is similar to the Python API. Channels are created as follows:

pvti::TraceChannel channel = {"Tests Channel"};

The following example demonstrates how to use tracepoints and metadata in C++:

#include "pvti.hpp"
#include <iostream>
#include <nlohmann/json.hpp>

pvti::TraceChannel channel = {"C custom channel"};
int main(int argc, char **argv) {

  auto filename = pvti::getCurrentTraceFilename();

  std::cout << "Trace file is " << filename << std::endl;

  pvti::Tracepoint::begin(&pvti::traceDrivers, "begin");

  pvti::Tracepoint::begin(&channel, "begin on custom channel");

  pvti::Tracepoint::event(&channel, "another event on custom channel");

  pvti::disableTraceChannel(&channel);

  pvti::Tracepoint::event(&channel, "event while disabled, not logged");

  pvti::enableTraceChannel(&channel);

  pvti::Tracepoint::end(&channel, "now events are logged again");

  nlohmann::json metadata;

  metadata["Latency_ms"] = measureLatency();

  pvti::Metadata md = pvti::createJsonMetadata(metadata.dump());

  pvti::Tracepoint::begin(&pvti::traceDrivers, "Label1", &md);

  pvti::closeTrace();
}