ovni/doc/user/runtime/index.md
2024-09-17 08:05:27 +02:00

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:

  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.

  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.

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

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

  1. End the current thread. Emit a OHe event 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.