Developing new metrics plugins¶
It is possible to extend Optimizer Studio by writing plugins for specific hardware or software applications.
The supplied header file metricsPlugin.h
can be used to develop a plugin to Optimizer Studio for sampling metrics. Optimizer Studio will
call this plugin's sample_*()
functions:
sample_system()
will be called for sampling system-wide eventssample_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 Studio to a logical cpu on the respective socket.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 Studio 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 Studio, should not be
deleted as long as the plugin is alive.
Upon initialization, Optimizer Studio 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 libexample-metrics.so examplePlugin.cpp
Using a custom plugin¶
This will create a shared object by the name of libexample-metrics.so In order to load it, it needs to be specified in knobs.yaml:
domains:
common:
plugins: [/path/to/libexample-metrics.so]
include_metrics: [example.my_counter]
On startup, if the library was loaded successfully Optimizer Studio will report:
Metrics plugin /path/to/libexample-metrics.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:
metric-name:
kind: example
key: value
Each metrics is required, at least, to define its kind
- kind
specifies the metric plugin to be loaded, and can be file
, shell
or a user-defined plugin.
Additional optional parameters are
- aggregated
- true
if the metric value is aggregated, false
if it represents the change since the last measurement.
- normalize_by
- the name of a metric that can be used to normalize this metric. It can be duration
, performance
or any other available metric.
When the example
plugin is loaded, Optimizer Studio invokes:
example_plugin->set_metric_parameter("metric-name", "key", "value");
The "file" plugin¶
Metrics whose value is read from a file can be defined using the included file plugin - libfile-metrics.so
.
Below is an example of a metric that is read from a file:
domains:
common:
metrics:
metric-name:
kind: file
type: plaintext
path: /tmp/metric
Beyond the mandatory kind
parameter, a metric must have, at least, path
, that specifies the path to the file containing the metric value.
The optional parameter type
can be either plaintext
or json
.
The defaults for the optional parameters are: aggregated: false
, normalize_by=""
, type: plaintext
.
The "shell" plugin¶
Metrics whose value can be calculated using a shell script can be defined using the included shell plugin - libshell-metrics.so
. Below is an example of a metric that is read from a file:
domains:
common:
metrics:
metric-name:
kind: shell
init_script: echo 0 > /tmp/metric
sample_script: awk '{print $2}' /tmp/metric
Beyond the mandatory kind
parameter, a metric must have, at least, sample_script
, that defines the script whose (standard) output is fed to the metric value.
The optional parameter init_script
can contain a script required as a prerequisite for the shell metric - it can define a variable or source an external code file.
The init_script
is run once upon metric initialization.
Notice, that all the shell metrics reside in the same shell, so that init_script
of one metric impacts every shell metric.
The defaults for the optional parameters are: aggregated: false
, normalize_by=""
.