PerfSensorGroup.cpp 9.4 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
33
	_htAggregation(false),
	_htAggregator(true),
34
	_cpuId(0),
35
	_group_fd(-1),
36
	_buf(nullptr),
lu43jih's avatar
lu43jih committed
37
38
	_bufSize(0),
	_latest_time_enabled (0),
39
	_latest_time_running(0),
lu43jih's avatar
lu43jih committed
40
	_lastValid(true),
41
	_latestValueValid(false),
42
43
44
45
46
	_maxCorrection(20) {}

PerfSensorGroup::PerfSensorGroup(const PerfSensorGroup& other) :
    SensorGroupTemplate(other),
    _sensorGroupLeader(false),
47
48
    _htAggregation(other._htAggregation),
    _htAggregator(true),
49
50
51
52
53
54
55
    _cpuId(other._cpuId),
    _group_fd(-1),
    _buf(nullptr),
    _bufSize(0),
    _latest_time_enabled(0),
    _latest_time_running(0),
    _lastValid(true),
56
    _latestValueValid(false),
57
    _maxCorrection(other._maxCorrection) {
58
59
}

60
PerfSensorGroup::~PerfSensorGroup() {
61
62
63
64
65
	if (_buf) {
		delete[] _buf;
	}
}

66
67
68
PerfSensorGroup& PerfSensorGroup::operator=(const PerfSensorGroup& other) {
  SensorGroupTemplate::operator=(other);
  _sensorGroupLeader = false;
69
70
  _htAggregation = other._htAggregation;
  _htAggregator = true;
71
72
73
74
75
76
77
  _cpuId = other._cpuId;
  _group_fd = -1;
  _buf = nullptr;
  _bufSize = 0;
  _latest_time_enabled = 0;
  _latest_time_running = 0;
  _lastValid = true;
78
  _latestValueValid = false;
79
80
81
82
83
  _maxCorrection = other._maxCorrection;

  return *this;
}

