4.3 KiB
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.
Initialization
To initialize libovni follow these steps in all threads:
-
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. -
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. -
Init the thread. Call
ovni_thread_init()
to initialize the thread. Multiple attempts to initialize the same thread are ignored with a warning.
The ovni_proc_init()
arguments are as follows:
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
tell which one is which. The loom
argument defines the
loom name and maps the process to that
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.
-
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. -
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. -
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
processed by the emulator. Do so by emitting a OHx
event in the stream with the appropriate payload:
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.
- End the current thread. Emit a
OHe
event to inform the current thread ends. - Flush the buffer. Call
ovni_flush()
to be sure all events are written to disk. - Free the thread. Call
ovni_thread_free()
to complete the stream and free the memory used by the buffer. - 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.