in cpp/counters/ProcFs.cpp [378:491]
SchedInfo TaskSchedFile::doRead(int fd, uint32_t requested_stats_mask) {
int size = read(fd, buffer_, kMaxStatFileLength - 1);
if (size < 0) {
throw std::system_error(
errno, std::system_category(), "Could not read stat file");
}
char* endfile = buffer_ + size;
if (!initialized_) {
struct KnownKey {
const char* key;
StatType type;
};
static std::array<KnownKey, 4> kKnownKeys = {
{{"nr_voluntary_switches", StatType::NR_VOLUNTARY_SWITCHES},
{"nr_involuntary_switches", StatType::NR_INVOLUNTARY_SWITCHES},
{"se.statistics.iowait_count", StatType::IOWAIT_COUNT},
{"se.statistics.iowait_sum", StatType::IOWAIT_SUM}}};
// Skip 2 lines.
auto endline = std::strchr(buffer_, '\n');
if (endline == nullptr) {
throw std::runtime_error("Unexpected file format");
}
endline = std::strchr(endline + 1, '\n');
if (endline == nullptr) {
throw std::runtime_error("Unexpected file format");
}
// The stat file line is in the form of key:value record with fixed line
// length per metric, but which can vary depending on a metric name.
// The key is aligned to the left and the value is aligned to the
// right: "key : value"
// In the loop we parse the buffer line by line reading the key-value pairs.
// If key is in the known keys (kKnownKeys) we calculate a global offset to
// the value in the file and record it for fast access in the future.
for (auto pos = endline + 1; pos < endfile;) {
// Sometimes colon delimiter can go right after key ("key:") and we should
// account for this case too.
auto key_end = std::strchr(pos, ' ');
auto delim = std::strchr(pos, ':');
if (key_end == nullptr || delim == nullptr) {
break;
}
auto key_len =
std::min(std::distance(pos, key_end), std::distance(pos, delim));
auto known_key = std::find_if(
kKnownKeys.begin(),
kKnownKeys.end(),
[pos, key_len](KnownKey const& key) {
return std::strncmp(key.key, pos, key_len) == 0;
});
if (known_key != kKnownKeys.end()) {
int value_offset = std::distance(buffer_, delim) + 1;
value_offsets_.push_back(std::make_pair(known_key->type, value_offset));
availableStatsMask |= known_key->type;
}
// Switch to the next line.
pos = std::strchr(delim, '\n');
if (!pos) {
break;
}
if (!value_size_) {
// Saving allocated space for value (fixed size for all stats) to detect
// truncated values.
value_size_ = std::distance(delim, pos);
}
++pos;
}
initialized_ = true;
}
if (value_offsets_.empty()) {
throw std::runtime_error("No target fields found");
}
SchedInfo schedInfo{};
for (auto& entry : value_offsets_) {
auto key_type = entry.first;
auto value_offset = entry.second;
if (value_offset + value_size_ > size) {
// Possibly truncated value, ignoring.
continue;
}
errno = 0;
char* endptr;
auto value = parse_ull(buffer_ + value_offset, &endptr);
if (errno == ERANGE || (buffer_ + value_offset) == endptr ||
endptr > endfile) {
throw std::runtime_error("Could not parse value");
}
switch (key_type) {
case StatType::NR_VOLUNTARY_SWITCHES:
schedInfo.nrVoluntarySwitches = value;
break;
case StatType::NR_INVOLUNTARY_SWITCHES:
schedInfo.nrInvoluntarySwitches = value;
break;
case StatType::IOWAIT_COUNT:
schedInfo.iowaitCount = value;
break;
case StatType::IOWAIT_SUM:
schedInfo.iowaitSum = value;
break;
}
}
return schedInfo;
}