An example metric library using the Arm® 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/arm/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 MAP 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);
}