• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 The Android Open Source Project
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  *      http://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 "ProcStatCollector.h"
18 
19 #include <android-base/file.h>
20 #include <android-base/stringprintf.h>
21 #include <gmock/gmock.h>
22 
23 #include <inttypes.h>
24 
25 #include <string>
26 
27 namespace android {
28 namespace automotive {
29 namespace watchdog {
30 
31 namespace {
32 
33 using ::android::base::StringPrintf;
34 using ::android::base::WriteStringToFile;
35 
36 const int64_t kMillisPerClockTick = 1000 / sysconf(_SC_CLK_TCK);
37 
clockTicksToMillis(int64_t ticks)38 int64_t clockTicksToMillis(int64_t ticks) {
39     return ticks * kMillisPerClockTick;
40 }
41 
toString(const ProcStatInfo & info)42 std::string toString(const ProcStatInfo& info) {
43     const auto& cpuStats = info.cpuStats;
44     std::stringstream kernelStartTimeEpochSeconds;
45     kernelStartTimeEpochSeconds << info.kernelStartTimeEpochSeconds;
46     return StringPrintf("KernelStartTimeEpochSeconds: %s \nCpu Stats:\nUserTimeMillis: %" PRIu64
47                         " NiceTimeMillis: %" PRIu64 " SysTimeMillis: %" PRIu64
48                         " IdleTimeMillis: %" PRIu64 " IoWaitTimeMillis: %" PRIu64
49                         " IrqTimeMillis: %" PRIu64 " SoftIrqTimeMillis: %" PRIu64
50                         " StealTimeMillis: %" PRIu64 " GuestTimeMillis: %" PRIu64
51                         " GuestNiceTimeMillis: %" PRIu64 "\nNumber of running processes: %" PRIu32
52                         "\nNumber of blocked processes: %" PRIu32
53                         "\nNumber of context switches: %" PRIu64,
54                         kernelStartTimeEpochSeconds.str().c_str(), cpuStats.userTimeMillis,
55                         cpuStats.niceTimeMillis, cpuStats.sysTimeMillis, cpuStats.idleTimeMillis,
56                         cpuStats.ioWaitTimeMillis, cpuStats.irqTimeMillis,
57                         cpuStats.softIrqTimeMillis, cpuStats.stealTimeMillis,
58                         cpuStats.guestTimeMillis, cpuStats.guestNiceTimeMillis,
59                         info.runnableProcessCount, info.ioBlockedProcessCount,
60                         info.contextSwitchesCount);
61 }
62 
63 }  // namespace
64 
TEST(ProcStatCollectorTest,TestValidStatFile)65 TEST(ProcStatCollectorTest, TestValidStatFile) {
66     constexpr char firstSnapshot[] =
67             "cpu  6200 5700 1700 3100 1100 5200 3900 0 0 0\n"
68             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
69             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
70             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
71             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
72             "intr 694351583 0 0 0 297062868 0 5922464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
73             "0 0\n"
74             /* Skipped most of the intr line as it is not important for testing the ProcStat parsing
75              * logic.
76              */
77             "ctxt 579020168\n"
78             "btime 1579718450\n"
79             "processes 113804\n"
80             "procs_running 17\n"
81             "procs_blocked 5\n"
82             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
83     ProcStatInfo expectedFirstDelta;
84     expectedFirstDelta.kernelStartTimeEpochSeconds = 1579718450;
85     expectedFirstDelta.cpuStats = {
86             .userTimeMillis = clockTicksToMillis(6200),
87             .niceTimeMillis = clockTicksToMillis(5700),
88             .sysTimeMillis = clockTicksToMillis(1700),
89             .idleTimeMillis = clockTicksToMillis(3100),
90             .ioWaitTimeMillis = clockTicksToMillis(1100),
91             .irqTimeMillis = clockTicksToMillis(5200),
92             .softIrqTimeMillis = clockTicksToMillis(3900),
93             .stealTimeMillis = clockTicksToMillis(0),
94             .guestTimeMillis = clockTicksToMillis(0),
95             .guestNiceTimeMillis = clockTicksToMillis(0),
96     };
97     expectedFirstDelta.contextSwitchesCount = 579020168;
98     expectedFirstDelta.runnableProcessCount = 17;
99     expectedFirstDelta.ioBlockedProcessCount = 5;
100 
101     TemporaryFile tf;
102     ASSERT_NE(tf.fd, -1);
103     ASSERT_TRUE(WriteStringToFile(firstSnapshot, tf.path));
104 
105     ProcStatCollector collector(tf.path);
106     collector.init();
107 
108     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
109     ASSERT_RESULT_OK(collector.collect());
110 
111     const auto& actualFirstDelta = collector.deltaStats();
112     EXPECT_EQ(expectedFirstDelta, actualFirstDelta) << "First snapshot doesn't match.\nExpected:\n"
113                                                     << toString(expectedFirstDelta) << "\nActual:\n"
114                                                     << toString(actualFirstDelta);
115 
116     constexpr char secondSnapshot[] =
117             "cpu  16200 8700 2000 4100 2200 6200 5900 0 0 0\n"
118             "cpu0 4400 3400 700 890 800 4500 3100 0 0 0\n"
119             "cpu1 5900 3380 610 960 100 670 2000 0 0 0\n"
120             "cpu2 2900 1000 450 1400 800 600 460 0 0 0\n"
121             "cpu3 3000 920 240 850 500 430 340 0 0 0\n"
122             "intr 694351583 0 0 0 297062868 0 5922464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
123             "0 0\n"
124             "ctxt 810020192\n"
125             "btime 1579718450\n"
126             "processes 113804\n"
127             "procs_running 10\n"
128             "procs_blocked 2\n"
129             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
130     ProcStatInfo expectedSecondDelta;
131     expectedSecondDelta.cpuStats = {
132             .userTimeMillis = clockTicksToMillis(10000),
133             .niceTimeMillis = clockTicksToMillis(3000),
134             .sysTimeMillis = clockTicksToMillis(300),
135             .idleTimeMillis = clockTicksToMillis(1000),
136             .ioWaitTimeMillis = clockTicksToMillis(1100),
137             .irqTimeMillis = clockTicksToMillis(1000),
138             .softIrqTimeMillis = clockTicksToMillis(2000),
139             .stealTimeMillis = clockTicksToMillis(0),
140             .guestTimeMillis = clockTicksToMillis(0),
141             .guestNiceTimeMillis = clockTicksToMillis(0),
142     };
143     expectedSecondDelta.kernelStartTimeEpochSeconds = 1579718450;
144     expectedSecondDelta.contextSwitchesCount = 231000024;
145     expectedSecondDelta.runnableProcessCount = 10;
146     expectedSecondDelta.ioBlockedProcessCount = 2;
147 
148     ASSERT_TRUE(WriteStringToFile(secondSnapshot, tf.path));
149     ASSERT_RESULT_OK(collector.collect());
150 
151     const auto& actualSecondDelta = collector.deltaStats();
152     EXPECT_EQ(expectedSecondDelta, actualSecondDelta)
153             << "Second snapshot doesn't match.\nExpected:\n"
154             << toString(expectedSecondDelta) << "\nActual:\n"
155             << toString(actualSecondDelta);
156 }
157 
TEST(ProcStatCollectorTest,TestErrorOnCorruptedStatFile)158 TEST(ProcStatCollectorTest, TestErrorOnCorruptedStatFile) {
159     constexpr char contents[] =
160             "cpu  6200 5700 1700 3100 CORRUPTED DATA\n"
161             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
162             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
163             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
164             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
165             "intr 694351583 0 0 0 297062868 0 5922464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
166             "0 0\n"
167             "ctxt 579020168\n"
168             "btime 1579718450\n"
169             "processes 113804\n"
170             "procs_running 17\n"
171             "procs_blocked 5\n"
172             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
173     TemporaryFile tf;
174     ASSERT_NE(tf.fd, -1);
175     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
176 
177     ProcStatCollector collector(tf.path);
178     collector.init();
179 
180     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
181     EXPECT_FALSE(collector.collect().ok()) << "No error returned for corrupted file";
182 }
183 
TEST(ProcStatCollectorTest,TestErrorOnMissingCpuLine)184 TEST(ProcStatCollectorTest, TestErrorOnMissingCpuLine) {
185     constexpr char contents[] =
186             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
187             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
188             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
189             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
190             "intr 694351583 0 0 0 297062868 0 5922464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
191             "0 0\n"
192             "ctxt 579020168\n"
193             "btime 1579718450\n"
194             "processes 113804\n"
195             "procs_running 17\n"
196             "procs_blocked 5\n"
197             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
198     TemporaryFile tf;
199     ASSERT_NE(tf.fd, -1);
200     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
201 
202     ProcStatCollector collector(tf.path);
203     collector.init();
204 
205     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
206     EXPECT_FALSE(collector.collect().ok()) << "No error returned due to missing cpu line";
207 }
208 
TEST(ProcStatCollectorTest,TestErrorOnMissingCtxtLine)209 TEST(ProcStatCollectorTest, TestErrorOnMissingCtxtLine) {
210     constexpr char contents[] =
211             "cpu  16200 8700 2000 4100 1250 6200 5900 0 0 0\n"
212             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
213             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
214             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
215             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
216             "intr 694351583 0 0 0 297062868 0 5922464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
217             "0 0\n"
218             "btime 1579718450\n"
219             "processes 113804\n"
220             "procs_running 17\n"
221             "procs_blocked 5\n"
222             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
223     TemporaryFile tf;
224     ASSERT_NE(tf.fd, -1);
225     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
226 
227     ProcStatCollector collector(tf.path);
228     collector.init();
229 
230     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
231     EXPECT_FALSE(collector.collect().ok()) << "No error returned due to missing ctxt line";
232 }
233 
TEST(ProcStatCollectorTest,TestErrorOnMissingProcsRunningLine)234 TEST(ProcStatCollectorTest, TestErrorOnMissingProcsRunningLine) {
235     constexpr char contents[] =
236             "cpu  16200 8700 2000 4100 1250 6200 5900 0 0 0\n"
237             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
238             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
239             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
240             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
241             "intr 694351583 0 0 0 297062868 0 5922464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
242             "0 0\n"
243             "ctxt 579020168\n"
244             "btime 1579718450\n"
245             "processes 113804\n"
246             "procs_blocked 5\n"
247             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
248     TemporaryFile tf;
249     ASSERT_NE(tf.fd, -1);
250     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
251 
252     ProcStatCollector collector(tf.path);
253     collector.init();
254 
255     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
256     EXPECT_FALSE(collector.collect().ok()) << "No error returned due to missing procs_running line";
257 }
258 
TEST(ProcStatCollectorTest,TestErrorOnMissingProcsBlockedLine)259 TEST(ProcStatCollectorTest, TestErrorOnMissingProcsBlockedLine) {
260     constexpr char contents[] =
261             "cpu  16200 8700 2000 4100 1250 6200 5900 0 0 0\n"
262             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
263             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
264             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
265             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
266             "intr 694351583 0 0 0 297062868 0 5922464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
267             "0 0\n"
268             "ctxt 579020168\n"
269             "btime 1579718450\n"
270             "processes 113804\n"
271             "procs_running 17\n"
272             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
273     TemporaryFile tf;
274     ASSERT_NE(tf.fd, -1);
275     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
276 
277     ProcStatCollector collector(tf.path);
278     collector.init();
279 
280     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
281     EXPECT_FALSE(collector.collect().ok()) << "No error returned due to missing procs_blocked line";
282 }
283 
TEST(ProcStatCollectorTest,TestErrorOnUnknownProcsLine)284 TEST(ProcStatCollectorTest, TestErrorOnUnknownProcsLine) {
285     constexpr char contents[] =
286             "cpu  16200 8700 2000 4100 1250 6200 5900 0 0 0\n"
287             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
288             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
289             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
290             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
291             "intr 694351583 0 0 0 297062868 0 5922464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
292             "0 0\n"
293             "ctxt 579020168\n"
294             "btime 1579718450\n"
295             "processes 113804\n"
296             "procs_running 17\n"
297             "procs_blocked 5\n"
298             "procs_sleeping 15\n"
299             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
300     TemporaryFile tf;
301     ASSERT_NE(tf.fd, -1);
302     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
303 
304     ProcStatCollector collector(tf.path);
305     collector.init();
306 
307     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
308     EXPECT_FALSE(collector.collect().ok()) << "No error returned due to unknown procs line";
309 }
310 
TEST(ProcStatCollectorTest,TestProcStatContentsFromDevice)311 TEST(ProcStatCollectorTest, TestProcStatContentsFromDevice) {
312     ProcStatCollector collector;
313     collector.init();
314 
315     ASSERT_TRUE(collector.enabled()) << kProcStatPath << " file is inaccessible";
316     ASSERT_RESULT_OK(collector.collect());
317 
318     const auto& info = collector.deltaStats();
319     /* The below checks should pass because the /proc/stats file should have
320      * 1. The Kernel start time.
321      * 2. The CPU time spent since bootup.
322      * 3. There should be at least one running process.
323      */
324     EXPECT_GT(info.kernelStartTimeEpochSeconds, static_cast<time_t>(0));
325     EXPECT_GT(info.totalCpuTimeMillis(), 0);
326     EXPECT_GT(info.totalProcessCount(), static_cast<uint32_t>(0));
327 }
328 
TEST(ProcStatCollectorTest,TestReadKernelStartTimeOnce)329 TEST(ProcStatCollectorTest, TestReadKernelStartTimeOnce) {
330     constexpr char contents[] =
331             "cpu  16200 8700 2000 4100 1250 6200 5900 0 0 0\n"
332             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
333             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
334             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
335             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
336             "intr 694351583 0 0 0 297062868 0 5922464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
337             "0 0\n"
338             "ctxt 579020168\n"
339             "btime 1579718450\n"
340             "processes 113804\n"
341             "procs_running 17\n"
342             "procs_blocked 5\n"
343             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
344     TemporaryFile tf;
345     ASSERT_NE(tf.fd, -1);
346     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
347 
348     ProcStatCollector collector(tf.path);
349     collector.init();
350 
351     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
352     ASSERT_RESULT_OK(collector.collect());
353 
354     const auto& firstLatestStats = collector.latestStats();
355 
356     constexpr char contents_with_new_btime[] =
357             "cpu  16200 8700 2000 4100 1250 6200 5900 0 0 0\n"
358             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
359             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
360             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
361             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
362             "intr 694351583 0 0 0 297062868 0 5922464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
363             "0 0\n"
364             "ctxt 579020168\n"
365             "btime 6482659380\n"
366             "processes 113804\n"
367             "procs_running 17\n"
368             "procs_blocked 5\n"
369             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
370     ASSERT_TRUE(WriteStringToFile(contents_with_new_btime, tf.path));
371 
372     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
373     ASSERT_RESULT_OK(collector.collect());
374 
375     const auto& secondLatestStats = collector.latestStats();
376 
377     ASSERT_TRUE(firstLatestStats.kernelStartTimeEpochSeconds ==
378                 secondLatestStats.kernelStartTimeEpochSeconds)
379             << "kernel start time is read more than once";
380 }
381 
382 }  // namespace watchdog
383 }  // namespace automotive
384 }  // namespace android
385