PerfeventConfigurator.cpp 10.6 KB
Newer Older
1
/*
2
 * PerfeventConfigurator.cpp
3
4
5
6
7
 *
 *  Created on: 13.12.2017
 *      Author: Micha Mueller
 */

8
#include "PerfeventConfigurator.h"
9

10
#include <iostream>
11
#include <sstream>
12
#include <string>
13
14
15
#include <unistd.h>
#include <iomanip>
#include <sys/sysinfo.h>
16
17
18
19
20
21
22
23
#include <linux/perf_event.h>

#include <boost/foreach.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/algorithm/string.hpp>

using namespace std;

24
PerfeventConfigurator::PerfeventConfigurator() {
25
	//set up enum-maps to map string from cfgFile to an enum value defined in linux/perf_event.h
26
27
28
29
30
31
	_enumType["PERF_TYPE_HARDWARE"] 	= PERF_TYPE_HARDWARE;
	_enumType["PERF_TYPE_SOFTWARE"] 	= PERF_TYPE_SOFTWARE;
	_enumType["PERF_TYPE_TRACEPOINT"] 	= PERF_TYPE_TRACEPOINT;
	_enumType["PERF_TYPE_HW_CACHE"] 	= PERF_TYPE_HW_CACHE;
	_enumType["PERF_TYPE_RAW"] 			= PERF_TYPE_RAW;
	_enumType["PERF_TYPE_BREAKPOINT"] 	= PERF_TYPE_BREAKPOINT;
32

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
	//if type==PERF_TYPE_HARDWARE
	_enumConfig["PERF_COUNT_HW_CPU_CYCLES"] 		 = PERF_COUNT_HW_CPU_CYCLES;
	_enumConfig["PERF_COUNT_HW_INSTRUCTIONS"] 		 = PERF_COUNT_HW_INSTRUCTIONS;
	_enumConfig["PERF_COUNT_HW_CACHE_REFERENCES"] 	 = PERF_COUNT_HW_CACHE_REFERENCES;
	_enumConfig["PERF_COUNT_HW_CACHE_MISSES"] 		 = PERF_COUNT_HW_CACHE_MISSES;
	_enumConfig["PERF_COUNT_HW_BRANCH_INSTRUCTIONS"] = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
	_enumConfig["PERF_COUNT_HW_BRANCH_MISSES"] 		 = PERF_COUNT_HW_BRANCH_MISSES;
	_enumConfig["PERF_COUNT_HW_BUS_CYCLES"] 			 = PERF_COUNT_HW_BUS_CYCLES;
	_enumConfig["PERF_COUNT_HW_STALLED_CYCLES_FRONTEND"] = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND;
	_enumConfig["PERF_COUNT_HW_STALLED_CYCLES_BACKEND"]	 = PERF_COUNT_HW_STALLED_CYCLES_BACKEND;
	_enumConfig["PERF_COUNT_HW_REF_CPU_CYCLES"] 		 = PERF_COUNT_HW_REF_CPU_CYCLES;

	//if type==PERF_TYPE_SOFTWARE
	_enumConfig["PERF_COUNT_SW_CPU_CLOCK"] 		  = PERF_COUNT_SW_CPU_CLOCK;
	_enumConfig["PERF_COUNT_SW_TASK_CLOCK"] 	  = PERF_COUNT_SW_TASK_CLOCK;
	_enumConfig["PERF_COUNT_SW_PAGE_FAULTS"] 	  = PERF_COUNT_SW_PAGE_FAULTS;
	_enumConfig["PERF_COUNT_SW_CONTEXT_SWITCHES"] = PERF_COUNT_SW_CONTEXT_SWITCHES;
	_enumConfig["PERF_COUNT_SW_CPU_MIGRATIONS"]   = PERF_COUNT_SW_CPU_MIGRATIONS;
	_enumConfig["PERF_COUNT_SW_PAGE_FAULTS_MIN"]  = PERF_COUNT_SW_PAGE_FAULTS_MIN;
	_enumConfig["PERF_COUNT_SW_PAGE_FAULTS_MAJ"]  = PERF_COUNT_SW_PAGE_FAULTS_MAJ;
	_enumConfig["PERF_COUNT_SW_ALIGNMENT_FAULTS"] = PERF_COUNT_SW_ALIGNMENT_FAULTS;
	_enumConfig["PERF_COUNT_SW_EMULATION_FAULTS"] = PERF_COUNT_SW_EMULATION_FAULTS;
	_enumConfig["PERF_COUNT_SW_DUMMY"] 			  = PERF_COUNT_SW_DUMMY;

	//TODO set up map for rest of config enum
58
}
59

60
PerfeventConfigurator::~PerfeventConfigurator() {}
61

