ovni/doc/user/runtime/mark.md

7.6 KiB

Mark events

The mark API allows you to add arbitrary events in a trace to mark regions of interest while debugging or developing a new program or library. The events are processed by the emulator to generate a timeline.

Usage in runtime

Follow these steps to correctly use the API. Most problems will be detected in emulation and cause a panic if you are not careful.

Create a mark type

You can create up to 100 types of marks, each will be shown in its own Paraver timeline. To create a new type, use the following call:

void ovni_mark_type(int32_t type, long flags, const char *title);

The numeric value type must be a number between 0 and 99 (both included). The title will be used to give a name to the Paraver timeline.

The default with flags set to zero, is to create a channel that can hold a single value only (see channels). To use a stack of values add the OVNI_MARK_STACK value to the flags.

Only one thread among all nodes needs to define a type to be available globally, but the same type can be defined from multiple threads, as long as the same flags and title argument are used. The idea is to avoid having to check if the type was already defined or not.

Define labels (optional)

The values that are written to the channel can have labels to display in the Paraver timeline. The labels are optional, if not given the numeric value will be shown in Paraver.

Use the following call to register a label for a value in a given type.

void ovni_mark_label(int32_t type, int64_t value, const char *label);

A value can only have at most a single label associated. Multiple threads can call the ovni_mark_label() with the same type and value as long as they use the same label. New labels for the same type can be associated from different threads, as long as the values are different.

All value and label pairs are combined from all threads and will be available in the Paraver view for each type.

Emit events

All mark channels begin with the default value null, which is not shown in Paraver and will be displayed as the usual empty space. The value of the channel can be changed over time with the following functions.

!!! warning

The value 0 is forbidden, as it is used by Paraver to represent the
"empty" state.

If you have used a single channel (without OVNI_MARK_STACK), then you must use the following call to emit events at runtime:

void ovni_mark_set(int32_t type, int64_t value);

It will update the value of the channel to the given value.

If you have used a stack channel (with OVNI_MARK_STACK), then you must use the push/pop set of calls:

void ovni_mark_push(int32_t type, int64_t value);
void ovni_mark_pop(int32_t type, int64_t value);

The value in the pop call must match the previous pushed value.

Example OmpSs-2 program

Here is a dummy program showing how to use the mark API with an OmpSs-2 program. Notice that there is no initialization of the current thread or process, as it already occurs inside the OmpSs-2 runtime before reaching the main.

/* Build with:
 *     $ clang -fompss-2 -lovni dummy.c -o dummy
 * Enable instrumentation in nanos6:
 *     $ echo 'version.instrument = "ovni"' > nanos6.toml
 * Run:
 *     $ ./dummy
 * Emulate:
 *     $ ovniemu ovni
 * View timeline:
 *     $ wxparaver ovni/cpu.prv ovni/cfg/cpu/ovni/mark.cfg
 */
#include <ovni.h>
#include <unistd.h>

enum { INDEX = 0, RUN = 1 };

static void process(int run, int i)
{
    ovni_mark_push(RUN, run + 1);
    ovni_mark_push(INDEX, i + 1);
    usleep(10000); // Dummy operation for 10 ms
    ovni_mark_pop(INDEX, i + 1);
    ovni_mark_pop(RUN, run + 1);
}

int main(void)
{
    ovni_mark_type(INDEX, OVNI_MARK_STACK, "Index");
    ovni_mark_type(RUN, OVNI_MARK_STACK, "Run");

    for (int run = 0; run < 10; run++) {
        for (int i = 0; i < 50; i++) {
            #pragma oss task
            process(run, i);
        }
    }

    #pragma oss taskwait

    return 0;
}

Here is the resulting timeline loaded in Paraver with the gradient color configuration, showing the first mark type (the index):

Usage in Paraver

Each thread holds a channel for each mark type that you have defined. The information of the mark channels is propagated to the Paraver timeline in Thread and CPU views.

When a thread is not running, the value of the mark channels is not shown in Paraver. In the case of the CPU timeline, only the values of the running thread are shown. If there are no running threads, nothing is shown.

Follow the next steps to create a configuration to suit your needs. You only need to do it once, then you can save the configuration file and reuse it for future traces.

Filtering the type

To see a mark type, you will have to create a Paraver configuration that matches the type that you have created. The mark type value gets converted into a PRV type by adding 100 (as values from 0 to 99 are reserved).

You can use the cpu/ovni/mark.cfg and thread/ovni/mark.cfg configurations as a starting point to create your own.

Go to "Window Properties" (the second button under "Files & Window Properties") and then go to Filter > Events > Event type. Set Function to = and click the Types value to display the [...] button, which will allow you to choose which type to display.

In the "Events Selection" window, ensure that only one type is selected, and the "Values" panel shows Function "All", to see all values for the selected type.

Setting the title

In the "Window Properties" adjust the Name so it reflects what you are seeing. This will be shown in the saved images, so it is good to use a proper description.

Configure the display method

By default, the timeline will display the values as "Code color". To switch to a gradient or other methods, left-click in the timeline and go to "Paint As" and select "Gradient" (or others).

You may also want to adjust the "Drawmode" which determines what happens when there are multiple values under a given pixel. This is specially important when you are viewing the trace with a large time range, before zooming into a given region.

By default, the "Random not zero" mode is selected, which will select a random value from the ones under each pixel, disregarding the occurrences of each value. This mode will give importance to rare values, so it is usually a safe starting point. The "Last" mode will show the last value in that pixel, which is more or less fair, but will often hide rare values.

To change in both horizontal (Time) and in vertical (Objects) directions, go to: left click on timeline > Drawmode > Both > Last.

Ensure the range is good

Paraver will only display values in the timeline that are in the Semantic range. If you see a red triangle in the lower left corner then there are values outside the range that are not being displayed. You can click on this button to expand the range to cover all values in the current view.

The opposite may also happen, where the range is too big for the current values. You can also click on the same spot (even if the triangle is not shown) to shrink the range to cover the values in the view, or go to the Window Properties and modify the "Semantic Minimum" and "Semantic Maximum" values manually.

Save the configuration

Once you finish configuring the timeline, save the configuration by left-clicking the view and then "Save > Configuration...". You can use this configuration in future traces to avoid doing these steps again.