File: procfs_stat_cpu_parser.cc

package info (click to toggle)
chromium 138.0.7204.92-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,576 kB
  • sloc: cpp: 34,933,512; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,956; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (148 lines) | stat: -rw-r--r-- 5,114 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/system_cpu/procfs_stat_cpu_parser.h"

#include <stdint.h>

#include <limits>
#include <string_view>
#include <utility>
#include <vector>

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/sequence_checker.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/system/sys_info.h"

namespace system_cpu {

constexpr base::FilePath::CharType ProcfsStatCpuParser::kProcfsStatPath[];

ProcfsStatCpuParser::ProcfsStatCpuParser(base::FilePath stat_path)
    : stat_path_(std::move(stat_path)) {
  core_times_.reserve(base::SysInfo::NumberOfProcessors());
  DETACH_FROM_SEQUENCE(sequence_checker_);
}

ProcfsStatCpuParser::~ProcfsStatCpuParser() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}

bool ProcfsStatCpuParser::Update() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  // This implementation takes advantage of the fact that /proc/stat has 8
  // lines in addition to the per-core lines (cpu0...cpuN). These 8 lines are
  // cpu, intr, ctxt, btime, processes, procs_running, procs_blocked, softirq.
  // Each of these lines consists of a small number of tokens. Each
  // token has a small upper-bound on its size, because tokens are 64-bit
  // base-10 numbers.
  //
  // This has the following consequences.
  // 1) Reading the whole file in memory has a constant size/memory overhead,
  //    relative to the class' usage of per-core CoreTime structs.
  // 2) Splitting the entire file into lines and processing each line has a
  //    constant size/memory overhead compared to a streaming parser that
  //    ignores irrelevant data and stops after the last per-core line (cpuN).
  std::string stat_bytes;

  // This implementation could use base::ReadFileToStringWithMaxSize() to avoid
  // the risk that a kernel bug leads to an OOM. The size limit depends on the
  // maximum number of cores we'd want to support.
  //
  // Each CPU line has ~220 bytes, and the other lines should amount to less
  // than 10,000 bytes. So, for example, a limit of 2.3Mb should be sufficient
  // to support systems up to 10,000 cores.
  if (!base::ReadFileToString(stat_path_, &stat_bytes)) {
    return false;
  }

  static constexpr std::string_view kNewlineSeparator("\n", 1);
  std::vector<std::string_view> stat_lines = base::SplitStringPiece(
      stat_bytes, kNewlineSeparator, base::WhitespaceHandling::KEEP_WHITESPACE,
      base::SplitResult::SPLIT_WANT_ALL);
  for (std::string_view stat_line : stat_lines) {
    int core_id = CoreIdFromLine(stat_line);
    if (core_id < 0) {
      continue;
    }

    CHECK_LE(core_times_.size(), size_t{std::numeric_limits<int>::max()});
    if (static_cast<int>(core_times_.size()) <= core_id) {
      core_times_.resize(core_id + 1);
    }

    CoreTimes& current_core_times = core_times_[core_id];
    UpdateCore(stat_line, current_core_times);
  }

  return true;
}

// static
int ProcfsStatCpuParser::CoreIdFromLine(std::string_view stat_line) {
  // The first token of valid lines is cpu<number>. The token is at least 4
  // characters ("cpu" plus one digit).
  auto space_index = stat_line.find(' ');
  if (space_index < 4 || space_index == std::string_view::npos) {
    return -1;
  }

  if (stat_line[0] != 'c' || stat_line[1] != 'p' || stat_line[2] != 'u') {
    return -1;
  }
  std::string_view core_id_string = stat_line.substr(3, space_index - 3);

  int core_id;
  if (!base::StringToInt(core_id_string, &core_id) || core_id < 0) {
    return -1;
  }

  return core_id;
}

// static
void ProcfsStatCpuParser::UpdateCore(std::string_view core_line,
                                     CoreTimes& core_times) {
  CHECK_GE(CoreIdFromLine(core_line), 0);

  static constexpr std::string_view kSpaceSeparator(" ", 1);
  std::vector<std::string_view> tokens = base::SplitStringPiece(
      core_line, kSpaceSeparator, base::WhitespaceHandling::KEEP_WHITESPACE,
      base::SplitResult::SPLIT_WANT_ALL);

  // Accept lines with more than 10 numbers, so the code keeps working if
  // /proc/stat is extended with new per-core metrics.
  //
  // The first token on the line is the "cpuN" core ID. One core ID plus 10
  // numbers equals 11 tokens.
  if (tokens.size() < 11) {
    return;
  }

  std::vector<uint64_t> parsed_numbers(10, 0);
  for (int i = 0; i < 10; ++i) {
    uint64_t parsed_number;
    if (!base::StringToUint64(tokens[i + 1], &parsed_number)) {
      break;
    }
    parsed_numbers[i] = parsed_number;
  }

  core_times.set_user(parsed_numbers[0]);
  core_times.set_nice(parsed_numbers[1]);
  core_times.set_system(parsed_numbers[2]);
  core_times.set_idle(parsed_numbers[3]);
  core_times.set_iowait(parsed_numbers[4]);
  core_times.set_irq(parsed_numbers[5]);
  core_times.set_softirq(parsed_numbers[6]);
  core_times.set_steal(parsed_numbers[7]);
  core_times.set_guest(parsed_numbers[8]);
  core_times.set_guest_nice(parsed_numbers[9]);
}

}  // namespace system_cpu