62
bool PerfeventConfigurator::readConfig(std::string cfgPath) {
63
	Configurator::readConfig(cfgPath);
64
65
66
67

	boost::property_tree::iptree cfg;
	boost::property_tree::read_info(cfgPath, cfg);

68
69
70
71
72
73
74
75
76
	//read global variables (if present overwrite those from global.conf)
	boost::optional<boost::property_tree::iptree&> globalVals = cfg.get_child_optional("global");
	if (globalVals) {
		BOOST_FOREACH(boost::property_tree::iptree::value_type &global, cfg.get_child("global")) {
			if(boost::iequals(global.first, "mqttprefix")) {
				_mqttPrefix = global.second.data();
				if (_mqttPrefix[_mqttPrefix.length()-1] != '/') {
					_mqttPrefix.append("/");
				}
77
				LOG(debug) << "  Using own MQTT-Prefix " << _mqttPrefix;
78
79
80
81
			} else if (boost::iequals(global.first, "cacheInterval")) {
				_cacheInterval = stoul(global.second.data());
				LOG(debug) << "  Using own caching interval " << _cacheInterval << " [s]";
				_cacheInterval *= 1000;
82
			} else {
83
				LOG(warning) << "  Value \"" << global.first << "\" not recognized. Omitting...";
84
85
86
87
			}
		}
	}

88
	//read template counters
89
90
	BOOST_FOREACH(boost::property_tree::iptree::value_type &counter, cfg.get_child("CounterTemplate")) {
		if (boost::iequals(counter.first, "counter")) {
91
			LOG(debug) << "Template Counter \"" << counter.second.data() << "\"";
92
93
			if (!counter.second.empty()) {
				PerfCounter perfCounter(counter.second.data());
94
95
				if(readCounter(perfCounter, counter.second)) {
					_templateCounters.insert(counterMap_t::value_type(perfCounter.getName(), perfCounter));
96
				} else {
97
					LOG(warning) << "Template Counter \"" << counter.second.data() << "\" has bad values! Ignoring...";
98
				}
99
100
101
102
103
104
105

				//check if cpus-list is given for this template counter
				boost::optional<boost::property_tree::iptree&> cpus = counter.second.get_child_optional("cpus");
				if(cpus) {
					std::set<int> cpuVec = parseCpuString(cpus.get().data());
					_templateCpus.insert(templateCpuMap_t::value_type(perfCounter.getName(), cpuVec));
				}
106
107
108
109
110
111
112
			}
		}
	}

	//read one counter at a time
	BOOST_FOREACH(boost::property_tree::iptree::value_type &counter, cfg.get_child("counters")) {
		if (boost::iequals(counter.first, "counter")) {
113
			LOG(debug) << "Counter \"" << counter.second.data() << "\"";
114
115
116
117
118
119
			if (!counter.second.empty()) {
				PerfCounter perfCounter(counter.second.data());

				//first check if default counter is given
				boost::optional<boost::property_tree::iptree&> defaultC = counter.second.get_child_optional("default");
				if(defaultC) {
120
					LOG(debug) << "  Using \"" << defaultC.get().data() << "\" as default.";
121
					counterMap_t::iterator it = _templateCounters.find(defaultC.get().data());
122
123
124
125
					if(it != _templateCounters.end()) {
						perfCounter = it->second;
						perfCounter.setName(counter.second.data());
					} else {
126
						LOG(warning) << "  Template counter \"" << defaultC.get().data() << "\" not found! Using standard values.";
127
128
					}
				}
129

130
131
132
				//initialize set with cpuIDs
				//default cpuSet: contains all cpuIDs
				std::set<int> cpuSet;
133
				for (int i = 0; i < get_nprocs(); i++) {
134
					cpuSet.insert(i);
135
136
137
138
				}
				//check if (differing) cpus-list is given; if so, overwrite default cpuVec
				boost::optional<boost::property_tree::iptree&> cpus = counter.second.get_child_optional("cpus");
				if (cpus) { //cpu list given
139
					cpuSet = parseCpuString(cpus.get().data());
140
141
142
				} else if (defaultC) { //cpu list not given, but perhaps template counter has one
					templateCpuMap_t::iterator itC = _templateCpus.find(defaultC.get().data());
					if(itC != _templateCpus.end()) {
143
						cpuSet = itC->second;
144
145
146
					}
				}

147
				//read remaining values
148
				if(readCounter(perfCounter, counter.second)) {
149
					//create distinct perfCounter and mqttSuffix per CPU
150
151
152
153

					string startMqtt = perfCounter.getMqtt();

					//customize perfCounter for every CPU
154
					for (auto i : cpuSet) {
155
156
						PerfCounter* perfCC = new PerfCounter(counter.second.data());
						*perfCC = perfCounter;
157
158
						string incMqtt = increaseMqtt(startMqtt, i);

159
						perfCC->setName(perfCC->getName() + std::to_string(i));
160
						perfCC->setCpuId(i);
161
						perfCC->setMqtt(_mqttPrefix + incMqtt);
162
						LOG(debug) << "  CPU " << perfCC->getCpuId() << " using MQTT-Topic " << perfCC->getMqtt();
163
						_sensors.push_back(perfCC);
164
					}
165
				} else {
166
					LOG(warning) << "  Counter \"" << counter.second.data() << "\" has bad values! Ignoring...";
167
				}
168
169
170
			}
		}
	}
171
	return true;
172
173
}

