PerfSensorGroup.cpp 7.7 KB
Newer Older
1
/*
2
 * PerfSensorGroup.cpp
3
4
 *
 *  Created on: 04.08.2018
lu43jih's avatar
lu43jih committed
5
 *      Author: Micha Mueller, Carla Guillen
6
7
 */

8
9
#include "PerfSensorGroup.h"

10
11
12
13
14
15
16
17
18
19
20
#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;
21
22
	uint64_t time_enabled;  /* if PERF_FORMAT_TOTAL_TIME_ENABLED */
	uint64_t time_running;  /* if PERF_FORMAT_TOTAL_TIME_RUNNING */
23
24
25
26
27
28
	struct {
		uint64_t value;
		uint64_t id;
	} values[];
};

29
30
PerfSensorGroup::PerfSensorGroup(const std::string& name) :
	SensorGroupTemplate(name),
31
	_sensorGroupLeader(false),
32
	_cpuId(0),
33
	_group_fd(-1),
34
	_buf(nullptr),
lu43jih's avatar
lu43jih committed
35
36
	_bufSize(0),
	_latest_time_enabled (0),
37
	_latest_time_running(0),
lu43jih's avatar
lu43jih committed
38
	_lastValid(true),
39
40
41
42
43
44
45
46
47
48
49
50
51
	_maxCorrection(20) {}

PerfSensorGroup::PerfSensorGroup(const PerfSensorGroup& other) :
    SensorGroupTemplate(other),
    _sensorGroupLeader(false),
    _cpuId(other._cpuId),
    _group_fd(-1),
    _buf(nullptr),
    _bufSize(0),
    _latest_time_enabled(0),
    _latest_time_running(0),
    _lastValid(true),
    _maxCorrection(other._maxCorrection) {
52
53
}

54
PerfSensorGroup::~PerfSensorGroup() {
55
56
57
58
59
	if (_buf) {
		delete[] _buf;
	}
}

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
PerfSensorGroup& PerfSensorGroup::operator=(const PerfSensorGroup& other) {
  SensorGroupTemplate::operator=(other);
  _sensorGroupLeader = false;
  _cpuId = other._cpuId;
  _group_fd = -1;
  _buf = nullptr;
  _bufSize = 0;
  _latest_time_enabled = 0;
  _latest_time_running = 0;
  _lastValid = true;
  _maxCorrection = other._maxCorrection;

  return *this;
}

