PerfSensorGroup.cpp 4.68 KB
Newer Older
1
/*
2
 * PerfSensorGroup.cpp
3
4
5
6
7
 *
 *  Created on: 04.08.2018
 *      Author: Micha Mueller
 */

8
9
#include "PerfSensorGroup.h"

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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[];
};

27
28
PerfSensorGroup::PerfSensorGroup(const std::string& name) :
	SensorGroupTemplate(name),
29
	_sensorGroupLeader(false),
30
	_cpuId(0),
31
	_group_fd(-1),
32
33
	_bufSize(0) {
	_buf = nullptr;
34
35
}

36
PerfSensorGroup::~PerfSensorGroup() {
37
38
39
40
41
	if (_buf) {
		delete[] _buf;
	}
}

42
void PerfSensorGroup::init(boost::asio::io_service& io) {
43
	SensorGroupTemplate::init(io);
44

45
46
	//allocate buffer to read in later
	//requires 16 byte per sensor plus an additional 8 byte for a 64bit value
47
48
49
50
51
52
53
54
55
	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;
	}
56
57
58
59
60

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

63
void PerfSensorGroup::start() {
64
65
	if (_keepRunning) {
		//we have been started already
66
		LOG(info) << "Sensorgroup " << _groupName << " already running.";
67
68
69
70
71
72
73
		return;
	}

	uint64_t id;

	//open perf-counters
	struct perf_event_attr pe;
74
	PerfSensorBase* pc = _sensors[0];
75
76
77

	memset(&pe, 0, sizeof(struct perf_event_attr));
	pe.size = sizeof(struct perf_event_attr);
78
79
	pe.type = pc->getType();
	pe.config = pc->getConfig();
80
81
82
83
84
85
	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
86
87
	_group_fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, -1, 0);
	ioctl(_group_fd, PERF_EVENT_IOC_ID, &id);
88
89
90
	//store ids to make them distinguishable when reading
	_ids.push_back(id);

91
	if (_group_fd == -1) {
92
		LOG(error) << "Failed to open performance-counter group \"" << _groupName << "\"";
93
94
		return;
	}
95
	LOG(debug) << "  " << _groupName << "::" << pc->getName() << " opened with ID " << std::to_string(id);
96
97
98

	pe.disabled = 0;
	//open all other counters attached to group leader
99
100
	for(unsigned i = 1; i < _sensors.size(); i++) {
		pc = _sensors[i];
101
		int fd;
102
103
		pe.type = pc->getType();
		pe.config = pc->getConfig();
104

105
		fd = syscall(__NR_perf_event_open, &pe, -1, _cpuId, _group_fd, 0);
106
107
108
		//store id, so that we can match counters with values later (see read())
		ioctl(fd, PERF_EVENT_IOC_ID, &id);
		_ids.push_back(id);
109
		_fds.push_back(fd);
110
		LOG(debug) << "  " << _groupName << "::" << pc->getName() << " opened with ID " << std::to_string(id);
111
112
	}

113
114
	ioctl(_group_fd, PERF_EVENT_IOC_RESET, 0);
	ioctl(_group_fd, PERF_EVENT_IOC_ENABLE, 0);
115
116

	_keepRunning = 1;
117
118
119
120
	if (_sensorGroupLeader) {
		_pendingTasks++;
		_timer->async_wait(std::bind(&PerfSensorGroup::readAsync, this));
	}
121
	LOG(info) << "Sensorgroup " << _groupName << " started.";
122
123
}

124
void PerfSensorGroup::stop() {
125
	_keepRunning = 0;
126
127
128
129
	if(_group_fd != -1) {
		for(auto& fd : _fds) {
			close(fd);
		}
130
		_ids.clear();
131
132
133
		_fds.clear();
		close(_group_fd);
		_group_fd = -1;
134
	}
135
	LOG(info) << "Sensorgroup " << _groupName << " stopped.";
136
137
}

138
void PerfSensorGroup::read() {
139
140
141
142
143
144
	reading_t reading;
	reading.timestamp = getTimestamp();

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

145
	if (::read(_group_fd, _buf, _bufSize) < 0) {
146
		LOG(error) << "Sensorgroup" << _groupName << " could not read value";
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
		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 << "\"";
#endif
				_sensors[j]->storeReading(reading, _cacheIndex);
				break;
			}
		}
	}
	_cacheIndex = (_cacheIndex + 1) % _cacheSize;
}

176
void PerfSensorGroup::readAsync() {
177
	read();
178
179
180
181
182
183
	if(_sensorGroupLeader) {
		for(auto g : _fellowSensorGroups) {
			g->read();
		}
	}

Micha Mueller's avatar
Micha Mueller committed
184
	if (_timer && _keepRunning) {
185
		_timer->expires_at(timestamp2ptime(nextReadingTime()));
186
		_pendingTasks++;
187
		_timer->async_wait(std::bind(&PerfSensorGroup::readAsync, this));
188
189
	}
	_pendingTasks--;
190
}