PerfSensorGroup.cpp 4.41 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
27
#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[];
};

28
PerfSensorGroup::PerfSensorGroup(const std::string name) :
29
	SensorGroupTemplate(name) {
30
31
32
33
34

	_bufSize = 0;
	_buf = NULL;
}

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

41
void PerfSensorGroup::init(boost::asio::io_service& io) {
42
	_cacheSize = _cacheInterval / _interval + 1;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
	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;
	}

57
58
	for (auto s : _sensors) {
		s->initSensor(_cacheSize);
59
60
61
	}
}

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

	uint64_t id;

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

	memset(&pe, 0, sizeof(struct perf_event_attr));
	pe.size = sizeof(struct perf_event_attr);
77
78
	pe.type = pc->getType();
	pe.config = pc->getConfig();
79
80
81
82
83
84
85
86
87
88
89
90
	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) {
91
		LOG(error) << "Failed to open performance-counter group \"" << _groupName << "\"";
92
93
		return;
	}
94
	LOG(debug) << "  " << _groupName << "::" << pc->getName() << " opened with ID " << std::to_string(id);
95
96
97

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

		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);
108
		LOG(debug) << "  " << _groupName << "::" << pc->getName() << " opened with ID " << std::to_string(id);
109
110
111
112
113
114
115
	}

	ioctl(_fd, PERF_EVENT_IOC_RESET, 0);
	ioctl(_fd, PERF_EVENT_IOC_ENABLE, 0);

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

120
void PerfSensorGroup::stop() {
121
122
123
124
125
	_keepRunning = 0;
	if(_fd != -1) {
		close(_fd);
		_fd = -1;
	}
126
	LOG(info) << "Sensorgroup " << _groupName << " stopped.";
127
128
}

129
void PerfSensorGroup::read() {
130
131
132
133
134
135
136
	reading_t reading;
	reading.timestamp = getTimestamp();

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

	if (::read(_fd, _buf, _bufSize) < 0) {
137
		LOG(error) << "Sensorgroup" << _groupName << " could not read value";
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
		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;
}

167
void PerfSensorGroup::readAsync() {
168
169
170
171
172
173
	uint64_t now = getTimestamp();
	read();
	if (_timer != NULL && _keepRunning) {
		uint64_t next = now + MS_TO_NS(_interval);
		_timer->expires_at(timestamp2ptime(next));
		_pendingTasks++;
174
		_timer->async_wait(std::bind(&PerfSensorGroup::readAsync, this));
175
176
	}
	_pendingTasks--;
177
}