custom1.c

An example metric library using the Linaro MAP Metric Plugin API.

This implements the functions as defined in Metric Plugin Template and makes calls to the Metric plugin API. This plugin provides a custom metric showing the number of interrupts handled by the system, as obtained from /proc/interrupts.

Compile the plugin using this command:

gcc -fPIC -I/path/to/forge/metrics/include -shared -o libcustom1.so custom1.c

For the corresponding definition file to enable the libcustom1.so metric library to be used by compatible profilers, see custom1.xml.

/* The following functions are assumed to be async-signal-safe, although not
* required by POSIX:
*
* strchr strstr strtoull
*/

#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include "allinea_metric_plugin_api.h"

#define PROC_STAT "/proc/stat"

#define ERROR_NO_PROC_STAT                        1000

#define BUFSIZE     256
#define OVERLAP    64

#ifndef min
#define min(x, y) ( ((x) < (y)) ? (x) : (y) )
#endif

static uint64_t previous = 0;
static int have_previous = 0;

int allinea_plugin_initialise(plugin_id_t plugin_id, void *unused)
{
    // Check that /proc/interrupts exists.
    if (access(PROC_STAT, F_OK) != 0) {
        if (errno == ENOENT)
            allinea_set_plugin_error_messagef(plugin_id, ERROR_NO_PROC_STAT,
                "Not supported (no /proc/interrupts)");
        else
            allinea_set_plugin_error_messagef(plugin_id, errno,
                "Error accessing %s: %s", PROC_STAT, strerror(errno));
        return -1;
    }
}

int allinea_plugin_cleanup(plugin_id_t plugin_id, void *unused)
{
}

int sample_interrupts(metric_id_t metric_id, struct timespec *in_out_sample_time, uint64_t *out_value)
{
    // Main buffer. Add an extra byte for the '\0' we add below.
    char buf[BUFSIZE + 1];
    *in_out_sample_time = allinea_get_current_time();
    // We must use the allinea_safe variants of open / read / write / close so
    // that we are not included in the I/O accounting of the Forge sampler.
    const int fd = allinea_safe_open(PROC_STAT, O_RDONLY);
    if (fd == -1) {
        allinea_set_metric_error_messagef(metric_id, errno,
            "Error opening %s: %d", PROC_STAT, strerror(errno));
        return -1;
    }
    for (;;) {
        const ssize_t bytes_read = allinea_safe_read_line(fd, buf, BUFSIZE);
        if (bytes_read == -1) {
            // read failed
            allinea_set_metric_error_messagef(metric_id, errno,
                "Error opening %s: %d", PROC_STAT, strerror(errno));
            break;
        }
        if (bytes_read == 0) {
            // end of file
            break;
        }
        if (strncmp(buf, "intr ", 5)==0) { // Check if this is the interrupts line.
            // The format of the line is:
            // intr <total> <intr 1 count> <intr 2 count> ...
            // Check we have the total by looking for the space after it.
            const char *total = buf + /* strlen("intr ") */ 5;
            char *space = strchr(total, ' ');
            if (space) {
                uint64_t current;
                // NUL-terminate the total.
                *space = '\0';
                // total now points to the NUL-terminated total. Convert it to
                // an integer.
                current = strtoull(total, NULL, 10);
                if (have_previous)
                    *out_value = current - previous;
                previous = current;
                have_previous = 1;
                break;
            }
        }
    }
    allinea_safe_close(fd);
}