PerfeventConfigurator.cpp 13 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
11
#include <unistd.h>
#include <sys/sysinfo.h>
12
13
14
15
#include <linux/perf_event.h>

using namespace std;

16
PerfeventConfigurator::PerfeventConfigurator() {
17
18
19
	_groupName = "group";
	_baseName = "counter";

20
	//set up enum-maps to map string from cfgFile to an enum value defined in linux/perf_event.h
21
22
23
24
25
26
	_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;
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
	//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
53
}
54

55
PerfeventConfigurator::~PerfeventConfigurator() {}
56

57
58
59
60
61
62
void PerfeventConfigurator::sensorBase(PerfSensorBase& s, CFG_VAL config) {
	/*
	 * Custom code, as perf-event is an extra special plugin
	 */
	BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
		if (boost::iequals(val.first, "type")) {
63
64
65
66
67
68
69
70
71
72
73
74
75
76
			try {
				uint64_t type = stoull(val.second.data(), 0, 0);
				LOG(debug) << "  Type:      0x" << hex << type << dec;
				s.setType(type);
			} catch (const std::invalid_argument& e) {
				enumMap_t::iterator it = _enumType.find(val.second.data());
				if(it != _enumType.end()) {
					s.setType(it->second);
					LOG(debug) << "  Type:      " << val.second.data() << " (= " << s.getType() << ")";
				} else {
					LOG(warning) << "  Type \"" << val.second.data() << "\" not known and could not be parsed as integer type.";
					}
			} catch (const std::exception& e) {
				LOG(warning) << "  Error parsing event type \"" << val.second.data() << "\": " << e.what();
77
78
79
80
			}
		} else if (boost::iequals(val.first, "config")) {
			if (s.getType() == PERF_TYPE_BREAKPOINT) {
				//leave config zero
81
			} else if (s.getType() == PERF_TYPE_RAW || s.getType() > PERF_TYPE_MAX) {
82
				//read in custom hex-value
83
				unsigned long config = stoull(val.second.data(), 0, 0);
84
				s.setConfig(config);
85
				LOG(debug) << "  Config:    Raw value: 0x" << hex << s.getConfig() << dec;
86
87
88
89
			} else {
				enumMap_t::iterator it = _enumConfig.find(val.second.data());
				if(it != _enumConfig.end()) {
					s.setConfig(it->second);
90
					LOG(debug) << "  Config:    " << val.second.data() << " (= " << s.getConfig() << ")";
91
92
				} else {
					LOG(warning) << "  Config \"" << val.second.data() << "\" not known.";
93
				}
94
			}
95
96
97
		} else if (boost::iequals(val.first, "delta")) {
			//it is explicitly stated to be off --> set it to false
			s.setDelta( !(val.second.data() == "off") );
98
99
		}
	}
100
}
101

102
103
void PerfeventConfigurator::sensorGroup(PerfSensorGroup& s, CFG_VAL config) {
	ADD {
lu43jih's avatar
lu43jih committed
104
105
106
107
		if (boost::iequals(val.first, "maxCorrection")) {
			double maxCorrection = std::stod(val.second.data());
			s.setMaxCorrection(maxCorrection);
		}
108
109
	}
}
110

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
bool PerfeventConfigurator::readConfig(std::string cfgPath) {
	/*
	 * Custom code, as perf-event is an extra special plugin
	 */
	_cfgPath = cfgPath;

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

	//read global variables (if present overwrite those from global.conf)
	readGlobal(cfg);

	//read groups and templates for groups
	BOOST_FOREACH(boost::property_tree::iptree::value_type &val, cfg) {
		if (boost::iequals(val.first, "template_" + _groupName)) {
			LOG(debug) << "Template " << _groupName << " \"" << val.second.data() << "\"";
			if (!val.second.empty()) {
				PerfSensorGroup* group = new PerfSensorGroup(val.second.data());
				if (readSensorGroup(*group, val.second)) {
					//check if cpus-list is given for this template group
					boost::optional<boost::property_tree::iptree&> cpus = val.second.get_child_optional("cpus");
					if(cpus) {
						LOG(debug) << "Reading CPUs for \"" << val.second.data() << "\"";
						std::set<int> cpuVec = parseCpuString(cpus.get().data());
						_templateCpus.insert(templateCpuMap_t::value_type(val.second.data(), cpuVec));
136
					}
137

138
139
140
					auto ret = _templateSensorGroups.insert(std::pair<std::string, PerfSensorGroup*>(val.second.data(), group));
					if(!ret.second) {
						LOG(warning) << "Template " << _groupName << " " << val.second.data() << " already exists! Omitting...";
141
						delete group;
142
					}
143
				} else {
144
					LOG(warning) << "Template " << _groupName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
145
					delete group;
146
				}
147
			}
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
        //template single sensor
        } else if (boost::iequals(val.first, "template_single_" + _baseName)) {
            LOG(debug) << "Template single " << _baseName << " \"" << val.second.data() << "\"";
            if (!val.second.empty()) {
                PerfSensorGroup* group = new PerfSensorGroup(val.second.data());
                if (readSensorGroup(*group, val.second)) {
                    //check if cpus-list is given for this template single counter
                    boost::optional<boost::property_tree::iptree&> cpus = val.second.get_child_optional("cpus");
                    if(cpus) {
                        LOG(debug) << "Reading CPUs for \"" << val.second.data() << "\"";
                        std::set<int> cpuVec = parseCpuString(cpus.get().data());
                        _templateCpus.insert(templateCpuMap_t::value_type(val.second.data(), cpuVec));
                    }

                    //group which consists of only one sensor
163
                    std::shared_ptr<PerfSensorBase> sensor = std::make_shared<PerfSensorBase>(val.second.data());
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
                    if (readSensorBase(*sensor, val.second)) {
                        group->pushBackSensor(sensor);

                        auto ret = _templateSensorGroups.insert(std::pair<std::string, PerfSensorGroup*>(val.second.data(), group));
                        if(!ret.second) {
                            LOG(warning) << "Template single " << _baseName << " " << val.second.data() << " already exists! Omitting...";
                            delete group;
                        }
                    } else {
                        LOG(warning) << "Template single " << _baseName << " " << val.second.data() << " could not be read! Omitting";
                        delete group;
                    }
                } else {
                    LOG(warning) << "Template single " << _baseName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
                    delete group;
                }
            }
181
182
183
184
185
		} else if (boost::iequals(val.first, _groupName)) {
			LOG(debug) << _groupName << " \"" << val.second.data() << "\"";
			if (!val.second.empty()) {
				PerfSensorGroup group(val.second.data());
				if (readSensorGroup(group, val.second)) {
186
				    customizeAndStore(group, val.second);
187
188
				} else {
					LOG(warning) << _groupName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
189
190
				}
			}
191
192
193
194
195
196
197
        //single sensor
        } else if (boost::iequals(val.first, "single_" + _baseName)) {
            LOG(debug) << "Single " << _baseName << " \"" << val.second.data() << "\"";
            if (!val.second.empty()) {
                PerfSensorGroup group(val.second.data());
                if (readSensorGroup(group, val.second)) {
                    //group which consists of only one sensor
198
                    std::shared_ptr<PerfSensorBase> sensor;
199
200
                    //perhaps one sensor is already present because it was copied from the template group
                    if (group.getSensors().size() != 0) {
201
202
203
204
205
206
207
208
209
210
211
212
                        sensor = std::dynamic_pointer_cast<PerfSensorBase>(group.getSensors()[0]);
                        //check if cast was successful (sensor != nullptr)
                        if (sensor) {
                          sensor->setName(val.second.data());
                          if (readSensorBase(*sensor, val.second)) {
                              customizeAndStore(group, val.second);
                          } else {
                              LOG(warning) << "Single " << _baseName << " " << val.second.data() << " could not be read! Omitting";
                          }
                       } else {
                         LOG(warning) << "Single " << _baseName << " " << val.second.data() << " had a type mismatch when casting! Omitting";
                       }
213
                    } else {
214
                        sensor = std::make_shared<PerfSensorBase>(val.second.data());
215
216
217
218
219
220
221
222
223
224
225
                        if (readSensorBase(*sensor, val.second)) {
                            group.pushBackSensor(sensor);
                            customizeAndStore(group, val.second);
                        } else {
                            LOG(warning) << "Single " << _baseName << " " << val.second.data() << " could not be read! Omitting";
                        }
                    }
                } else {
                    LOG(warning) << "Single " << _baseName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
                }
            }
Alessio Netti's avatar
Alessio Netti committed
226
		} else if( !boost::iequals(val.first, "global") ) {
227
228
229
		    LOG(error) << "\"" << val.first << "\": unknown construct!";
            return false;
        }
230
	}
231
	//read of config finished. Now we build the mqtt-topic for every sensor
Alessio Netti's avatar
Alessio Netti committed
232
233
	if(!constructSensorNames())
		return false;
234
235
	for(const auto& g : _sensorGroups) {
		for(const auto& s : g->getSensors()) {
236
			s->setMqtt(_mqttPrefix + g->getMqttPart() + s->getMqtt());
237
		}
238
	}
239
240
	//we do not need them anymore
	_templateCpus.clear();
241
	return true;
242
}
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269