84
void PerfSensorGroup::init(boost::asio::io_service& io) {
85
	SensorGroupTemplate::init(io);
86

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
	/* 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;
102
103
104
105
106
107
108
109
	if (!_buf) {
		_buf = new char[bufSize];
		_bufSize = bufSize;
	} else if (bufSize > _bufSize) {
		delete _buf;
		_buf = new char[bufSize];
		_bufSize = bufSize;
	}
110
111
112
113
114

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

117
void PerfSensorGroup::start() {
118
119
	if (_keepRunning) {
		//we have been started already
120
		LOG(info) << "Sensorgroup " << _groupName << " already running.";
121
122
123
124
125
126
127
		return;
	}

	uint64_t id;

	//open perf-counters
	struct perf_event_attr pe;
128
	PerfSBPtr pc = _sensors[0];
129
130
131

	memset(&pe, 0, sizeof(struct perf_event_attr));
	pe.size = sizeof(struct perf_event_attr);
132
133
	pe.type = pc->getType();
	pe.config = pc->getConfig();
134
135
	pe.disabled = 1;
	pe.exclude_kernel = 0;
136
	pe.exclude_hv = 0;
137
	pe.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID  | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;
138
139

	//perf_event_open() group leader
140
	_group_fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, -1, 0);
141
142
143
144
	if (_group_fd == -1) {
		LOG(error) << "Failed to open performance-counter group \"" << _groupName << "\":" << strerror(errno);
		return;
	}
145
	ioctl(_group_fd, PERF_EVENT_IOC_ID, &id);
146
147
148
	//store ids to make them distinguishable when reading
	_ids.push_back(id);

149
	LOG(debug) << "  " << _groupName << "::" << pc->getName() << " opened with ID " << std::to_string(id);
150
151
152

	pe.disabled = 0;
	//open all other counters attached to group leader
153
154
	for(unsigned i = 1; i < _sensors.size(); i++) {
		pc = _sensors[i];
155
		int fd;
156
157
		pe.type = pc->getType();
		pe.config = pc->getConfig();
158

159
		fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, _group_fd, 0);
160
		//store id, so that we can match counters with values later (see read())
161
162
163
164
165
166
167
		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 {
168
				LOG(debug) << "  " << _groupName << "::" << pc->getName() << " error obtaining ID: " << strerror(rc);
169
170
			}
		} else {
171
			LOG(debug) << "  " << _groupName << "::" << pc->getName() << " error opening perf file descriptor: " << strerror(errno);
172
		}
173
174
	}

175
176
	ioctl(_group_fd, PERF_EVENT_IOC_RESET, 0);
	ioctl(_group_fd, PERF_EVENT_IOC_ENABLE, 0);
177
178

	_keepRunning = 1;
179
180
181
182
	if (_sensorGroupLeader) {
		_pendingTasks++;
		_timer->async_wait(std::bind(&PerfSensorGroup::readAsync, this));
	}
183
	LOG(info) << "Sensorgroup " << _groupName << " started.";
184
185
}

186
void PerfSensorGroup::stop() {
187
	_keepRunning = 0;
188
189
190
191
	if(_group_fd != -1) {
		for(auto& fd : _fds) {
			close(fd);
		}
192
		_ids.clear();
193
194
195
		_fds.clear();
		close(_group_fd);
		_group_fd = -1;
196
	}
197
	LOG(info) << "Sensorgroup " << _groupName << " stopped.";
198
199
}

200
void PerfSensorGroup::read() {
201
202
    ureading_t reading;
    reading.timestamp = getTimestamp();
203
204
205
206

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

207
	if (::read(_group_fd, _buf, _bufSize) < 0) {
208
		LOG(error) << "Sensorgroup" << _groupName << " could not read value";
209
210
211
		return;
	}

lu43jih's avatar
lu43jih committed
212
	double correction = 1;
213
	bool validMeasurement=true;
lu43jih's avatar
lu43jih committed
214
215
216
	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 );

217
218
219
220
	//store latest times
	_latest_time_enabled = rf->time_enabled;
	_latest_time_running = rf->time_running;

lu43jih's avatar
lu43jih committed
221
222
	if(time_running){
    	correction = static_cast<double>(time_enabled)/time_running;
223
	} else {
lu43jih's avatar
lu43jih committed
224
		LOG(info) << "PerfSensorGroup: Group: " << _groupName << " could not be measured. Time running==0";
225
226
		validMeasurement = false;
    }
lu43jih's avatar
lu43jih committed
227
228
    if(correction > _maxCorrection  || correction < 1 ){
        LOG(info) << "PerfCounterGroup: Group: " << _groupName << " could not be measured. Correction factor ==" << correction;
229
230
231
232
        validMeasurement = false;
    }

    if(!validMeasurement){
233
    	_lastValid=false;
234
    	_latestValueValid=false;
235
236
    	return;
    }
237
238
	//iterate over all values returned by ::read()
	for (unsigned i = 0; i < rf->nr; i++) {
239
		reading.value = rf->values[i].value;
240

241
242
243
		//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]) {
244
#ifdef DEBUG
245
				LOG(debug) << _groupName << "::" << _sensors[j]->getName() << " raw reading: \"" << reading.value << "\"";
246
#endif
247
				if(_lastValid){
248
249
250
251
252
253
254
				    if (_htAggregation) {
				        _sensors[j]->storeReadingLocal(reading, correction, PerfSensorBase::MAXCOUNTERVALUE);
				    } else {
                        //storeReading takes care of delta computation and applies correction value on the result
                        _sensors[j]->storeReading(reading, correction, PerfSensorBase::MAXCOUNTERVALUE);
				    }
				    _latestValueValid = true;
255
256
				} else {
					//Before we can compute correct values again after an invalid reading we have to update the lastRawValue first
257
					_sensors[j]->setLastURaw(reading.value);
258
				}
259
260
261
262
				break;
			}
		}
	}
263
264
265
	if(validMeasurement){ //set valid for the next time to read
		_lastValid = true;
	}
266
267
}

268
void PerfSensorGroup::readAsync() {
269
	read();
270
	if(_sensorGroupLeader) {
271
		for(const auto& g : _fellowSensorGroups) {
272
273
274
			g->read();
		}
	}
275
276
277
278
279
280
	if(_htAggregation) {
	    accumulateHT();
	    for(const auto& g : _fellowSensorGroups) {
            g->accumulateHT();
        }
	}
281

Micha Mueller's avatar
Micha Mueller committed
282
	if (_timer && _keepRunning) {
283
		_timer->expires_at(timestamp2ptime(nextReadingTime()));
284
		_pendingTasks++;
285
		_timer->async_wait(std::bind(&PerfSensorGroup::readAsync, this));
286
287
	}
	_pendingTasks--;
288
}
289
290
291
292
293
294
295

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
296
297
298

void PerfSensorGroup::printConfig(LOG_LEVEL ll) {
    if (_sensorGroupLeader) {
299
      LOG_VAR(ll) << "            This is a Leader Group";
Micha Mueller's avatar
Micha Mueller committed
300
    } else {
301
      LOG_VAR(ll) << "            This is a Non-Leader Group";
Micha Mueller's avatar
Micha Mueller committed
302
    }
303
304
305
306
307
308
309
    if (_htAggregation) {
        if (_htAggregator) {
            LOG_VAR(ll) << "            This is an Aggregator Group";
        } else {
            LOG_VAR(ll) << "            This is not an Aggregator Group";
        }
    }
310
    LOG_VAR(ll) << "            CPU Id: " << _cpuId;
Micha Mueller's avatar
Micha Mueller committed
311
}
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338

void PerfSensorGroup::accumulateHT() {
    if (!_htAggregator) {
        //this group gets aggregated
        return;
    }
    for(size_t i = 0; i < _sensors.size(); i++) {
        reading_t reading;
        if (_latestValueValid) {
            reading = _sensors[i]->getLatestValue();
        } else {
            reading.timestamp = 0;
            reading.value = 0;
        }
        for(const auto& g : _htAggregationGroups) {
            if(g->getLatestValueValid()) {
                reading.value += g->_sensors[i]->getLatestValue().value;
                if (!_latestValueValid) {
                    reading.timestamp = g->_sensors[i]->getLatestValue().timestamp;
                }
            }
        }
        if (reading.timestamp != 0) {
            _sensors[i]->storeReadingGlobal(reading);
        }
    }
}