110 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			110 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# 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.
 | 
						|
 | 
						|
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:
 | 
						|
 | 
						|
```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
 | 
						|
tell which one is which. The `loom` argument defines the
 | 
						|
[loom](../concepts/part-model.md#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](../emulation/events.md#OHx) in the stream with the appropriate payload:
 | 
						|
 | 
						|
```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.
 |