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