Commit f0b8f7bf authored by Micha Mueller's avatar Micha Mueller
Browse files

Adapt perfevent plugin to sensorgroupsV2 architecture

parent 7d45a2a1
sensorTemplates {
sensor def1 {
interval 5000
mqttsuffix 0222
minValues 5
type PERF_TYPE_HARDWARE
config PERF_COUNT_HW_INSTRUCTIONS
}
template_group def1 {
interval 5000
mqttpart 02
minValues 5
sensor def2 {
interval 2000
type PERF_TYPE_HARDWARE
counter hw_instructions {
type PERF_TYPE_HARDWARE
config PERF_COUNT_HW_INSTRUCTIONS
mqttsuffix 22
}
}
template_group def2 {
interval 2000
cpus 1,2
}
}
sensors {
sensor hw_instructions {
default def1
mqttsuffix 0020
}
group hw_i {
default def1
mqttpart 23
}
sensor hw_branch_instructions {
default def2
mqttsuffix 0024
group hw_bi {
default def2
mqttpart 0024
counter hw_branch_instructions {
mqttsuffix
type PERF_TYPE_HARDWARE
config PERF_COUNT_HW_BRANCH_INSTRUCTIONS
}
}
group hw_bm {
default def2
cpus 0,2-3
mqttpart 002C
sensor hw_branch_misses {
default def2
mqttsuffix 002C
counter hw_branch_misses {
mqttsuffix
type PERF_TYPE_HARDWARE
config PERF_COUNT_HW_BRANCH_MISSES
cpus 0,2-3
}
}
groups {
group cache {
interval 2000
mqttprefix 02
minValues 3
cpus 2-3
sensor references {
mqttsuffix 10
type PERF_TYPE_HARDWARE
config PERF_COUNT_HW_CACHE_REFERENCES
}
sensor misses {
mqttsuffix 20
type PERF_TYPE_HARDWARE
config PERF_COUNT_HW_CACHE_MISSES
}
group cache {
interval 2000
mqttpart 02
minValues 3
cpus 2-3
counter references {
mqttsuffix 10
type PERF_TYPE_HARDWARE
config PERF_COUNT_HW_CACHE_REFERENCES
}
group sw {
interval 1000
mqttprefix 03
minValues 3
cpus 1-4
sensor sw_pagefaults {
mqttsuffix 30
type PERF_TYPE_SOFTWARE
config PERF_COUNT_SW_PAGE_FAULTS
}
counter misses {
mqttsuffix 20
type PERF_TYPE_HARDWARE
config PERF_COUNT_HW_CACHE_MISSES
}
}
sensor sw_context_switches {
mqttsuffix 34
type PERF_TYPE_SOFTWARE
config PERF_COUNT_SW_CONTEXT_SWITCHES
}
group sw {
interval 1000
mqttpart 03
minValues 3
cpus 1-4
counter sw_pagefaults {
mqttsuffix 30
type PERF_TYPE_SOFTWARE
config PERF_COUNT_SW_PAGE_FAULTS
}
counter sw_context_switches {
mqttsuffix 34
type PERF_TYPE_SOFTWARE
config PERF_COUNT_SW_CONTEXT_SWITCHES
}
sensor sw_cpu_migrations {
mqttsuffix 38
type PERF_TYPE_SOFTWARE
config PERF_COUNT_SW_CPU_MIGRATIONS
}
counter sw_cpu_migrations {
mqttsuffix 38
type PERF_TYPE_SOFTWARE
config PERF_COUNT_SW_CPU_MIGRATIONS
}
}
......@@ -73,11 +73,13 @@ public:
/**
* Read in the given configuration
*
* Overwriting this method is only required if a custom logic is really necessary!
*
* @param cfgPath Path to the config-file
*
* @return True on success, false otherwise
*/
bool readConfig(std::string cfgPath) final {
bool readConfig(std::string cfgPath) {
_cfgPath = cfgPath;
boost::property_tree::iptree cfg;
......@@ -214,7 +216,7 @@ public:
return _sensorGroupInterfaces;
}
private:
protected:
void storeSensorGroup(SGroup* sGroup) {
_sensorGroups.push_back(sGroup);
_sensorGroupInterfaces.push_back(sGroup);
......@@ -380,7 +382,6 @@ private:
return true;
}
protected:
/**
* Virtual interface method, responsible for setting global values specifically
* for its plugin.
......
/*
* PerfAttributes.h
*
* Created on: 13.08.2018
* Author: Micha Mueller
*/
#ifndef PERFEVENT_PERFATTRIBUTES_H_
#define PERFEVENT_PERFATTRIBUTES_H_
class PerfAttributes {
public:
PerfAttributes() :
_cpuId(0),
_fd(-1) {}
virtual ~PerfAttributes() {}
int getCpuId() const { return _cpuId; }
void setCpuId(int cpuId) { _cpuId = cpuId; }
protected:
int _cpuId;
int _fd;
};
#endif /* PERFEVENT_PERFATTRIBUTES_H_ */
......@@ -25,11 +25,12 @@ struct read_format {
} values[];
};
PerfSensorGroup::PerfSensorGroup(const std::string name) :
SensorGroupTemplate(name) {
_bufSize = 0;
_buf = NULL;
PerfSensorGroup::PerfSensorGroup(const std::string& name) :
SensorGroupTemplate(name),
_cpuId(0),
_fd(-1),
_bufSize(0) {
_buf = nullptr;
}
PerfSensorGroup::~PerfSensorGroup() {
......@@ -39,8 +40,7 @@ PerfSensorGroup::~PerfSensorGroup() {
}
void PerfSensorGroup::init(boost::asio::io_service& io) {
_cacheSize = _cacheInterval / _interval + 1;
_timer.reset(new boost::asio::deadline_timer(io, boost::posix_time::seconds(0)));
SensorGroupTemplate::init(io);
std::size_t bufSize = _sensors.size() * 16 + 8;
if (!_buf) {
......@@ -51,10 +51,6 @@ void PerfSensorGroup::init(boost::asio::io_service& io) {
_buf = new char[bufSize];
_bufSize = bufSize;
}
for (auto s : _sensors) {
s->initSensor(_cacheSize);
}
}
void PerfSensorGroup::start() {
......
......@@ -9,22 +9,27 @@
#define PERFSENSORGROUP_H_
#include "../../includes/SensorGroupTemplate.h"
#include "PerfAttributes.h"
#include "PerfSensorBase.h"
class PerfSensorGroup : public SensorGroupTemplate<PerfSensorBase>, public PerfAttributes {
class PerfSensorGroup : public SensorGroupTemplate<PerfSensorBase> {
public:
PerfSensorGroup(const std::string name);
PerfSensorGroup(const std::string& name);
virtual ~PerfSensorGroup();
void init(boost::asio::io_service& io) override;
void start() override;
void stop() override;
int getCpuId() const { return _cpuId; }
void setCpuId(int cpuId) { _cpuId = cpuId; }
private:
void read() override;
void readAsync() override;
int _cpuId;
int _fd;
char * _buf;
std::size_t _bufSize;
std::vector<uint64_t> _ids;
......
/*
* PerfSingleSensor.cpp
*
* Created on: 11.12.2017
* Author: Micha Mueller
*/
#include "PerfSingleSensor.h"
#include "timestamp.h"
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <asm/unistd.h>
#include <functional>
#include <limits.h>
PerfSingleSensor::PerfSingleSensor(const std::string& name) :
SensorBase(name), PerfSensorBase(name), SingleSensor(name) {}
PerfSingleSensor::~PerfSingleSensor() {}
void PerfSingleSensor::start() {
if (_keepRunning) {
//we have been started already
LOG(info) << "Sensor " << _name << " already running.";
return;
}
//open perf-counter
struct perf_event_attr pe;
memset(&pe, 0, sizeof(struct perf_event_attr));
pe.type = _type;
pe.size = sizeof(struct perf_event_attr);
pe.config = _config;
pe.disabled = 1;
pe.exclude_kernel = 0;
pe.exclude_hv = 1;
//perf_event_open()
_fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, -1, 0);
if (_fd == -1) {
LOG(error) << "Failed to open performance-counter \"" << _name << "\"";
return;
}
ioctl(_fd, PERF_EVENT_IOC_RESET, 0);
ioctl(_fd, PERF_EVENT_IOC_ENABLE, 0);
_keepRunning = 1;
_pendingTasks++;
_timer->async_wait(std::bind(&PerfSingleSensor::readAsync, this));
LOG(info) << "Sensor " << _name << " started.";
}
void PerfSingleSensor::stop() {
_keepRunning = 0;
if(_fd != -1) {
close(_fd);
_fd = -1;
}
LOG(info) << "Sensor " << _name << " stopped.";
}
void PerfSingleSensor::storeReading(reading_t reading, unsigned cacheIndex) {
_readingQueue->push(reading);
_cache[_cacheIndex] = reading;
_cacheIndex = (_cacheIndex + 1) % _cacheSize;
//want to store absolute values here
if (ULLONG_MAX - _latestValue.value < reading.value) {
_latestValue.value = reading.value - (ULLONG_MAX - _latestValue.value);
} else {
_latestValue.value += reading.value;
}
_latestValue.timestamp = reading.timestamp;
}
void PerfSingleSensor::read() {
reading_t reading;
reading.timestamp = getTimestamp();
unsigned long long count;
if (::read(_fd, &count, sizeof(long long)) < 0) {
LOG(error) << _name << " could not read value";
return;
}
if (count >= _latestValue.value) {
reading.value = count - _latestValue.value;
} else {
//the counter overflow since last read
//according to perf_event_open() man-page u64 (=64bit) is used for event values
//--> unsigned long long is usually also 64bit
reading.value = count + (ULLONG_MAX - _latestValue.value);
}
#ifdef DEBUG
LOG(debug) << _name << ": \"" << reading.value << "\"";
#endif
storeReading(reading, _cacheIndex);
}
void PerfSingleSensor::readAsync() {
uint64_t now = getTimestamp();
read();
if (_timer && _keepRunning) {
uint64_t next = now + MS_TO_NS(_interval);
_timer->expires_at(timestamp2ptime(next));
_pendingTasks++;
_timer->async_wait(std::bind(&PerfSingleSensor::readAsync, this));
}
_pendingTasks--;
}
/*
* PerfSingleSensor.h
*
* Created on: 11.12.2017
* Author: Micha Mueller
*/
#ifndef PERFSINGLESENSOR_H_
#define PERFSINGLESENSOR_H_
#include "PerfAttributes.h"
#include "PerfSensorBase.h"
#include "../../includes/SingleSensor.h"
class PerfSingleSensor : public PerfSensorBase, public PerfAttributes, public SingleSensor {
public:
PerfSingleSensor(const std::string& name);
virtual ~PerfSingleSensor();
void start() override;
void stop() override;
private:
void storeReading(reading_t reading, unsigned cacheIndex) override;
void read() override;
void readAsync() override;
};
#endif /* PERFSINGLESENSOR_H_ */
......@@ -17,6 +17,9 @@
using namespace std;
PerfeventConfigurator::PerfeventConfigurator() {
_groupName = "group";
_baseName = "counter";
//set up enum-maps to map string from cfgFile to an enum value defined in linux/perf_event.h
_enumType["PERF_TYPE_HARDWARE"] = PERF_TYPE_HARDWARE;
_enumType["PERF_TYPE_SOFTWARE"] = PERF_TYPE_SOFTWARE;
......@@ -54,178 +57,133 @@ PerfeventConfigurator::PerfeventConfigurator() {
PerfeventConfigurator::~PerfeventConfigurator() {}
bool PerfeventConfigurator::derivedReadConfig(boost::property_tree::iptree& cfg) {
//read cpu-lists for template sensors
BOOST_FOREACH(boost::property_tree::iptree::value_type &sensor, cfg.get_child("sensorTemplates")) {
if (STRCMP(sensor, "sensor")) {
if (!sensor.second.empty()) {
//check if cpus-list is given for this template sensor
boost::optional<boost::property_tree::iptree&> cpus = sensor.second.get_child_optional("cpus");
if(cpus) {
LOG(debug) << "Reading CPUs for \"" << sensor.second.data() << "\"";
std::set<int> cpuVec = parseCpuString(cpus.get().data());
_templateCpus.insert(templateCpuMap_t::value_type(sensor.second.data(), cpuVec));
void PerfeventConfigurator::sensorBase(PerfSensorBase& s, CFG_VAL config) {
/*
* Custom code, as perf-event is an extra special plugin
*/
BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
if (boost::iequals(val.first, "type")) {
enumMap_t::iterator it = _enumType.find(val.second.data());
if(it != _enumType.end()) {
s.setType(it->second);
//LOG(debug) << " Type: " << val.second.data() << " (= " << s.getType() << ")";
} else {
LOG(warning) << " Type \"" << val.second.data() << "\" not known.";
}
} else if (boost::iequals(val.first, "config")) {
if (s.getType() == PERF_TYPE_BREAKPOINT) {
//leave config zero
} else if (s.getType() == PERF_TYPE_RAW) {
//read in custom hex-value
unsigned long config = stoul(val.second.data(), 0, 16);
s.setConfig(config);
LOG(debug) << " Config: Raw value: " << s.getConfig();
} else {
enumMap_t::iterator it = _enumConfig.find(val.second.data());
if(it != _enumConfig.end()) {
s.setConfig(it->second);
//LOG(debug) << " Config: " << val.second.data() << " (= " << s.getConfig() << ")";
} else {
LOG(warning) << " Config \"" << val.second.data() << "\" not known.";
}
}
}
}
}
//read one sensor at a time
BOOST_FOREACH(boost::property_tree::iptree::value_type &sensor, cfg.get_child("sensors")) {
if (STRCMP(sensor, "sensor")) {
LOG(debug) << "Sensor \"" << sensor.second.data() << "\"";
if (!sensor.second.empty()) {
PerfSingleSensor perfSensor(sensor.second.data());
void PerfeventConfigurator::sensorGroup(PerfSensorGroup& s, CFG_VAL config) {
ADD {
//no group attributes currently
}
}
//first check if default counter is given
boost::optional<boost::property_tree::iptree&> defaultC = sensor.second.get_child_optional("default");
if(defaultC) {
LOG(debug) << " Using \"" << defaultC.get().data() << "\" as default.";
sensorMap_t::iterator it = _templateSensors.find(defaultC.get().data());
if(it != _templateSensors.end()) {
perfSensor = it->second;
perfSensor.setName(sensor.second.data());
} else {
LOG(warning) << " Template sensor \"" << defaultC.get().data() << "\" not found! Using standard values.";
bool PerfeventConfigurator::readConfig(std::string cfgPath) {
/*
* Custom code, as perf-event is an extra special plugin
*/
_cfgPath = cfgPath;
boost::property_tree::iptree cfg;
boost::property_tree::read_info(cfgPath, cfg);
//read global variables (if present overwrite those from global.conf)
readGlobal(cfg);
//read groups and templates for groups
BOOST_FOREACH(boost::property_tree::iptree::value_type &val, cfg) {
if (boost::iequals(val.first, "template_" + _groupName)) {
LOG(debug) << "Template " << _groupName << " \"" << val.second.data() << "\"";
if (!val.second.empty()) {
PerfSensorGroup* group = new PerfSensorGroup(val.second.data());
if (readSensorGroup(*group, val.second)) {
//check if cpus-list is given for this template group
boost::optional<boost::property_tree::iptree&> cpus = val.second.get_child_optional("cpus");
if(cpus) {
LOG(debug) << "Reading CPUs for \"" << val.second.data() << "\"";
std::set<int> cpuVec = parseCpuString(cpus.get().data());
_templateCpus.insert(templateCpuMap_t::value_type(val.second.data(), cpuVec));
}
}
//initialize set with cpuIDs
//default cpuSet: contains all cpuIDs
std::set<int> cpuSet;
for (int i = 0; i < get_nprocs(); i++) {
cpuSet.insert(i);
}
//check if (differing) cpus-list is given; if so, overwrite default cpuVec
boost::optional<boost::property_tree::iptree&> cpus = sensor.second.get_child_optional("cpus");
if (cpus) { //cpu list given
cpuSet = parseCpuString(cpus.get().data());
} else if (defaultC) { //cpu list not given, but perhaps template counter has one
templateCpuMap_t::iterator itC = _templateCpus.find(defaultC.get().data());
if(itC != _templateCpus.end()) {
cpuSet = itC->second;
auto ret = _templateSensorGroups.insert(std::pair<std::string, PerfSensorGroup*>(val.second.data(), group));
if(!ret.second) {
LOG(warning) << "Template " << _groupName << " " << val.second.data() << " already exists! Omitting...";
}
} else {
LOG(warning) << "Template " << _groupName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
}
}
} else if (boost::iequals(val.first, _groupName)) {
LOG(debug) << _groupName << " \"" << val.second.data() << "\"";
if (!val.second.empty()) {
PerfSensorGroup group(val.second.data());
if (readSensorGroup(group, val.second)) {
//now for the cpus...
//initialize set with cpuIDs
//default cpuSet: contains all cpuIDs
std::set<int> cpuSet;
for (int i = 0; i < get_nprocs(); i++) {
cpuSet.insert(i);
}
//check if (differing) cpus-list is given; if so, overwrite default cpuVec
boost::optional<boost::property_tree::iptree&> cpus = val.second.get_child_optional("cpus");
if (cpus) { //cpu list given
cpuSet = parseCpuString(cpus.get().data());
} else { //cpu list not given, but perhaps template counter has one
boost::optional<boost::property_tree::iptree&> def = val.second.get_child_optional("default");
if (def) {
templateCpuMap_t::iterator itC = _templateCpus.find(def.get().data());
if(itC != _templateCpus.end()) {
cpuSet = itC->second;
}
}
}
//read remaining values
if(readSingleSensor(perfSensor, sensor.second)) {
//create distinct perfCounter and mqttSuffix per CPU
string startMqtt = perfSensor.getMqtt();
string startMqtt = group.getMqttPart();
//customize perfCounter for every CPU
//customize perfCounterGroup for every CPU
for (auto i : cpuSet) {
PerfSingleSensor* perfS = new PerfSingleSensor(sensor.second.data());
*perfS = perfSensor;
PerfSensorGroup* perfSG = new PerfSensorGroup(group);
string incMqtt = increaseMqtt(startMqtt, i);
perfS->setName(perfS->getName() + std::to_string(i));
perfS->setCpuId(i);
perfS->setMqtt(_mqttPrefix + incMqtt);
LOG(debug) << " CPU " << perfS->getCpuId() << " using MQTT-Topic " << perfS->getMqtt();
_sensors.push_back(perfS);