# 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:

```c
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](../../dev/channels.md)). 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.

```c
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:

```c
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:

```c
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.

<details>
<summary>Example OmpSs-2 program</summary>
<br>
<div style="padding-left: 1em; border-left: 3px solid #ddd">
<p>
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.

```c
/* 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;
}
```

<!-- Images don't seem to work via markdown -->
<p>Here is the resulting timeline loaded in Paraver with the gradient color
configuration, showing the first mark type (the index):
<img style="margin-top: 1em" alt="" src="../fig/mark.png"></p>

</div>
</details>
<p></p>

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