Essential API

Note

Many file names and identifiers in this API contain placeholders for event, monitor, and synchronous set names. These placeholders are marked by the use of ALLCAPS.

The generated monitoring code is separated into multiple layers, ordered below from lowest to highest. SMEDL specifications are used to generate the monitor layer, and the rest are generated from the architecture specification.

Monitor

Actual monitor logic for a single SMEDL specification

Local wrapper

Manages instances, does unicast/multicast routing, and dynamic instantiation for a single monitor

Global wrapper

Routes events within a single synchronous set to the proper local wrappers or PEDL

Transport adapters

A “sibling” of the global wrapper in the hierarchy—sends and receives asynchronous events over the selected transport mechanism (RabbitMQ, ROS, etc.)

Manager

Initiates event handling at the global wrapper and transport adapters and passes asynchronous events between them

Most programs will only need to interact with part of the global wrapper and manager, so those are the only APIs described on this page. (The full API is described at Full API.)

Overview

The target program interacts with SMEDL through PEDL events. First, it makes a function call to emit an event. At that point, the event is queued, but the call immediately returns. Then, it makes another call to pass control to the manager. The manager allows all pending synchronous events to be processed and asynchronous events to be sent. This call returns when there are no more events immediately left to process (though there may still be asynchronous events outstanding). Finally, the target program must provide functions of its own for receiving PEDL events back from the monitors.

By default, SMEDL generates a PEDL stub that reads events from a CSV and does all of the above. This allows the monitors to be buildable immediately and can serve as demo code, but most real-world programs will want to replace most or all of this file with actual instrumentation.

For asynchronous PEDL events (that is, those not explicitly placed in a synchronous set), SMEDL generates a pedl synchronous set that does nothing except send and receive PEDL events asynchronously. This allows for the same API to be used, regardless of synchronization. But, if convenient, the pedl synchronous set can be dropped, and the target program or its instrumentation can send and receive events over the asynchronous message channels directly. For more information on that, go back to SMEDL Programming Interface and visit the page for the appropriate asynchronous transport.

SMEDL Types

Most types in SMEDL correspond directly to a well-known type in C (see Types). The exception is the opaque type, represented in C by SMEDLOpaque from smedl_types.h:

struct SMEDLOpaque

C type representing SMEDL’s opaque type

void *data

Pointer to the opaque’s contents

size_t size

Length of the opaque in bytes

For situations where C code needs to store or pass SMEDL values of arbitrary type, there is SMEDLValue, also in smedl_types.h:

struct SMEDLValue

Represents a SMEDL value of arbitrary type

SMEDLType t

SMEDL type of this value

union v

Value itself, using the C type matching the SMEDLType

int i
double d
char c
char *s
void *p
SMEDLOpaque o

For the t member, there is a SMEDLType enum:

enum SMEDLType

Identifies one of the six SMEDL types

enumerator SMEDL_INT
enumerator SMEDL_FLOAT
enumerator SMEDL_CHAR
enumerator SMEDL_STRING
enumerator SMEDL_POINTER
enumerator SMEDL_OPAQUE
enumerator SMEDL_NULL

Represents a wildcard parameter or a parameter that is irrelevant and unknown

Event Passing Conventions

Many functions in the SMEDL API are used to pass events from one place to another. For consistency, all of these functions use the same signature:

int event_passing_function(SMEDLValue *identities, SMEDLValue *params, void *aux)

The signature used by every event-passing function in SMEDL.

Parameters
  • identities – An array of monitor identities, either the source monitor (for an exported event) or the destination monitor (for an imported event). If the monitor has no identities or or it is a PEDL event, this parameter is ignored and can be safely set to NULL.

  • params – An array of the event parameters.

  • aux – Auxiliary data pointer. When one event causes another to be raised, this pointer is passed through untouched. This can be used for provenance information or any other purpose. Some asynchronous transports expect this to point to a particular type of data. NULL is always allowed.

Returns

Nonzero for success, zero for error

Note that there is no need to specify the length for the identities or params parameters since they are known from the architecture specification.

The convention that SMEDL follows is that the caller retains responsibility for any allocated memory passed as a parameter. Thus, any event handling function that receives parameter arrays must make a copy if they need to be retained after returning, and the same goes for the contents of any string or opaque values.

Initialization and Cleanup

Before emitting any PEDL events or running the manager, SMEDL must be initialized. If SMEDL is no longer needed, it can also be deinitialized to free resources. Both of these functions are in the manager, SYNCSET_manager.h.

int init_manager(void)

Initialize SMEDL. Must be called before any other function in the SMEDL API.

Returns

Nonzero for success, zero for error

void free_manager(void)

Destroy all monitors and wrappers, close all connections, and free the resources used by SMEDL. May be called if SMEDL monitoring is no longer required.

Emitting PEDL Events

PEDL events are sent to PEDL by calling the following function in the global wrapper, SYNCSET_global_wrapper.h:

int raise_pedl_EVENT(SMEDLValue *identities, SMEDLValue *params, void *aux)

Raise a PEDL event. This function queues the event for later processing and immediately returns.

Parameters
  • identities – This parameter is provided for consistency with other event passing functions. It is ignored and may safely be set to NULL.

  • params – Array of event parameters

  • aux – Auxiliary data pointer. This pointer is passed through to any events triggered by this one. This can be used for provenance information or any other purpose.

Returns

Nonzero for success, zero for error

To actually process the event, the program must call run_manager().

Receiving PEDL Events

Whenever run_manager() is called, it is possible that there might be some PEDL events for the target program to receive back. For each PEDL event that can be received, the target program must provide the following function:

int perform_pedl_EVENT(SMEDLValue *identities, SMEDLValue *params, void *aux)

Receive a PEDL event. Called by SMEDL whenever a PEDL event needs to be returned to the target program.

Parameters
  • identities – This parameter is provided for consistency with other event passing functions. It will be set to NULL.

  • params – Array of event parameters

  • aux – Auxiliary data pointer. This pointer is passed through directly from whatever event triggered this one.

Returns

Nonzero for success, zero for error

As noted in Event Passing Conventions, the parameter array and the contents of any strings and opaques may become invalid at any point after the function returns. If they are needed longer than that, be sure to keep a copy.

Running SMEDL

SMEDL does not process PEDL and asynchronous events as soon as they are queued. It only happens when initiated by calling the following function in the manager, SYNCSET_manager.h:

int run_manager(void)

Allow SMEDL to run. When called, the following happens:

  1. SMEDL processes any pending PEDL events.

  2. SMEDL processes any events raised as a result. Synchronous events are passed to the appropriate destination within the synchronous set. Asynchronous events are passed to the transport adapter to be sent out. This continues until there are no more events pending in the global wrapper.

  3. The transport adapter is given a chance to receive any incoming asynchronous events that are ready. These are passed along to the global wrapper for processing, and any events raised as a result are handled as in #2.

Then, the function returns.

Returns

Nonzero for success, zero for error