omlc_inject(3)
==============

NAME
----
omlc_inject - inject a measurement sample into a measurement point

SYNOPSIS
--------
[verse]
*#include <oml2/omlc.h>*
[verse]
'int' *omlc_inject*('OmlMP'* mp, 'OmlValueU'* values); +
'int' *omlc_inject_metadata*('OmlMP'* mp, 'const char'* key, 'const OmlValueU'* value, 'OmlValueT' type, 'const char'* fname); +

DESCRIPTION
-----------

The main purpose of the linkoml:liboml[3] is to allow easy reporting of
measurement tuples from within instrumented applications. This is called
'injection'. A set of 'Measurement Points' (MPs) are first registered to
the library, with a fixed schema describing the tuples to be reported
through each MP. At run-time, the injected data is serialised as a
'Measurement Stream' (MS), and sent towards a collection point.

*omlc_inject*() takes an array of values and injects them as a
measurement sample into an MP previously defined via a call to
linkoml:omlc_add_mp[3]. If linkoml:omlc_inject[3] is called prior to the
start of measurement sampling, it will be ignored.  Measurement sampling
is initiated by a call to linkoml:omlc_start[3].

METADATA
--------

The client library can also inject metadata which doesn't quite fit in
the defined measurement points, or occurs too seldom to justify creating
a new one. Example metadata includes ancillary information about the
application such as version or command line, or some specific details
about MPs or specific fields such as precision and units.  Metadata is
sent along a specific MS named '_experiment_metadata'. Metadata
information consists of 'key'/'value' pairs qualifying a 'subject'. The
subject is a string formatted as '.[mpname[.fname]]', where '.'
identifies the root of the application, and additional components allow
to specify the entity to which the metadata refers more precisely.

Metadata can be injected from the client side using the
*omlc_inject_metadata*() function. The 'subject' is derived from 'mp'
(which can be 'NULL') and that MP's optional field 'fname' (which is
ignored if 'mp' is 'NULL'). The 'key' is specified as a nil-terminated
C-string, and the 'value' as an  linkoml:OmlValueU[3], allowing some
type flexibility.  At the moment, the only valid 'type' for 'value' is
'OML_STRING_VALUE'; anything else will generate an error. Nonetheless it
needs to be properly initialised (linkomlalias:omlc_zero[OmlValueU,3])
and cleaned up (linkomlalias:omlc_reset_string[OmlValueU,3]).

AN IMPORTANT NOTE ON THE GOODNESS OF GENERATED CODE
---------------------------------------------------

Prior to reading the following, it is worthy to note that
linkoml:oml-scaffold[1] can be used to generate custom registration and
injection helpers from a high-level description of an application's MPs.
This is sufficient for most applications, and avoids errors in
manipulating low-level data structures. This also provides some
abstraction from occasional API changes, and simplifies porting to newer
versions of OML. It is therefore the recommended approach for all
applications with static measurement points (i.e., not created
dynamically with variable MPs depending on run-time conditions, such as
specific instrumented plugin code).

The code generated by linkoml:oml-scaffold[1] follows the structure
described thereafter. The registration helpers create the 'OmlMPDef'
structures as exemplified below, and rely on linkoml:omlc_add_mp[3] to
register the MPs on behalf of the user (in
linkomlalias:oml_register_mps[liboml2,3]).
The injection helpers (see linkomlalias:oml_inject_MPNAME[liboml2,3])
offer C-functions with typed argument, and map them internally to
'OmlValueU' before calling *omlc_inject*().

MANUALLY INJECTING SAMPLES
--------------------------

In some cases (mostly when defining MPs dynamically at run time), it is
not possible to use injection helpers (see
link:#_an_important_note_on_the_goodness_of_generated_code[previous
section]). Only in these cases is it justified to use 'omlc_inject'
directly. This is described here.

The 'mp' parameter must be an MP handle returned by a call
to linkoml:omlc_add_mp[3]. The 'values' array must contain values whose
number and types correspond to the types declared for the MP tuple in
the call to linkoml:omlc_add_mp[3]. The convenience macros *omlc_set_**()
should be used to set up the values array.

For instance, suppose the following MP definition:

----
OmlMPDef mp_def [] =
{
  { "source", OML_UINT32_VALUE },
  { "destination", OML_UINT32_VALUE },
  { "length", OML_UINT32_VALUE },
  { "weight", OML_DOUBLE_VALUE },
  { "protocol", OML_STRING_VALUE },
  { "txrates", OML_VECTOR_UINT32_VALUE },
  { NULL, (OmlValueT)0 } /* Sentinel value */
};
----

(See linkoml:omlc_add_mp[3] for details.)

To inject a measurement sample into this MP, the programmer must first
declare an 'OmlValueU' array with the right number of elements, in
this case six (the sentinel value is only needed when declaring the
MP to linkoml:omlc_add_mp[3], it doesn't play any further part).  Then, call
the value setting macros to load new values into the array, and
finally pass the array to *omlc_inject*():

----
OmlMP *mp = omlc_add_mp ("myMP", mp_def);

...

omlc_start();

...

uint32_t source_id;
uint32_t destination_id;
uint32_t packet_length;
double weight;
char *protocol;
double txrates[16];
size_t txrates_sz = sizeof(txrates) / sizeof(txrates[0]);

/* Some application-specific code to obtain new values for the variables above */

...

OmlValueU values[6];

omlc_zero_array(values, 6);

omlc_set_uint32 (values[0], source_id);
omlc_set_uint32 (values[1], destination_id);
omlc_set_uint32 (values[2], packet_length);
omlc_set_double (values[3], weight);
omlc_set_string (values[4], protocol);
omlc_set_vector_double (values[5], txrates, txrates_sz);

omlc_inject (mp, values);

omlc_reset_vector(values[5]);
omlc_reset_string(values[4]);
----

Internally, the library guards each MP with a mutex, so this function
should be thread safe.

The caller must be careful to ensure that the 'values' array has
enough elements to accommodate the declared number of fields in the
tuple for the given 'mp'. Otherwise, *omlc_inject*() might try to read
past the end of the array. Similarly, the types of the values must be
as declared, and in the same order in the array as specified for the
MP tuple definition. This is particularly important for strings.

The values array should be considered "write-only" and should only be
manipulated using the value-setting macros, see linkoml:OmlValueU[3] for
information on supported data types and there accessors.

Once a call to *omlc_inject*() has been made, it is safe to modify/free
the values vector, as *omlc_inject*() creates internal copies.

RETURN VALUE
------------

These functions return 0 on success, or a negative value on error.

This is particularly the case if the library has not been initialized
with a call to linkoml:omlc_init[3], or if measurement sampling has not
been started with a call to linkoml:omlc_start[3]. It this case the
function exits, without performing any actions, with status -1.
Similarly, if either 'mp' or 'values' is NULL, then the function exits
with the same status.

BUGS
----
include::bugs.txt[]

SEE ALSO
--------
Manual Pages
~~~~~~~~~~~~
linkoml:oml2-server[1], linkoml:liboml2[1], linkoml:liboml2[3], linkoml:liboml2.conf[5]

linkoml:omlc_init[3], linkoml:omlc_add_mp[3], linkoml:omlc_start[3], linkoml:OmlValue[3]

include::manual.txt[]

// vim: ft=asciidoc:tw=72
