Commit fadec908 authored by Alessio Netti's avatar Alessio Netti
Browse files

SAR parser changes

- Now the percentages are computed based from the delta of each column
with respect to the last reading
- ProcstatPercParser renamed to SARParser for clarity
- New corresponding sensor group type is "sar"
parent 55367251
......@@ -487,7 +487,7 @@ Explanation of the values specific for the ProcFS plugin:
| Value | Explanation |
|:----- |:----------- |
| type | The type of the file parsed by the sensor group. Can be either "vmstat", "meminfo", "procstat" or "procstat_perc"
| type | The type of the file parsed by the sensor group. Can be either "vmstat", "meminfo", "procstat" or "sar"
| path | Path of the file, if different from the default path in the /proc filesystem
| mqttPart | The mqttPart works similarly as in the Perf-event plugin. For sensors associated to metrics that are core-specific (e.g. some of those in /proc/stat) the mqttPart is replaced with the CPU id. For all other metrics that are system-wide, the mqttPart is used as it is.
| mqttStart | Base MQTT suffix that is automatically incremented to generate topics for sensors associated to metrics in the same file. Note that this parameter is used only if automatic MQTT topic generation is enabled, when no sensors are explicitly defined.
......
......@@ -128,8 +128,8 @@ void ProcfsConfigurator::sensorGroup(ProcfsSensorGroup& sGroup, CFG_VAL config)
parser = new VmstatParser(filePath);
else if(fileType == "procstat")
parser = new ProcstatParser(filePath);
else if(fileType == "procstat_perc")
parser = new ProcstatPercParser(filePath);
else if(fileType == "sar")
parser = new SARParser(filePath);
else if(fileType == "meminfo")
parser = new MeminfoParser(filePath);
else {
......
......@@ -157,7 +157,7 @@ bool MeminfoParser::_readNames(std::map<std::string, ProcfsSBPtr> *sensorMap, st
this->_skipLine.clear();
break;
}
// We check if the token matches one entry in sensorMap, meaning that it must be extracted
// We check if the token matches one entry in sensorMap, meaning that it must be extracted
else if ( sensorMap == NULL || sensorMap->empty() || sensorMap->find(lineToken) != sensorMap->end() ) {
if ( sensorMap == NULL || sensorMap->empty() )
this->_sensors->push_back(std::make_shared<ProcfsSensorBase>(std::string(lineToken), std::string(lineToken)));
......@@ -165,7 +165,7 @@ bool MeminfoParser::_readNames(std::map<std::string, ProcfsSBPtr> *sensorMap, st
this->_sensors->push_back(std::make_shared<ProcfsSensorBase>(*sensorMap->find(lineToken)->second));
this->_skipLine.push_back(false);
}
// If the token does not match any entry in sensorMap, its line is flagged as ignored
// If the token does not match any entry in sensorMap, its line is flagged as ignored
else
this->_skipLine.push_back(true);
......@@ -227,9 +227,9 @@ bool MeminfoParser::_readMetrics() {
this->_readings->at(ctr++).value = this->_valueBuffer.value;
// Storing MemTotal and MemFree values to be used to compute MemUsed
if(lineCtr == this->_memFreeLine)
{ this->_memFreeValue.value = this->_valueBuffer.value; this->_memFreeValue.timestamp = 1; }
{ this->_memFreeValue.value = this->_valueBuffer.value; this->_memFreeValue.timestamp = 1; }
else if(lineCtr == this->_memTotalLine)
{ this->_memTotalValue.value = this->_valueBuffer.value; this->_memTotalValue.timestamp = 1; }
{ this->_memTotalValue.value = this->_valueBuffer.value; this->_memTotalValue.timestamp = 1; }
// If the last metric left to compute is MemUsed, and both MemTotal and MemFree have been acquired,
// we can break out of the loop early
if(this->_memTotalValue.timestamp!=0 && this->_memFreeValue.timestamp!=0 && ctr+1 == this->_numMetrics)
......@@ -274,6 +274,7 @@ bool ProcstatParser::_readNames(std::map<std::string, ProcfsSBPtr> *sensorMap, s
// We detect which columns in the procstat file, corresponding to CPU-specific metrics must be skipped or
// considered depending on the sensors in the sensormap
this->_numColumns = 0;
this->_numCPUs = 0;
for(int i=0; i < this->DEFAULTMETRICS; i++)
if(sensorMap != NULL && !sensorMap->empty() && sensorMap->find(this->DEFAULT_NAMES[i]) == sensorMap->end())
this->_skipColumn.push_back( 0 );
......@@ -314,6 +315,7 @@ bool ProcstatParser::_readNames(std::map<std::string, ProcfsSBPtr> *sensorMap, s
if( currCPU == -1 || cpuSet == NULL || cpuSet->empty() || cpuSet->find(currCPU) != cpuSet->end() ) {
// The number of CPU cores in the system is inferred from the maximum CPU ID found while parsing
if (currCPU >= (int)this->_numCPUs) this->_numCPUs = currCPU + 1;
// Counting the number of CPU-related lines
std::string cpuName(lineToken);
while ((lineToken = strtok_r(this->_stringBuffer, this->LINE_SEP, &savePtr)) != NULL && colCtr < this->DEFAULTMETRICS) {
if( this->_skipColumn[colCtr] == 1 || ( this->_skipColumn[colCtr] == 2 && currCPU == -1 ) ) {
......@@ -417,7 +419,24 @@ bool ProcstatParser::_readMetrics() {
}
// **************************************************************
// Definitions for the ProcstatPercParser class
// Definitions for the SARParser class
/**
* Closes the parser.
*
* For SARParser, this method also releases all internal buffers used to store temporary readings.
*
*/
void SARParser::close() {
ProcfsParser::close();
if(this->_columnBuffer != NULL)
delete[] this->_columnBuffer;
if(this->_columnRawReadings != NULL)
delete[] this->_columnRawReadings;
this->_columnBuffer = NULL;
this->_columnRawReadings = NULL;
}
/**
* Parses the pointed file and updates sensor readings accordingly.
......@@ -426,11 +445,14 @@ bool ProcstatParser::_readMetrics() {
*
* @return true if successful, false otherwise
*/
bool ProcstatPercParser::_readMetrics() {
if ( this->_readings == NULL )
bool SARParser::_readMetrics() {
if ( this->_readings == NULL ) {
this->_readings = new std::vector<reading_t>(this->_numMetrics);
this->_columnBuffer = new unsigned long long[DEFAULTMETRICS]();
this->_columnRawReadings = new unsigned long long[DEFAULTMETRICS * this->_skipLine.size()]();
}
fseek(this->_metricsFile, 0, SEEK_SET);
unsigned int ctr = 0, lineCtr = 0, colCtr = 0, parsedCols = 0;
unsigned int ctr=0, lineCtr=0, colCtr=0, parsedCols=0;
char *lineToken, *cpuToken, *savePtr;
bool nodeCPU = false;
......@@ -448,18 +470,20 @@ bool ProcstatPercParser::_readMetrics() {
this->_accumulator = 1;
// We iterate over the line and capture all of the CPU core-related metrics
while((lineToken = strtok_r(NULL, this->LINE_SEP, &savePtr)) != NULL && colCtr < this->DEFAULTMETRICS) {
try { this->_columnReadings[colCtr] = std::stoll(lineToken); }
try { this->_latestValue = std::stoll(lineToken); }
catch (const std::invalid_argument &ia) { return false; }
catch (const std::out_of_range &oor) { return false; }
this->_accumulator += this->_columnReadings[colCtr++];
// Computing the difference between the latest reading and the corresponding previous one
this->_columnBuffer[colCtr] = this->_latestValue - this->_columnRawReadings[(lineCtr-1)*DEFAULTMETRICS + colCtr];
this->_columnRawReadings[(lineCtr-1)*DEFAULTMETRICS + colCtr] = this->_latestValue;
this->_accumulator += this->_columnBuffer[colCtr++];
}
parsedCols = colCtr;
for(colCtr=0; colCtr<parsedCols; colCtr++) {
// Second part of the if: if the line contains node-level CPU metrics (starts with "cpu") and the
// column is flagged with 2 (sample only at node level) then the metric can be sampled
if (this->_skipColumn[colCtr] == 1 || (this->_skipColumn[colCtr] == 2 && strlen(cpuToken) == this->_cpu_prefix_len)) {
this->_readings->at(ctr++).value = this->_columnReadings[colCtr] * 1000 / this->_accumulator;
}
if (this->_skipColumn[colCtr] == 1 || (this->_skipColumn[colCtr] == 2 && strlen(cpuToken) == this->_cpu_prefix_len))
this->_readings->at(ctr++).value = this->_columnBuffer[colCtr] * 1000 / this->_accumulator;
}
// Error: less CPU core-related metrics than those counted upon initialization were parsed
//if ( colCtr != this->_numColumns ) return NULL;
......
......@@ -123,7 +123,7 @@ protected:
bool _readMetrics() override;
// Internal variables
unsigned short _numColumns;
unsigned int _numColumns;
boost::cmatch _match;
// The number of known metrics in each "cpu" line together with their names, as of Oct. 2018
enum { DEFAULTMETRICS = 10 };
......@@ -139,23 +139,28 @@ protected:
// **************************************************************
/**
* The ProcstatPercParser class allows for parsing and reading values from a /proc/stat file, with percentage readings.
* The SARParser class allows for parsing and reading values from a /proc/stat file, with percentage readings.
*
* This parser extends the normal ProcstatParser by computing percentage readings for the cpu columns, instead of
* using the raw values. Each column is divided by the sum of all columns, and then multiplied by 1000 to be stored
* as an integer.
* as an integer. Moreover, we use the differences between successive readings for each column.
*
*/
class ProcstatPercParser : public ProcstatParser {
class SARParser : public ProcstatParser {
public:
ProcstatPercParser(const std::string path = "") : ProcstatParser(path) {}
SARParser(const std::string path = "") : ProcstatParser(path) {}
void close() override;
protected:
bool _readMetrics() override;
unsigned long long _columnReadings[DEFAULTMETRICS];
// Stores the readings for all columns of a given CPU line; used to later compute percentages
unsigned long long *_columnBuffer;
// Matrix storing the readings of columns from all CPU lines; used to compute differences between readings
unsigned long long *_columnRawReadings;
unsigned long long _accumulator;
unsigned long long _latestValue;
};
#endif /* PROCFSPARSER_H_ */
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment