Commit 271a187e authored by Micha Mueller's avatar Micha Mueller
Browse files

Add minimal working implementation of Perfcounter-groups (although it implies some overhead)

parent 642469ae
......@@ -68,13 +68,14 @@ install: $(TARGET)
install -m 644 $(TARGET).conf $(DCDBDEPLOYPATH)/etc
src/Sensor.o: CXXFLAGS+= $(PLUGINFLAGS)
src/SensorGroup.o: CXXFLAGS+= $(PLUGINFLAGS)
src/sensors/%.o: CXXFLAGS+= $(PLUGINFLAGS) -I$(DCDBDEPSPATH)/bacnet-stack-$(BACNET-STACK_VERSION)/include -I$(DCDBDEPSPATH)/bacnet-stack-$(BACNET-STACK_VERSION)/ports/$(BACNET_PORT)
#src/sensors/*/%.o: %.cpp
libdcdbplugin_sysfs.$(LIBEXT): src/Sensor.o src/sensors/sysfs/SysfsSensor.o src/sensors/sysfs/SysfsConfigurator.o
$(CXX) $(LIBFLAGS)$@ -o $@ $^ -L$(DCDBDEPLOYPATH)/lib/ -lboost_log -lboost_system
libdcdbplugin_perfevent.$(LIBEXT): src/Sensor.o src/sensors/perfevent/PerfCounter.o src/sensors/perfevent/PerfeventConfigurator.o
libdcdbplugin_perfevent.$(LIBEXT): src/Sensor.o src/SensorGroup.o src/sensors/perfevent/PerfCounter.o src/sensors/perfevent/PerfeventConfigurator.o src/sensors/perfevent/PerfCounterGroup.o
$(CXX) $(LIBFLAGS)$@ -o $@ $^ -L$(DCDBDEPLOYPATH)/lib/ -lboost_log -lboost_system
libdcdbplugin_ipmi.$(LIBEXT): src/Sensor.o src/sensors/ipmi/IPMISensor.o src/sensors/ipmi/IPMIHost.o src/sensors/ipmi/IPMIConfigurator.o
......
......@@ -15,43 +15,68 @@ CounterTemplate {
}
counters {
counter hw_instructions {
default def1
mqttsuffix 0020
}
counter hw_branch_instructions {
default def2
mqttsuffix 0024
config PERF_COUNT_HW_BRANCH_INSTRUCTIONS
}
counter hw_instructions {
default def1
mqttsuffix 0020
}
counter hw_branch_misses {
default def2
mqttsuffix 002C
config PERF_COUNT_HW_BRANCH_MISSES
cpus 0,2-3
}
counter sw_pagefaults {
interval 5000
mqttsuffix 0030
type PERF_TYPE_SOFTWARE
config PERF_COUNT_SW_PAGE_FAULTS
}
counter sw_context_switches {
interval 5000
mqttsuffix 0034
type PERF_TYPE_SOFTWARE
config PERF_COUNT_SW_CONTEXT_SWITCHES
}
counter hw_branch_instructions {
default def2
mqttsuffix 0024
config PERF_COUNT_HW_BRANCH_INSTRUCTIONS
}
counter sw_cpu_migrations {
interval 5000
mqttsuffix 0038
type PERF_TYPE_SOFTWARE
config PERF_COUNT_SW_CPU_MIGRATIONS
}
counter hw_branch_misses {
default def2
mqttsuffix 002C
config PERF_COUNT_HW_BRANCH_MISSES
cpus 0,2-3
}
}
groups {
group cache {
interval 2000
mqttprefix 02
minValues 3
cpus 2-3
counter references {
mqttsuffix 10
type PERF_TYPE_HARDWARE
config PERF_COUNT_HW_CACHE_REFERENCES
}
counter misses {
mqttsuffix 20
type PERF_TYPE_HARDWARE
config PERF_COUNT_HW_CACHE_MISSES
}
}
group sw {
interval 1000
mqttprefix 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
}
counter sw_cpu_migrations {
mqttsuffix 38
type PERF_TYPE_SOFTWARE
config PERF_COUNT_SW_CPU_MIGRATIONS
}
}
}
......@@ -67,6 +67,7 @@ public:
delete g;
}
_sensors.clear();
_sensorGroups.clear();
return true;
}
......
......@@ -289,6 +289,10 @@ void HttpsServer::requestHandler::operator()(server::request const &request, ser
s->init(_httpsServer._io);
s->startPolling();
}
for(auto g : p.configurator->getSensorGroups()) {
g->init(_httpsServer._io);
g->startPolling();
}
//continue MQTTPusher
_httpsServer._mqttPusher->cont();
......
......@@ -85,13 +85,12 @@ void MQTTPusher::push() {
for(auto& p : _plugins) {
if(_doHalt) {
//for faster response
continue;
break;
}
for(auto s : p.configurator->getSensors()) {
if (s->getSizeOfReadingQueue() >= s->getMinValues()) {
sendReadings(s, reads, totalCount);
}
}
for(auto g : p.configurator->getSensorGroups()) {
for(auto s : g->getSensors()) {
......
......@@ -25,6 +25,8 @@ typedef struct {
*/
class Sensor {
friend class PerfCounterGroup;
public:
Sensor(const std::string name);
......
......@@ -55,17 +55,16 @@ void SensorGroup::setCacheInterval(unsigned cacheInterval) {
_cacheInterval = cacheInterval;
}
void SensorGroup::init(boost::asio::io_service& io) {
if (!_timer) {
_timer = new boost::asio::deadline_timer(io, boost::posix_time::seconds(0));
}
for (auto s : _sensors) {
s->init(io);
}
}
void SensorGroup::wait() {
while(_pendingTasks) {
sleep(1);
}
}
void SensorGroup::pushBackSensor(Sensor* s) {
_sensors.push_back(s);
}
std::vector<Sensor*>& SensorGroup::getSensors() {
return _sensors;
}
......@@ -34,20 +34,16 @@ public:
void setCacheInterval(unsigned cacheInterval);
//can be overwritten methods
virtual void init(boost::asio::io_service& io);
//have to be overwritten methods
virtual void init(boost::asio::io_service& io) = 0;
virtual void read() = 0;
virtual void readAsync() = 0;
virtual void startPolling() = 0;
virtual void stopPolling() = 0;
std::vector<Sensor*>& getSensors() {
return _sensors;
}
void wait();
void pushBackSensor(Sensor* s);
std::vector<Sensor*>& getSensors();
protected:
......
......@@ -12,6 +12,8 @@
class PerfCounter : public Sensor{
friend class PerfCounterGroup;
public:
PerfCounter(const std::string& name);
virtual ~PerfCounter();
......
/*
* PerfCounterGroup.cpp
*
* Created on: 04.08.2018
* Author: Micha Mueller
*/
#include "PerfCounterGroup.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>
//the read group data will have this format
struct read_format {
uint64_t nr;
struct {
uint64_t value;
uint64_t id;
} values[];
};
PerfCounterGroup::PerfCounterGroup(const std::string name) :
SensorGroup(name) {
_bufSize = 0;
_buf = NULL;
_fd = -1;
_cpuId = 0;
}
PerfCounterGroup::~PerfCounterGroup() {
if (_buf) {
delete[] _buf;
}
}
void PerfCounterGroup::init(boost::asio::io_service& io) {
if (!_timer) {
_timer = new boost::asio::deadline_timer(io, boost::posix_time::seconds(0));
}
std::size_t bufSize = _sensors.size() * 16 + 8;
if (!_buf) {
_buf = new char[bufSize];
_bufSize = bufSize;
} else if (bufSize > _bufSize) {
delete _buf;
_buf = new char[bufSize];
_bufSize = bufSize;
}
for (auto c : _sensors) {
c->setCacheInterval(_cacheInterval);
c->setInterval(_interval);
c->init(io);
}
}
void PerfCounterGroup::read() {
reading_t reading;
reading.timestamp = getTimestamp();
struct read_format* rf = (struct read_format*) _buf;
unsigned long long count;
if (::read(_fd, _buf, _bufSize) < 0) {
LOG(error) << "Countergroup" << _name << " could not read value";
return;
}
//iterate over all values returned by ::read()
for (unsigned i = 0; i < rf->nr; i++) {
uint64_t val = rf->values[i].value;
//iterate over all counters and find the one with matching id
for (unsigned j = 0; j < _sensors.size(); j++) {
if (rf->values[i].id == _ids[j]) {
if (val >= _sensors[j]->_latestValue.value) {
reading.value = val - _sensors[j]->_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 = val + (ULLONG_MAX - _sensors[j]->_latestValue.value);
}
#ifdef DEBUG
LOG(debug) << _name << "::" << _sensors[j]->getName() << ": \"" << reading.value << "\"";
#endif
_sensors[j]->storeReading(reading);
//TODO make latestValue assignment in storeReading()
_sensors[j]->_latestValue.value = val;
_sensors[j]->_latestValue.timestamp = reading.timestamp;
break;
}
}
}
}
void PerfCounterGroup::readAsync() {
uint64_t now = getTimestamp();
read();
if (_timer != NULL && _keepRunning) {
uint64_t next = now + MS_TO_NS(_interval);
_timer->expires_at(timestamp2ptime(next));
_pendingTasks++;
_timer->async_wait(std::bind(&PerfCounterGroup::readAsync, this));
}
_pendingTasks--;
}
void PerfCounterGroup::startPolling() {
if (_keepRunning) {
//we have been started already
LOG(info) << "Countergroup " << _name << " already running.";
return;
}
uint64_t id;
std::vector<Sensor*>::iterator iter = _sensors.begin();
//open perf-counters
struct perf_event_attr pe;
PerfCounter* pc = (PerfCounter*) (*iter);
memset(&pe, 0, sizeof(struct perf_event_attr));
pe.size = sizeof(struct perf_event_attr);
pe.type = pc->_type;
pe.config = pc->_config;
pe.disabled = 1;
pe.exclude_kernel = 0;
pe.exclude_hv = 1;
pe.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
//perf_event_open() group leader
_fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, -1, 0);
ioctl(_fd, PERF_EVENT_IOC_ID, &id);
//store ids to make them distinguishable when reading
_ids.push_back(id);
if (_fd == -1) {
LOG(error) << "Failed to open performance-counter group \"" << _name << "\"";
return;
}
LOG(debug) << " " << _name << "::" << pc->getName() << " opened with ID " << std::to_string(id);
iter++;
pe.disabled = 0;
//open all other counters attached to group leader
for(; iter != _sensors.end(); iter++) {
pc = (PerfCounter*) (*iter);
int fd;
pe.type = pc->_type;
pe.config = pc->_config;
fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, _fd, 0);
//store id, so that we can match counters with values later (see read())
ioctl(fd, PERF_EVENT_IOC_ID, &id);
_ids.push_back(id);
LOG(debug) << " " << _name << "::" << pc->getName() << " opened with ID " << std::to_string(id);
}
ioctl(_fd, PERF_EVENT_IOC_RESET, 0);
ioctl(_fd, PERF_EVENT_IOC_ENABLE, 0);
_keepRunning = 1;
_pendingTasks++;
_timer->async_wait(std::bind(&PerfCounterGroup::readAsync, this));
LOG(info) << "Countergroup " << _name << " started.";
}
void PerfCounterGroup::stopPolling() {
_keepRunning = 0;
if(_fd != -1) {
close(_fd);
_fd = -1;
}
LOG(info) << "Countergroup " << _name << " stopped.";
}
/*
* PerfCounterGroup.h
*
* Created on: 04.08.2018
* Author: Micha Mueller
*/
#ifndef PERFCOUNTERGROUP_H_
#define PERFCOUNTERGROUP_H_
#include "../../SensorGroup.h"
#include "PerfCounter.h"
class PerfCounterGroup : public SensorGroup {
public:
PerfCounterGroup(const std::string name);
virtual ~PerfCounterGroup();
int getCpuId() const {
return _cpuId;
}
void setCpuId(int cpuId) {
_cpuId = cpuId;
}
void init(boost::asio::io_service& io);
void read();
void readAsync();
void startPolling();
void stopPolling();
private:
int _fd;
int _cpuId;
char * _buf;
std::size_t _bufSize;
std::vector<uint64_t> _ids;
};
#endif /* PERFCOUNTERGROUP_H_ */
......@@ -6,6 +6,7 @@
*/
#include "PerfeventConfigurator.h"
#include "PerfCounterGroup.h"
#include <iostream>
#include <sstream>
#include <string>
......@@ -167,6 +168,70 @@ bool PerfeventConfigurator::readConfig(std::string cfgPath) {
}
}
}
//read counter groups
BOOST_FOREACH(boost::property_tree::iptree::value_type &group, cfg.get_child("groups")) {
if (boost::iequals(group.first, "group")) {
LOG(debug) << "Group \"" << group.second.data() << "\"";
if (!group.second.empty()) {
PerfCounterGroup perfGroup(group.second.data());
//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 = group.second.get_child_optional("cpus");
if (cpus) { //cpu list given
cpuSet = parseCpuString(cpus.get().data());
}
std::string mqttPart = "";
boost::optional<boost::property_tree::iptree&> mqtt = group.second.get_child_optional("mqttprefix");
if (mqtt) {
mqttPart = mqtt.get().data();
}
//read remaining values
BOOST_FOREACH(boost::property_tree::iptree::value_type &val, group.second) {
if (boost::iequals(val.first, "interval")) {
perfGroup.setInterval(stoull(val.second.data()));
} else if (boost::iequals(val.first, "minValues")) {
perfGroup.setMinValues(stoull(val.second.data()));
}
}
//customize perfGroup for every CPU
for (auto i : cpuSet) {
PerfCounterGroup* perfCG = new PerfCounterGroup(group.second.data());
*perfCG = perfGroup;
perfCG->setName(perfCG->getName() + std::to_string(i));
perfCG->setCpuId(i);
//read counters
BOOST_FOREACH(boost::property_tree::iptree::value_type &val, group.second) {
if (boost::iequals(val.first, "counter")) {
LOG(debug) << "Counter \"" << val.second.data() << "\"";
PerfCounter* perfCounter = new PerfCounter(val.second.data() + std::to_string(i));
perfCounter->setInterval(perfGroup.getInterval());
perfCounter->setMinValues(perfGroup.getMinValues());
perfCounter->setCpuId(i);
if(readCounter(*perfCounter, val.second)) {
perfCounter->setMqtt(_mqttPrefix + mqttPart + increaseMqtt(perfCounter->getMqtt(), i));
LOG(debug) << " Counter " << perfCounter->getName() << " on CPU " << perfCG->getCpuId() << " using MQTT-Topic " << perfCounter->getMqtt();
perfCG->pushBackSensor(perfCounter);
} else {
LOG(warning) << " Counter \"" << val.second.data() << "\" has bad values! Ignoring...";
}
}
}
_sensorGroups.push_back(perfCG);
}
}
}
}
return true;
}
......@@ -206,7 +271,7 @@ bool PerfeventConfigurator::readCounter(PerfCounter& counter, boost::property_tr
}
}
} else if (boost::iequals(val.first, "cpus")) {
//avoid unncecessary "Value not recognized" message. cpus are handled in readConfig()
//avoid unnecessary "Value not recognized" message. cpus are handled in readConfig()
} else if (boost::iequals(val.first, "default")) {
//avoid unnecessary "Value not recognized" message
} else {
......
......@@ -72,6 +72,7 @@ private:
*/
std::set<int> parseCpuString(const std::string& cpuString);
//TODO add possibility for template groups
counterMap_t _templateCounters;
templateCpuMap_t _templateCpus;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment