• 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     return StringPrintf("Cpu Stats:\nUserTimeMillis: %" PRIu64 " NiceTimeMillis: %" PRIu64
45                         " SysTimeMillis: %" PRIu64 " IdleTimeMillis: %" PRIu64
46                         " IoWaitTimeMillis: %" PRIu64 " IrqTimeMillis: %" PRIu64
47                         " SoftIrqTimeMillis: %" PRIu64 " StealTimeMillis: %" PRIu64
48                         " GuestTimeMillis: %" PRIu64 " GuestNiceTimeMillis: %" PRIu64
49                         "\nNumber of running processes: %" PRIu32
50                         "\nNumber of blocked processes: %" PRIu32,
51                         cpuStats.userTimeMillis, cpuStats.niceTimeMillis, cpuStats.sysTimeMillis,
52                         cpuStats.idleTimeMillis, cpuStats.ioWaitTimeMillis, cpuStats.irqTimeMillis,
53                         cpuStats.softIrqTimeMillis, cpuStats.stealTimeMillis,
54                         cpuStats.guestTimeMillis, cpuStats.guestNiceTimeMillis,
55                         info.runnableProcessCount, info.ioBlockedProcessCount);
56 }
57 
58 }  // namespace
59 
TEST(ProcStatCollectorTest,TestValidStatFile)60 TEST(ProcStatCollectorTest, TestValidStatFile) {
61     constexpr char firstSnapshot[] =
62             "cpu  6200 5700 1700 3100 1100 5200 3900 0 0 0\n"
63             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
64             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
65             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
66             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
67             "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 "
68             "0 0\n"
69             /* Skipped most of the intr line as it is not important for testing the ProcStat parsing
70              * logic.
71              */
72             "ctxt 579020168\n"
73             "btime 1579718450\n"
74             "processes 113804\n"
75             "procs_running 17\n"
76             "procs_blocked 5\n"
77             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
78     ProcStatInfo expectedFirstDelta;
79     expectedFirstDelta.cpuStats = {
80             .userTimeMillis = clockTicksToMillis(6200),
81             .niceTimeMillis = clockTicksToMillis(5700),
82             .sysTimeMillis = clockTicksToMillis(1700),
83             .idleTimeMillis = clockTicksToMillis(3100),
84             .ioWaitTimeMillis = clockTicksToMillis(1100),
85             .irqTimeMillis = clockTicksToMillis(5200),
86             .softIrqTimeMillis = clockTicksToMillis(3900),
87             .stealTimeMillis = clockTicksToMillis(0),
88             .guestTimeMillis = clockTicksToMillis(0),
89             .guestNiceTimeMillis = clockTicksToMillis(0),
90     };
91     expectedFirstDelta.contextSwitchesCount = 579020168;
92     expectedFirstDelta.runnableProcessCount = 17;
93     expectedFirstDelta.ioBlockedProcessCount = 5;
94 
95     TemporaryFile tf;
96     ASSERT_NE(tf.fd, -1);
97     ASSERT_TRUE(WriteStringToFile(firstSnapshot, tf.path));
98 
99     ProcStatCollector collector(tf.path);
100     collector.init();
101 
102     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
103     ASSERT_RESULT_OK(collector.collect());
104 
105     const auto& actualFirstDelta = collector.deltaStats();
106     EXPECT_EQ(expectedFirstDelta, actualFirstDelta) << "First snapshot doesn't match.\nExpected:\n"
107                                                     << toString(expectedFirstDelta) << "\nActual:\n"
108                                                     << toString(actualFirstDelta);
109 
110     constexpr char secondSnapshot[] =
111             "cpu  16200 8700 2000 4100 2200 6200 5900 0 0 0\n"
112             "cpu0 4400 3400 700 890 800 4500 3100 0 0 0\n"
113             "cpu1 5900 3380 610 960 100 670 2000 0 0 0\n"
114             "cpu2 2900 1000 450 1400 800 600 460 0 0 0\n"
115             "cpu3 3000 920 240 850 500 430 340 0 0 0\n"
116             "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 "
117             "0 0\n"
118             "ctxt 810020192\n"
119             "btime 1579718450\n"
120             "processes 113804\n"
121             "procs_running 10\n"
122             "procs_blocked 2\n"
123             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
124     ProcStatInfo expectedSecondDelta;
125     expectedSecondDelta.cpuStats = {
126             .userTimeMillis = clockTicksToMillis(10000),
127             .niceTimeMillis = clockTicksToMillis(3000),
128             .sysTimeMillis = clockTicksToMillis(300),
129             .idleTimeMillis = clockTicksToMillis(1000),
130             .ioWaitTimeMillis = clockTicksToMillis(1100),
131             .irqTimeMillis = clockTicksToMillis(1000),
132             .softIrqTimeMillis = clockTicksToMillis(2000),
133             .stealTimeMillis = clockTicksToMillis(0),
134             .guestTimeMillis = clockTicksToMillis(0),
135             .guestNiceTimeMillis = clockTicksToMillis(0),
136     };
137     expectedFirstDelta.contextSwitchesCount = 810020192;
138     expectedSecondDelta.runnableProcessCount = 10;
139     expectedSecondDelta.ioBlockedProcessCount = 2;
140 
141     ASSERT_TRUE(WriteStringToFile(secondSnapshot, tf.path));
142     ASSERT_RESULT_OK(collector.collect());
143 
144     const auto& actualSecondDelta = collector.deltaStats();
145     EXPECT_EQ(expectedSecondDelta, actualSecondDelta)
146             << "Second snapshot doesnt't match.\nExpected:\n"
147             << toString(expectedSecondDelta) << "\nActual:\n"
148             << toString(actualSecondDelta);
149 }
150 
TEST(ProcStatCollectorTest,TestErrorOnCorruptedStatFile)151 TEST(ProcStatCollectorTest, TestErrorOnCorruptedStatFile) {
152     constexpr char contents[] =
153             "cpu  6200 5700 1700 3100 CORRUPTED DATA\n"
154             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
155             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
156             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
157             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
158             "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 "
159             "0 0\n"
160             "ctxt 579020168\n"
161             "btime 1579718450\n"
162             "processes 113804\n"
163             "procs_running 17\n"
164             "procs_blocked 5\n"
165             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
166     TemporaryFile tf;
167     ASSERT_NE(tf.fd, -1);
168     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
169 
170     ProcStatCollector collector(tf.path);
171     collector.init();
172 
173     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
174     EXPECT_FALSE(collector.collect().ok()) << "No error returned for corrupted file";
175 }
176 
TEST(ProcStatCollectorTest,TestErrorOnMissingCpuLine)177 TEST(ProcStatCollectorTest, TestErrorOnMissingCpuLine) {
178     constexpr char contents[] =
179             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
180             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
181             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
182             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
183             "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 "
184             "0 0\n"
185             "ctxt 579020168\n"
186             "btime 1579718450\n"
187             "processes 113804\n"
188             "procs_running 17\n"
189             "procs_blocked 5\n"
190             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
191     TemporaryFile tf;
192     ASSERT_NE(tf.fd, -1);
193     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
194 
195     ProcStatCollector collector(tf.path);
196     collector.init();
197 
198     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
199     EXPECT_FALSE(collector.collect().ok()) << "No error returned due to missing cpu line";
200 }
201 
TEST(ProcStatCollectorTest,TestErrorOnMissingCtxtLine)202 TEST(ProcStatCollectorTest, TestErrorOnMissingCtxtLine) {
203     constexpr char contents[] =
204             "cpu  16200 8700 2000 4100 1250 6200 5900 0 0 0\n"
205             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
206             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
207             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
208             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
209             "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 "
210             "0 0\n"
211             "btime 1579718450\n"
212             "processes 113804\n"
213             "procs_running 17\n"
214             "procs_blocked 5\n"
215             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
216     TemporaryFile tf;
217     ASSERT_NE(tf.fd, -1);
218     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
219 
220     ProcStatCollector collector(tf.path);
221     collector.init();
222 
223     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
224     EXPECT_FALSE(collector.collect().ok()) << "No error returned due to missing ctxt line";
225 }
226 
TEST(ProcStatCollectorTest,TestErrorOnMissingProcsRunningLine)227 TEST(ProcStatCollectorTest, TestErrorOnMissingProcsRunningLine) {
228     constexpr char contents[] =
229             "cpu  16200 8700 2000 4100 1250 6200 5900 0 0 0\n"
230             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
231             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
232             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
233             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
234             "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 "
235             "0 0\n"
236             "ctxt 579020168\n"
237             "btime 1579718450\n"
238             "processes 113804\n"
239             "procs_blocked 5\n"
240             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
241     TemporaryFile tf;
242     ASSERT_NE(tf.fd, -1);
243     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
244 
245     ProcStatCollector collector(tf.path);
246     collector.init();
247 
248     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
249     EXPECT_FALSE(collector.collect().ok()) << "No error returned due to missing procs_running line";
250 }
251 
TEST(ProcStatCollectorTest,TestErrorOnMissingProcsBlockedLine)252 TEST(ProcStatCollectorTest, TestErrorOnMissingProcsBlockedLine) {
253     constexpr char contents[] =
254             "cpu  16200 8700 2000 4100 1250 6200 5900 0 0 0\n"
255             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
256             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
257             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
258             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
259             "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 "
260             "0 0\n"
261             "ctxt 579020168\n"
262             "btime 1579718450\n"
263             "processes 113804\n"
264             "procs_running 17\n"
265             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
266     TemporaryFile tf;
267     ASSERT_NE(tf.fd, -1);
268     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
269 
270     ProcStatCollector collector(tf.path);
271     collector.init();
272 
273     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
274     EXPECT_FALSE(collector.collect().ok()) << "No error returned due to missing procs_blocked line";
275 }
276 
TEST(ProcStatCollectorTest,TestErrorOnUnknownProcsLine)277 TEST(ProcStatCollectorTest, TestErrorOnUnknownProcsLine) {
278     constexpr char contents[] =
279             "cpu  16200 8700 2000 4100 1250 6200 5900 0 0 0\n"
280             "cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
281             "cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
282             "cpu2 900 400 400 1000 600 400 160 0 0 0\n"
283             "cpu3 1000 20 190 650 109 130 140 0 0 0\n"
284             "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 "
285             "0 0\n"
286             "ctxt 579020168\n"
287             "btime 1579718450\n"
288             "processes 113804\n"
289             "procs_running 17\n"
290             "procs_blocked 5\n"
291             "procs_sleeping 15\n"
292             "softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
293     TemporaryFile tf;
294     ASSERT_NE(tf.fd, -1);
295     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
296 
297     ProcStatCollector collector(tf.path);
298     collector.init();
299 
300     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
301     EXPECT_FALSE(collector.collect().ok()) << "No error returned due to unknown procs line";
302 }
303 
TEST(ProcStatCollectorTest,TestProcStatContentsFromDevice)304 TEST(ProcStatCollectorTest, TestProcStatContentsFromDevice) {
305     ProcStatCollector collector;
306     collector.init();
307 
308     ASSERT_TRUE(collector.enabled()) << kProcStatPath << " file is inaccessible";
309     ASSERT_RESULT_OK(collector.collect());
310 
311     const auto& info = collector.deltaStats();
312     /* The below checks should pass because the /proc/stats file should have the CPU time spent
313      * since bootup and there should be at least one running process.
314      */
315     EXPECT_GT(info.totalCpuTimeMillis(), 0);
316     EXPECT_GT(info.totalProcessCount(), static_cast<uint32_t>(0));
317 }
318 
319 }  // namespace watchdog
320 }  // namespace automotive
321 }  // namespace android
322