Commit 37e9f37a authored by Michael Ott's avatar Michael Ott
Browse files

Check for errors when opening file descriptors for additional sensors in a group and obtaining IDs

parent 18aae967
......@@ -2,12 +2,11 @@
* PerfSensorGroup.cpp
*
* Created on: 04.08.2018
* Author: Micha Mueller
* Author: Micha Mueller, Carla Guillen
*/
#include "PerfSensorGroup.h"
#include "timestamp.h"
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/perf_event.h>
......@@ -19,6 +18,8 @@
//the read group data will have this format
struct read_format {
uint64_t nr;
uint64_t time_enabled; /* if PERF_FORMAT_TOTAL_TIME_ENABLED */
uint64_t time_running; /* if PERF_FORMAT_TOTAL_TIME_RUNNING */
struct {
uint64_t value;
uint64_t id;
......@@ -27,9 +28,14 @@ struct read_format {
PerfSensorGroup::PerfSensorGroup(const std::string& name) :
SensorGroupTemplate(name),
_sensorGroupLeader(false),
_cpuId(0),
_fd(-1),
_bufSize(0) {
_group_fd(-1),
_bufSize(0),
_latest_time_enabled (0),
_latest_time_running(0),
_lastValid(true),
_maxCorrection(20){
_buf = nullptr;
}
......@@ -42,7 +48,21 @@ PerfSensorGroup::~PerfSensorGroup() {
void PerfSensorGroup::init(boost::asio::io_service& io) {
SensorGroupTemplate::init(io);
std::size_t bufSize = _sensors.size() * 16 + 8;
/* Allocate buffer to read in later. Reading struct has the following format:
*
* struct read_format {
* u64 nr; // The number of events
* u64 time_enabled; // if PERF_FORMAT_TOTAL_TIME_ENABLED
* u64 time_running; // if PERF_FORMAT_TOTAL_TIME_RUNNING
* struct {
* u64 value; // The value of the event
* u64 id; // if PERF_FORMAT_ID
* } values[nr];
* };
*
* Therefore we require 16 byte per sensor plus an additional 8*3 byte
*/
std::size_t bufSize = _sensors.size() * 16 + 24;
if (!_buf) {
_buf = new char[bufSize];
_bufSize = bufSize;
......@@ -51,6 +71,11 @@ void PerfSensorGroup::init(boost::asio::io_service& io) {
_buf = new char[bufSize];
_bufSize = bufSize;
}
if(!_sensorGroupLeader) {
//only the sensorGroupLeader needs a timer
_timer = nullptr;
}
}
void PerfSensorGroup::start() {
......@@ -64,7 +89,7 @@ void PerfSensorGroup::start() {
//open perf-counters
struct perf_event_attr pe;
PerfSensorBase* pc = _sensors[0];
PerfSBPtr pc = _sensors[0];
memset(&pe, 0, sizeof(struct perf_event_attr));
pe.size = sizeof(struct perf_event_attr);
......@@ -72,19 +97,19 @@ void PerfSensorGroup::start() {
pe.config = pc->getConfig();
pe.disabled = 1;
pe.exclude_kernel = 0;
pe.exclude_hv = 1;
pe.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
pe.exclude_hv = 0;
pe.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;
//perf_event_open() group leader
_fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, -1, 0);
ioctl(_fd, PERF_EVENT_IOC_ID, &id);
_group_fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, -1, 0);
if (_group_fd == -1) {
LOG(error) << "Failed to open performance-counter group \"" << _groupName << "\":" << strerror(errno);
return;
}
ioctl(_group_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 \"" << _groupName << "\"";
return;
}
LOG(debug) << " " << _groupName << "::" << pc->getName() << " opened with ID " << std::to_string(id);
pe.disabled = 0;
......@@ -95,77 +120,127 @@ void PerfSensorGroup::start() {
pe.type = pc->getType();
pe.config = pc->getConfig();
fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, _fd, 0);
fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, _group_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) << " " << _groupName << "::" << pc->getName() << " opened with ID " << std::to_string(id);
if (fd != -1) {
int rc;
if ((rc = ioctl(fd, PERF_EVENT_IOC_ID, &id)) == 0) {
_ids.push_back(id);
_fds.push_back(fd);
LOG(debug) << " " << _groupName << "::" << pc->getName() << " opened with ID " << std::to_string(id);
} else {
LOG(debug) << " " << _groupName << "::" << pc->getName() << " errro obtaining ID: " << strerror(rc);
}
} else {
LOG(debug) << " " << _groupName << "::" << pc->getName() << " errro opening perf file descriptor: " << strerror(errno);
}
}
ioctl(_fd, PERF_EVENT_IOC_RESET, 0);
ioctl(_fd, PERF_EVENT_IOC_ENABLE, 0);
ioctl(_group_fd, PERF_EVENT_IOC_RESET, 0);
ioctl(_group_fd, PERF_EVENT_IOC_ENABLE, 0);
_keepRunning = 1;
_pendingTasks++;
_timer->async_wait(std::bind(&PerfSensorGroup::readAsync, this));
if (_sensorGroupLeader) {
_pendingTasks++;
_timer->async_wait(std::bind(&PerfSensorGroup::readAsync, this));
}
LOG(info) << "Sensorgroup " << _groupName << " started.";
}
void PerfSensorGroup::stop() {
_keepRunning = 0;
if(_fd != -1) {
close(_fd);
_fd = -1;
if(_group_fd != -1) {
for(auto& fd : _fds) {
close(fd);
}
_ids.clear();
_fds.clear();
close(_group_fd);
_group_fd = -1;
}
LOG(info) << "Sensorgroup " << _groupName << " stopped.";
}
void PerfSensorGroup::read() {
reading_t reading;
reading.timestamp = getTimestamp();
uint64_t timestamp = getTimestamp();
struct read_format* rf = (struct read_format*) _buf;
unsigned long long count;
if (::read(_fd, _buf, _bufSize) < 0) {
if (::read(_group_fd, _buf, _bufSize) < 0) {
LOG(error) << "Sensorgroup" << _groupName << " could not read value";
return;
}
double correction = 1;
bool validMeasurement=true;
uint64_t time_enabled=calculateIntervalValue(_latest_time_enabled, rf->time_enabled, ULLONG_MAX );
uint64_t time_running=calculateIntervalValue(_latest_time_running, rf->time_running, ULLONG_MAX );
//store latest times
_latest_time_enabled = rf->time_enabled;
_latest_time_running = rf->time_running;
if(time_running){
correction = static_cast<double>(time_enabled)/time_running;
} else {
LOG(info) << "PerfSensorGroup: Group: " << _groupName << " could not be measured. Time running==0";
validMeasurement = false;
}
if(correction > _maxCorrection || correction < 1 ){
LOG(info) << "PerfCounterGroup: Group: " << _groupName << " could not be measured. Correction factor ==" << correction;
validMeasurement = false;
}
if(!validMeasurement){
_lastValid=false;
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]) {
reading_t lV = _sensors[j]->getLatestValue();
if (val >= lV.value) {
reading.value = val - lV.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 - lV.value);
}
#ifdef DEBUG
LOG(debug) << _groupName << "::" << _sensors[j]->getName() << ": \"" << reading.value << "\"";
LOG(debug) << _groupName << "::" << _sensors[j]->getName() << ": \"" << val << "\"";
#endif
_sensors[j]->storeReading(reading, _cacheIndex);
if(_lastValid){
//storeReading takes care of delta computation and applies correction value on the result
_sensors[j]->storeReading(val, timestamp, correction, PerfSensorBase::MAXCOUNTERVALUE);
} else {
//Before we can compute correct values again after an invalid reading we have to update the lastRawValue first
_sensors[j]->setLastRaw(val);
}
break;
}
}
}
_cacheIndex = (_cacheIndex + 1) % _cacheSize;
if(validMeasurement){ //set valid for the next time to read
_lastValid = true;
}
}
void PerfSensorGroup::readAsync() {
uint64_t now = getTimestamp();
read();
if(_sensorGroupLeader) {
for(const auto& g : _fellowSensorGroups) {
g->read();
}
}
if (_timer && _keepRunning) {
uint64_t next = now + MS_TO_NS(_interval);
_timer->expires_at(timestamp2ptime(next));
_timer->expires_at(timestamp2ptime(nextReadingTime()));
_pendingTasks++;
_timer->async_wait(std::bind(&PerfSensorGroup::readAsync, this));
}
_pendingTasks--;
}
uint64_t PerfSensorGroup::calculateIntervalValue(uint64_t previous, uint64_t current, uint64_t maxValue){
if(previous > current) { //overflow
return current + (maxValue - previous);
}
return current - previous;
}
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