1 /*
2 Copyright 2019 Google LLC
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 https://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20
21 #include <cstring>
22 #include <fstream>
23 #include <iostream>
24
25 #include "absl/strings/numbers.h"
26 #include "apib/apib_cpu.h"
27 #include "apib/apib_lines.h"
28 #include "apib/apib_time.h"
29
30 #define PROC_BUF_LEN 8192
31
32 namespace apib {
33
34 static int CPUCount;
35 static double TicksPerSecond;
36
37 /* Count the CPUs in a simple way by counting "processor" lines in /proc/cpuinfo
38 */
cpu_Count()39 int cpu_Count() {
40 std::ifstream in("/proc/cpuinfo");
41 if (in.fail()) {
42 return 1;
43 }
44
45 LineState line(PROC_BUF_LEN);
46 int count = 0;
47 int rc;
48
49 do {
50 rc = line.readStream(in);
51 if (rc > 0) {
52 while (line.next()) {
53 const auto l = line.line();
54 const auto ten = l.substr(0, 10);
55 if (("processor " == ten) || ("processor\t" == ten)) {
56 count++;
57 }
58 }
59 line.consume();
60 }
61 } while (rc > 0);
62 in.close();
63
64 if (count < 1) {
65 count = 1;
66 }
67 return count;
68 }
69
cpu_Init()70 int cpu_Init() {
71 struct stat statBuf;
72
73 int err = stat("/proc/stat", &statBuf);
74 if (err != 0) {
75 return -1;
76 }
77 err = stat("/proc/meminfo", &statBuf);
78 if (err != 0) {
79 return -2;
80 }
81
82 TicksPerSecond = sysconf(_SC_CLK_TCK);
83
84 const char* countStr = getenv("CPU_COUNT");
85 if (countStr != NULL) {
86 CPUCount = atoi(countStr);
87 return 0;
88 }
89
90 CPUCount = cpu_Count();
91 if (CPUCount < 0) {
92 return -3;
93 }
94 return 0;
95 }
96
cpu_GetMemoryUsage()97 double cpu_GetMemoryUsage() {
98 std::ifstream meminfo("/proc/meminfo");
99 if (meminfo.fail()) {
100 return 0.0;
101 }
102
103 // PROC_BUF_LEN should be big enough to hold all of /proc/meminfo!
104 LineState line(PROC_BUF_LEN);
105
106 int rc = line.readStream(meminfo);
107 if (rc < 0) {
108 return 0.0;
109 }
110
111 long totalMem = 0;
112 long freeMem = 0;
113 long buffers = 0;
114 long cache = 0;
115
116 while (line.next()) {
117 const auto n = line.nextToken(" ");
118 const auto v = line.nextToken(" ");
119
120 if ("MemTotal:" == n) {
121 if (!absl::SimpleAtoi(v, &totalMem)) {
122 return 0.0;
123 }
124 } else if ("MemFree:" == n) {
125 if (!absl::SimpleAtoi(v, &freeMem)) {
126 return 0.0;
127 }
128 } else if ("Buffers:" == n) {
129 if (!absl::SimpleAtoi(v, &buffers)) {
130 return 0.0;
131 }
132 } else if ("Cached:" == n) {
133 if (!absl::SimpleAtoi(v, &cache)) {
134 return 0.0;
135 }
136 }
137 }
138
139 if ((totalMem <= 0) || (freeMem <= 0)) {
140 return 0.0;
141 }
142
143 return (double)(totalMem - (freeMem + buffers + cache)) / (double)totalMem;
144 }
145
getTicks(CPUUsage * cpu)146 static int getTicks(CPUUsage* cpu) {
147 std::ifstream stat("/proc/stat");
148 if (stat.fail()) {
149 return 0;
150 }
151
152 LineState line(PROC_BUF_LEN);
153
154 const int rc = line.readStream(stat);
155 if (rc < 0) {
156 return 0;
157 }
158
159 while (line.next()) {
160 const auto l = line.line();
161 if ("cpu " == l.substr(0, 4)) {
162 // Read the "cpu" line, which is a sum of all CPUs
163 int64_t idleCount = 0LL;
164 int64_t nonIdleCount = 0LL;
165 int i = 0;
166 absl::string_view tok;
167
168 line.nextToken(" \t");
169 do {
170 tok = line.nextToken(" \t");
171 if (!tok.empty()) {
172 int64_t c;
173 if (!absl::SimpleAtoi(tok, &c)) {
174 continue;
175 }
176 switch (i) {
177 case 3:
178 case 4:
179 case 7:
180 /* The fourth and fifth columns are "idle" and "iowait".
181 We consider both to be idle CPU.
182 The eigth is "steal", which is time lost to virtualization
183 as a client -- that's idle too in our estimation */
184 idleCount += c;
185 break;
186 default:
187 nonIdleCount += c;
188 break;
189 }
190 i++;
191 }
192 } while (!tok.empty());
193 cpu->nonIdle = nonIdleCount;
194 cpu->idle = idleCount;
195 return 1;
196 }
197 }
198
199 return 0;
200 }
201
cpu_GetUsage(CPUUsage * cpu)202 void cpu_GetUsage(CPUUsage* cpu) {
203 getTicks(cpu);
204 cpu->timestamp = GetTime();
205 }
206
cpu_GetInterval(CPUUsage * oldCpu)207 double cpu_GetInterval(CPUUsage* oldCpu) {
208 CPUUsage cpu;
209
210 if (!getTicks(&cpu)) {
211 return 0;
212 }
213 cpu.timestamp = GetTime();
214
215 const long long idleTicks = cpu.idle - oldCpu->idle;
216 const long long usageTicks = cpu.nonIdle - oldCpu->nonIdle;
217 const long long allUsageTicks = idleTicks + usageTicks;
218
219 memcpy(oldCpu, &cpu, sizeof(CPUUsage));
220
221 if (allUsageTicks == 0) {
222 return 0.0;
223 }
224 return ((double)usageTicks / (double)allUsageTicks);
225 }
226
227 } // namespace apib