75
void PerfSensorGroup::init(boost::asio::io_service& io) {
76
	SensorGroupTemplate::init(io);
77

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
	/* 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;
93
94
95
96
97
98
99
100
	if (!_buf) {
		_buf = new char[bufSize];
		_bufSize = bufSize;
	} else if (bufSize > _bufSize) {
		delete _buf;
		_buf = new char[bufSize];
		_bufSize = bufSize;
	}
101
102
103
104
105

	if(!_sensorGroupLeader) {
		//only the sensorGroupLeader needs a timer
		_timer = nullptr;
	}
106
107
}

108
void PerfSensorGroup::start() {
109
110
	if (_keepRunning) {
		//we have been started already
111
		LOG(info) << "Sensorgroup " << _groupName << " already running.";
112
113
114
115
116
117
118
		return;
	}

	uint64_t id;

	//open perf-counters
	struct perf_event_attr pe;
119
	PerfSBPtr pc = _sensors[0];
120
121
122

	memset(&pe, 0, sizeof(struct perf_event_attr));
	pe.size = sizeof(struct perf_event_attr);
123
124
	pe.type = pc->getType();
	pe.config = pc->getConfig();
125
126
	pe.disabled = 1;
	pe.exclude_kernel = 0;
127
	pe.exclude_hv = 0;
128
	pe.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID  | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;
129
130

	//perf_event_open() group leader
131
	_group_fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, -1, 0);
132
133
134
135
	if (_group_fd == -1) {
		LOG(error) << "Failed to open performance-counter group \"" << _groupName << "\":" << strerror(errno);
		return;
	}
136
	ioctl(_group_fd, PERF_EVENT_IOC_ID, &id);
137
138
139
	//store ids to make them distinguishable when reading
	_ids.push_back(id);

140
	LOG(debug) << "  " << _groupName << "::" << pc->getName() << " opened with ID " << std::to_string(id);
141
142
143

	pe.disabled = 0;
	//open all other counters attached to group leader
144
145
	for(unsigned i = 1; i < _sensors.size(); i++) {
		pc = _sensors[i];
146
		int fd;
147
148
		pe.type = pc->getType();
		pe.config = pc->getConfig();
149

150
		fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, _group_fd, 0);
151
		//store id, so that we can match counters with values later (see read())
152
153
154
155
156
157
158
159
160
161
162
163
		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);
		}
164
165
	}

166
167
	ioctl(_group_fd, PERF_EVENT_IOC_RESET, 0);
	ioctl(_group_fd, PERF_EVENT_IOC_ENABLE, 0);
168
169

	_keepRunning = 1;
170
171
172
173
	if (_sensorGroupLeader) {
		_pendingTasks++;
		_timer->async_wait(std::bind(&PerfSensorGroup::readAsync, this));
	}
174
	LOG(info) << "Sensorgroup " << _groupName << " started.";
175
176
}

177
void PerfSensorGroup::stop() {
178
	_keepRunning = 0;
179
180
181
182
	if(_group_fd != -1) {
		for(auto& fd : _fds) {
			close(fd);
		}
183
		_ids.clear();
184
185
186
		_fds.clear();
		close(_group_fd);
		_group_fd = -1;
187
	}
188
	LOG(info) << "Sensorgroup " << _groupName << " stopped.";
189
190
}

191
void PerfSensorGroup::read() {
192
193
    ureading_t reading;
    reading.timestamp = getTimestamp();
194
195
196
197

	struct read_format* rf = (struct read_format*) _buf;
	unsigned long long count;

198
	if (::read(_group_fd, _buf, _bufSize) < 0) {
199
		LOG(error) << "Sensorgroup" << _groupName << " could not read value";
200
201
202
		return;
	}

lu43jih's avatar
lu43jih committed
203
	double correction = 1;
204
	bool validMeasurement=true;
lu43jih's avatar
lu43jih committed
205
206
207
	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 );

208
209
210
211
	//store latest times
	_latest_time_enabled = rf->time_enabled;
	_latest_time_running = rf->time_running;

lu43jih's avatar
lu43jih committed
212
213
	if(time_running){
    	correction = static_cast<double>(time_enabled)/time_running;
214
	} else {
lu43jih's avatar
lu43jih committed
215
		LOG(info) << "PerfSensorGroup: Group: " << _groupName << " could not be measured. Time running==0";
216
217
		validMeasurement = false;
    }
lu43jih's avatar
lu43jih committed
218
219
    if(correction > _maxCorrection  || correction < 1 ){
        LOG(info) << "PerfCounterGroup: Group: " << _groupName << " could not be measured. Correction factor ==" << correction;
220
221
222
223
        validMeasurement = false;
    }

    if(!validMeasurement){
224
    	_lastValid=false;
225
226
    	return;
    }
227
228
	//iterate over all values returned by ::read()
	for (unsigned i = 0; i < rf->nr; i++) {
229
		reading.value = rf->values[i].value;
230

231
232
233
		//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]) {
234
#ifdef DEBUG
235
				LOG(debug) << _groupName << "::" << _sensors[j]->getName() << " raw reading: \"" << reading.value << "\"";
236
#endif
237
				if(_lastValid){
238
					//storeReading takes care of delta computation and applies correction value on the result
239
					_sensors[j]->storeReading(reading, correction, PerfSensorBase::MAXCOUNTERVALUE);
240
241
				} else {
					//Before we can compute correct values again after an invalid reading we have to update the lastRawValue first
242
					_sensors[j]->setLastURaw(reading.value);
243
				}
244
245
246
247
				break;
			}
		}
	}
248
249
250
	if(validMeasurement){ //set valid for the next time to read
		_lastValid = true;
	}
251
252
}

253
void PerfSensorGroup::readAsync() {
254
	read();
255
	if(_sensorGroupLeader) {
256
		for(const auto& g : _fellowSensorGroups) {
257
258
259
260
			g->read();
		}
	}

Micha Mueller's avatar
Micha Mueller committed
261
	if (_timer && _keepRunning) {
262
		_timer->expires_at(timestamp2ptime(nextReadingTime()));
263
		_pendingTasks++;
264
		_timer->async_wait(std::bind(&PerfSensorGroup::readAsync, this));
265
266
	}
	_pendingTasks--;
267
}
268
269
270
271
272
273
274

uint64_t PerfSensorGroup::calculateIntervalValue(uint64_t previous, uint64_t current, uint64_t maxValue){
	if(previous > current) { //overflow
		return current + (maxValue - previous);
	}
	return current - previous;
}
Micha Mueller's avatar
Micha Mueller committed
275
276
277
278
279
280
281
282
283

void PerfSensorGroup::printConfig(LOG_LEVEL ll) {
    if (_sensorGroupLeader) {
      LOG_VAR(ll) << "   This is a Leader Group";
    } else {
      LOG_VAR(ll) << "   This is a Non-Leader Group";
    }
    LOG_VAR(ll) << "   CPU Id: " << _cpuId;
}