174
bool PerfeventConfigurator::readCounter(PerfCounter& counter, boost::property_tree::iptree& config) {
175
176
	BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
		if (boost::iequals(val.first, "interval")) {
177
			counter.setInterval(stoull(val.second.data()));
178
		} else if (boost::iequals(val.first, "mqttsuffix")) {
179
			counter.setMqtt(val.second.data());
180
181
182
183
184
185
		} else if (boost::iequals(val.first, "minValues")) {
			counter.setMinValues(stoull(val.second.data()));
		} else if (boost::iequals(val.first, "type")) {
			enumMap_t::iterator it = _enumType.find(val.second.data());
			if(it != _enumType.end()) {
				counter.setType(it->second);
186
				LOG(debug) << "  Type:      " << val.second.data() << " (= " << counter.getType() << ")";
187
			} else {
188
				LOG(warning) << "  Type \"" << val.second.data() << "\" not known.";
189
				return false;
190
191
			}
		} else if (boost::iequals(val.first, "config")) {
192
193
194
195
196
197
			if (counter.getType() == PERF_TYPE_BREAKPOINT) {
				//leave config zero
			} else if (counter.getType() == PERF_TYPE_RAW) {
				//read in custom hex-value
				unsigned long config = stoul(val.second.data(), 0, 16);
				counter.setConfig(config);
198
				LOG(debug) << "  Config:    Raw value: " << counter.getConfig();
199
			} else {
200
201
202
				enumMap_t::iterator it = _enumConfig.find(val.second.data());
				if(it != _enumConfig.end()) {
					counter.setConfig(it->second);
203
					LOG(debug) << "  Config:    " << val.second.data() << " (= " << counter.getConfig() << ")";
204
				} else {
205
					LOG(warning) << "  Config \"" << val.second.data() << "\" not known.";
206
					return false;
207
				}
208
			}
209
210
		} else if (boost::iequals(val.first, "cpus")) {
			//avoid unncecessary "Value not recognized" message. cpus are handled in readConfig()
211
		} else if (boost::iequals(val.first, "default")) {
212
213
			//avoid unnecessary "Value not recognized" message
		} else {
214
			LOG(warning) << "  Value \"" << val.first << "\" not recognized. Omitting...";
215
		}
216
217
	}

218
219
	counter.setCacheInterval(_cacheInterval);

220
221
222
	LOG(debug) << "  StartMQTT: " << counter.getMqtt();
	LOG(debug) << "  Interval : " << counter.getInterval();
	LOG(debug) << "  minValues: " << counter.getMinValues();
223
	return true;
224
}
225

226
const std::string PerfeventConfigurator::increaseMqtt(const std::string& mqtt, int val) {
227
228
229
230
231
232
	unsigned long mqttDigits = stoul(mqtt, 0, 16);
	mqttDigits += val;
	std::stringstream stream;
	stream << std::setfill ('0') << std::setw(mqtt.length()) << std::hex << mqttDigits;
	return stream.str();
}
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262

std::set<int> PerfeventConfigurator::parseCpuString(const std::string& cpuString) {
	std::set<int> cpus;
	int maxCpu = get_nprocs();

	std::vector<std::string> subStrings;

	std::stringstream ssComma(cpuString);
	std::string item;
	while (std::getline(ssComma, item, ',')) {
		subStrings.push_back(item);
	}

	for (auto s : subStrings) {
		if (s.find('-') != std::string::npos) { //range of values (e.g. 1-5) specified
			std::stringstream ssHyphen(s);
			std::string min, max;
			std::getline(ssHyphen, min, '-');
			std::getline(ssHyphen, max);

			try {
				int minVal = stoi(min);
				int maxVal = stoi(max);

				for (int i = minVal; i <= maxVal; i++) {
					if (i >= 0 && i < maxCpu) {
						cpus.insert(i);
					}
				}
			} catch (const std::exception& e) {
263
				LOG(debug) << "Could not parse values \"" << min << "-" << max << "\"";
264
265
266
267
268
269
270
271
			}
		} else { //single value
			try {
				int val = stoi(s);
				if (val >= 0 && val < maxCpu) {
					cpus.insert(val);
				}
			} catch (const std::exception& e) {
272
				LOG(debug) << "Could not parse value \"" << s << "\"";
273
274
275
276
277
			}
		}
	}

	if (cpus.empty()) {
278
		LOG(warning) << "  CPUs could not be parsed!";
279
	} else {
280
281
		std::stringstream sstream;
		sstream << "  CPUS: ";
282
		for (auto i : cpus) {
283
			sstream << i << ", ";
284
		}
285
286
287
288
		std::string msg = sstream.str();
		msg.pop_back();
		msg.pop_back();
		LOG(debug) << msg;
289
290
291
	}
	return cpus;
}