2024-09-13 16:03:15 +02:00
|
|
|
# Introduction
|
|
|
|
|
|
|
|
To use *libovni* to instrument a program, follow the next instructions
|
|
|
|
carefully, or you may end up with an incomplete trace that is rejected at
|
|
|
|
emulation.
|
|
|
|
|
|
|
|
You can also generate a valid trace from your own software or hardware
|
|
|
|
directly, but be sure to follow the [trace specification](trace_spec.md).
|
|
|
|
|
|
|
|
## Initialization
|
|
|
|
|
|
|
|
To initialize libovni follow these steps in all threads:
|
|
|
|
|
|
|
|
1. **Check the version**. Call `ovni_version_check()` once before calling any
|
|
|
|
ovni function. It can be called multiple times from any thread, but only one
|
|
|
|
is required.
|
|
|
|
|
2024-09-17 08:05:27 +02:00
|
|
|
2. **Init the process**. Call `ovni_proc_init()` to initialize the process. It
|
|
|
|
can only be called **once per process** and it must be called before the
|
|
|
|
thread is initialized.
|
2024-09-13 16:03:15 +02:00
|
|
|
|
2024-09-17 08:05:27 +02:00
|
|
|
3. **Init the thread**. Call `ovni_thread_init()` to initialize the thread.
|
|
|
|
Multiple attempts to initialize the same thread are ignored with a warning.
|
2024-09-13 16:03:15 +02:00
|
|
|
|
|
|
|
The `ovni_proc_init()` arguments are as follows:
|
|
|
|
|
|
|
|
```c
|
|
|
|
void ovni_proc_init(int app, const char *loom, int pid);
|
|
|
|
```
|
|
|
|
|
|
|
|
The `app` defines the "appid" of the program, which must be a number >0. This is
|
|
|
|
useful to run multiple processes some of which run the same "app", so you can
|
2024-09-17 08:05:27 +02:00
|
|
|
tell which one is which. The `loom` argument defines the
|
|
|
|
[loom](../concepts/part-model.md#loom) name and maps the process to that
|
2024-09-13 16:03:15 +02:00
|
|
|
loom. It must be compose of the host name, a dot and a suffix. The PID is the
|
|
|
|
one obtained by `getpid(2)`.
|
|
|
|
|
|
|
|
The `ovni_thread_init()` function only accepts one argument, the TID as returned
|
|
|
|
by `gettid(2)`.
|
|
|
|
|
|
|
|
## Setup metadata
|
|
|
|
|
|
|
|
Once the process and thread are initialized, you can begin adding metadata to
|
|
|
|
the thread stream.
|
|
|
|
|
|
|
|
1. **Require models**. Call `ovni_thread_require()` with the required model
|
|
|
|
version before emitting events for a given model. Only required once from a
|
|
|
|
thread in a given trace.
|
|
|
|
|
|
|
|
2. **Emit loom CPUs**. Call `ovni_add_cpu()` to register each CPU in the loom. It can
|
|
|
|
be done from a single thread or multiple threads, in the latter the list of
|
|
|
|
CPUs is merged.
|
|
|
|
|
|
|
|
3. **Set the rank**. If you use MPI, call `ovni_proc_set_rank()` to register the
|
|
|
|
rank and number of ranks of the current execution. Only once per process.
|
|
|
|
|
|
|
|
## Start the execution
|
|
|
|
|
|
|
|
The current thread must switch to the "Running" state before any event can be
|
2024-09-17 08:05:27 +02:00
|
|
|
processed by the emulator. Do so by emitting a [`OHx`
|
|
|
|
event](../emulation/events.md#OHx) in the stream with the appropriate payload:
|
2024-09-13 16:03:15 +02:00
|
|
|
|
|
|
|
```c
|
|
|
|
static void thread_execute(int32_t cpu, int32_t ctid, uint64_t tag)
|
|
|
|
{
|
|
|
|
struct ovni_ev ev = {0};
|
|
|
|
ovni_ev_set_clock(&ev, ovni_clock_now());
|
|
|
|
ovni_ev_set_mcv(&ev, "OHx");
|
|
|
|
ovni_payload_add(&ev, (uint8_t *) &cpu, sizeof(cpu));
|
|
|
|
ovni_payload_add(&ev, (uint8_t *) &ctid, sizeof(ctid));
|
|
|
|
ovni_payload_add(&ev, (uint8_t *) &tag, sizeof(tag));
|
|
|
|
ovni_ev_emit(&ev);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The `cpu` is the logical index (not the physical ID) of the loom CPU at which
|
|
|
|
this thread will begin the execution. Use -1 if it is not known. The `ctid` and
|
|
|
|
`tag` allow you to track the exact point at which a given thread was created and
|
|
|
|
by which thread but they are not relevant for the first thread, so they can be
|
|
|
|
set to -1.
|
|
|
|
|
|
|
|
## Emit events
|
|
|
|
|
|
|
|
After this point you can emit any other event from this thread. Use the
|
|
|
|
`ovni_ev_*` set of functions to create and emit events. Notice that all events
|
|
|
|
are refer to the current thread that emits them.
|
|
|
|
|
|
|
|
If you need to store metadata information, use the `ovni_attr_*` set of
|
|
|
|
functions. The metadata is stored in disk by `ovni_attr_fluch()` and when the
|
|
|
|
thread is freed by `ovni_thread_free()`.
|
|
|
|
|
|
|
|
Attempting to emit events or writing metadata without having a thread
|
|
|
|
initialized will cause your program to abort.
|
|
|
|
|
|
|
|
## Finishing the execution
|
|
|
|
|
|
|
|
To finalize the execution **every thread** must perform the following steps,
|
|
|
|
otherwise the trace **will be rejected**.
|
|
|
|
|
|
|
|
1. **End the current thread**. Emit a [`OHe` event](../emulation/events.md#OHe) to inform the current thread ends.
|
|
|
|
2. **Flush the buffer**. Call `ovni_flush()` to be sure all events are written
|
|
|
|
to disk.
|
|
|
|
3. **Free the thread**. Call `ovni_thread_free()` to complete the stream and
|
|
|
|
free the memory used by the buffer.
|
|
|
|
4. **Finish the process**. If this is the last thread, call `ovni_proc_fini()`
|
|
|
|
to set the process state to finished.
|
|
|
|
|
|
|
|
If a thread fails to perform these steps, the complete trace will be rejected by
|
|
|
|
the emulator as it cannot guarantee the trace to be consistent.
|