SchedInfo TaskSchedFile::doRead()

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;
}