void PerfeventConfigurator::customizeAndStore(PerfSensorGroup& group, CFG_VAL cfg) {
    //initialize set with cpuIDs
    //default cpuSet: contains all cpuIDs
    std::set<int> cpuSet;
    for (int i = 0; i < get_nprocs(); i++) {
        cpuSet.insert(i);
    }
    //check if (differing) cpus-list is given; if so, overwrite default cpuVec
    boost::optional<boost::property_tree::iptree&> cpus = cfg.get_child_optional("cpus");
    if (cpus) { //cpu list given
        cpuSet = parseCpuString(cpus.get().data());
    } else { //cpu list not given, but perhaps template counter has one
        boost::optional<boost::property_tree::iptree&> def = cfg.get_child_optional("default");
        if (def) {
            templateCpuMap_t::iterator itC = _templateCpus.find(def.get().data());
            if(itC != _templateCpus.end()) {
                cpuSet = itC->second;
            }
        }
    }

    if (group.getMqttPart().size() == 0) {
        LOG(warning) << _groupName << " \"" << cfg.data() << "\" has no mqttPart entry set. This is required as a place holder for the CPU id!";
    }

    //customize perfCounterGroup for every CPU
Michael Ott's avatar
Michael Ott committed
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
    if(!cpuSet.empty()) {
	//first create groupLeader
	std::set<int>::iterator it = cpuSet.begin();
	PerfSGPtr leaderSG = std::make_shared<PerfSensorGroup>(group);
	leaderSG->setSensorGroupLeader(true);
	
	leaderSG->setGroupName(SensorBase::formatName(leaderSG->getGroupName(), *it));
	leaderSG->setCpuId(*it);
	leaderSG->setMqttPart(formatMqttCPU(group.getMqttPart(), *it) + "/");
	
	for(const auto& s : leaderSG->getSensors()) s->setName(s->getName(), *it);
	
	storeSensorGroup(leaderSG);
	it++;
	
	//create fellow groups
	for (; it != cpuSet.end(); ++it) {
	    PerfSGPtr perfSG = std::make_shared<PerfSensorGroup>(group);
	    
	    perfSG->setGroupName(SensorBase::formatName(perfSG->getGroupName(), *it));
	    perfSG->setCpuId(*it);
	    perfSG->setMqttPart(formatMqttCPU(group.getMqttPart(), *it) + "/");
	    
	    for(const auto& s : perfSG->getSensors()) s->setName(s->getName(), *it);
	    
	    storeSensorGroup(perfSG);
	    leaderSG->pushBackGroup(perfSG);
	}
298
299
    }
}