Skip to content

Development

Developing new metrics plugins

It is possible to extend Optimizer Runtime by writing plugins for specific hardware or software applications.

The supplied header file metricsPlugin.h can be used to develop a plugin to Optimizer Runtime for sampling metrics. Optimizer Runtime will call this plugin's sample_*() functions:

  1. sample_system() will be called for sampling system-wide events
  2. sample_socket() will be called for sampling socket-wide events. On a system with several physical chips, this function will be called concurrently for every socket. The calling thread will be affined by Optimizer Runtime to a logical cpu on the respective socket.
  3. sample_cpu() will be called for sampling per-logical-cpu events. This function will be called concurrently for each logical cpu. The calling thread will be affined by Optimizer Runtime to the respective logical cpu.

The sample*() functions will then send their sampled metrics to the MetricsPluginData object by using the ::insert() method of that class. The Metric struct, once passed to Optimizer Runtime, should not be deleted as long as the plugin is alive.

Upon initialization, Optimizer Runtime will pass metrics parameters that were defined in the knobs.yaml file by invoking calls to set_metric_parameter().

An example plugin

Below is an example of a plugin:

/*
 * examplePlugin.h
 *
 * This example plugin returns an aggregated counter normalized by time, 
 * and will appear under "example.my_counter" metric name
 *
 */

#include "metricsPlugin.h"

class examplePlugin : public MetricsPlugin {
public:
    examplePlugin();
    ~examplePlugin();
    void sample_system(MetricsPluginData *current_values, int phase) override;
    std::string get_name();
private:
    int m_counter;
    MetricsPlugin::Metric m_metric;
};

examplePlugin::examplePlugin() {
    m_metric.name="my_counter";
    m_metric.aggregated=true;
    m_metric.normalize_by="duration";
    m_counter=0;
}

void examplePlugin::sample_system(MetricsPluginData *current_values, int phase) {
    current_values->insert(m_metric,m_counter++);
}

std::string examplePlugin::get_name() {
    return "example";
}

examplePlugin::~examplePlugin() {
}

extern "C" MetricsPlugin* create_object() {
    return new examplePlugin;
}

extern "C" void destroy_object( MetricsPlugin* object ) {
    delete (examplePlugin *) object;
}

Compiling a plugin library

To compile the example in examplePlugin.cpp:

$ g++ -fpic -shared -o libexampleplugin.so examplePlugin.cpp

Using a custom plugin

This will create a shared object by the name of libexampleplugin.so In order to load it, it needs to be specified in knobs.yaml:

domains:
  common:
    plugins: [/path/to/libexampleplugin.so]
    include_metrics: [example.my_counter]

On startup, if the library was loaded successfully Optimizer Runtime will report:

Metrics plugin /path/to/libexampleplugin.so loaded successfully with name: example

Passing parameters to a plugin

It is possible to configure a custom plugin via knobs.yaml as follows:

domains:
  common:
    metrics:
      example.my_metric:
        key: value

When the example plugin is loaded, Optimizer Runtime will invoke:

`example_plugin->set_metric_parameter("my_metric","key","value");`

Using the "shell" plugin

It is possible to define metrics using shell scripts using the included plugin libshellplugin. Below is an example of a metric that is read from a file:

domains:
  common:
    metrics:
      shell.mymetric:
        sample_script: cat /tmp/metric
        aggregated: true
        normalize_by: duration
    plugins: [libshellplugin.so]

The metric names should have the prefix shell. and have at least one parameter sample_script which returns the value of the metric. The optional parameter aggregated can be either true or false. The optional parameter normalize_by defines how this metric will be normalized. It can either be by duration, performance, or by any other metric.