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 "ProcPidDir.h"
18 #include "UidProcStatsCollector.h"
19 #include "UidProcStatsCollectorTestUtils.h"
20 
21 #include <android-base/file.h>
22 #include <android-base/stringprintf.h>
23 #include <gmock/gmock.h>
24 
25 #include <inttypes.h>
26 
27 #include <algorithm>
28 #include <string>
29 
30 namespace android {
31 namespace automotive {
32 namespace watchdog {
33 
34 using ::android::automotive::watchdog::testing::populateProcPidDir;
35 using ::android::base::StringAppendF;
36 using ::android::base::StringPrintf;
37 using ::testing::UnorderedPointwise;
38 
39 namespace {
40 
41 MATCHER(UidProcStatsByUidEq, "") {
42     const auto& actual = std::get<0>(arg);
43     const auto& expected = std::get<1>(arg);
44     return actual.first == expected.first &&
45             ExplainMatchResult(UidProcStatsEq(expected.second), actual.second, result_listener);
46 }
47 
pidStatusStr(pid_t pid,uid_t uid)48 std::string pidStatusStr(pid_t pid, uid_t uid) {
49     return StringPrintf("Pid:\t%" PRIu32 "\nTgid:\t%" PRIu32 "\nUid:\t%" PRIu32 "\n", pid, pid,
50                         uid);
51 }
52 
toString(const std::unordered_map<uid_t,UidProcStats> & uidProcStatsByUid)53 std::string toString(const std::unordered_map<uid_t, UidProcStats>& uidProcStatsByUid) {
54     std::string buffer;
55     StringAppendF(&buffer, "Number of UIDs: %" PRIi32 "\n",
56                   static_cast<int>(uidProcStatsByUid.size()));
57     for (const auto& [uid, stats] : uidProcStatsByUid) {
58         StringAppendF(&buffer, "{UID: %d, %s}", uid, stats.toString().c_str());
59     }
60     return buffer;
61 }
62 
63 }  // namespace
64 
TEST(UidProcStatsCollectorTest,TestValidStatFiles)65 TEST(UidProcStatsCollectorTest, TestValidStatFiles) {
66     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
67             {1, {1, 453}},
68             {1000, {1000, 1100}},
69     };
70 
71     std::unordered_map<pid_t, std::string> perProcessStat = {
72             {1, "1 (init) S 0 0 0 0 0 0 0 0 220 0 0 0 0 0 0 0 2 0 0\n"},
73             {1000, "1000 (system_server) D 1 0 0 0 0 0 0 0 600 0 0 0 0 0 0 0 2 0 13400\n"},
74     };
75 
76     std::unordered_map<pid_t, std::string> perProcessStatus = {
77             {1, pidStatusStr(1, 0)},
78             {1000, pidStatusStr(1000, 10001234)},
79     };
80 
81     std::unordered_map<pid_t, std::string> perThreadStat = {
82             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 0\n"},
83             {453, "453 (init) D 0 0 0 0 0 0 0 0 20 0 0 0 0 0 0 0 2 0 275\n"},
84             {1000, "1000 (system_server) D 1 0 0 0 0 0 0 0 250 0 0 0 0 0 0 0 2 0 13400\n"},
85             {1100, "1100 (system_server) D 1 0 0 0 0 0 0 0 350 0 0 0 0 0 0 0 2 0 13900\n"},
86     };
87 
88     std::unordered_map<uid_t, UidProcStats> expected =
89             {{0,
90               UidProcStats{.totalMajorFaults = 220,
91                            .totalTasksCount = 2,
92                            .ioBlockedTasksCount = 1,
93                            .processStatsByPid = {{1, {"init", 0, 220, 2, 1}}}}},
94              {10001234,
95               UidProcStats{.totalMajorFaults = 600,
96                            .totalTasksCount = 2,
97                            .ioBlockedTasksCount = 2,
98                            .processStatsByPid = {{1000, {"system_server", 13'400, 600, 2, 2}}}}}};
99 
100     TemporaryDir firstSnapshot;
101     ASSERT_RESULT_OK(populateProcPidDir(firstSnapshot.path, pidToTids, perProcessStat,
102                                         perProcessStatus, perThreadStat));
103 
104     UidProcStatsCollector collector(firstSnapshot.path);
105 
106     ASSERT_TRUE(collector.enabled())
107             << "Files under the path `" << firstSnapshot.path << "` are inaccessible";
108     ASSERT_RESULT_OK(collector.collect());
109 
110     auto actual = collector.deltaStats();
111 
112     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
113             << "First snapshot doesn't match.\nExpected:\n"
114             << toString(expected) << "\nActual:\n"
115             << toString(actual);
116     pidToTids = {
117             {1, {1, 453}}, {1000, {1000, 1400}},  // TID 1100 terminated and 1400 instantiated.
118     };
119 
120     perProcessStat = {
121             {1, "1 (init) S 0 0 0 0 0 0 0 0 920 0 0 0 0 0 0 0 2 0 0\n"},
122             {1000, "1000 (system_server) R 1 0 0 0 0 0 0 0 1550 0 0 0 0 0 0 0 2 0 13400\n"},
123     };
124 
125     perThreadStat = {
126             {1, "1 (init) S 0 0 0 0 0 0 0 0 600 0 0 0 0 0 0 0 2 0 0\n"},
127             {453, "453 (init) S 0 0 0 0 0 0 0 0 320 0 0 0 0 0 0 0 2 0 275\n"},
128             {1000, "1000 (system_server) R 1 0 0 0 0 0 0 0 600 0 0 0 0 0 0 0 2 0 13400\n"},
129             // TID 1100 hits +400 major page faults before terminating. This is counted against
130             // PID 1000's perProcessStat.
131             {1400, "1400 (system_server) S 1 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 8977476\n"},
132     };
133 
134     expected = {{0,
135                  {.totalMajorFaults = 700,
136                   .totalTasksCount = 2,
137                   .ioBlockedTasksCount = 0,
138                   .processStatsByPid = {{1, {"init", 0, 700, 2, 0}}}}},
139                 {10001234,
140                  {.totalMajorFaults = 950,
141                   .totalTasksCount = 2,
142                   .ioBlockedTasksCount = 0,
143                   .processStatsByPid = {{1000, {"system_server", 13'400, 950, 2, 0}}}}}};
144 
145     TemporaryDir secondSnapshot;
146     ASSERT_RESULT_OK(populateProcPidDir(secondSnapshot.path, pidToTids, perProcessStat,
147                                         perProcessStatus, perThreadStat));
148 
149     collector.mPath = secondSnapshot.path;
150 
151     ASSERT_TRUE(collector.enabled())
152             << "Files under the path `" << secondSnapshot.path << "` are inaccessible";
153     ASSERT_RESULT_OK(collector.collect());
154 
155     actual = collector.deltaStats();
156     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
157             << "Second snapshot doesn't match.\nExpected:\n"
158             << toString(expected) << "\nActual:\n"
159             << toString(actual);
160 }
161 
TEST(UidProcStatsCollectorTest,TestHandlesProcessTerminationBetweenScanningAndParsing)162 TEST(UidProcStatsCollectorTest, TestHandlesProcessTerminationBetweenScanningAndParsing) {
163     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
164             {1, {1}},
165             {100, {100}},          // Process terminates after scanning PID directory.
166             {1000, {1000}},        // Process terminates after reading stat file.
167             {2000, {2000}},        // Process terminates after scanning task directory.
168             {3000, {3000, 3300}},  // TID 3300 terminates after scanning task directory.
169     };
170 
171     std::unordered_map<pid_t, std::string> perProcessStat = {
172             {1, "1 (init) S 0 0 0 0 0 0 0 0 220 0 0 0 0 0 0 0 1 0 0\n"},
173             // Process 100 terminated.
174             {1000, "1000 (system_server) R 1 0 0 0 0 0 0 0 600 0 0 0 0 0 0 0 1 0 1000\n"},
175             {2000, "2000 (logd) R 1 0 0 0 0 0 0 0 1200 0 0 0 0 0 0 0 1 0 4567\n"},
176             {3000, "3000 (disk I/O) R 1 0 0 0 0 0 0 0 10300 0 0 0 0 0 0 0 2 0 67890\n"},
177     };
178 
179     std::unordered_map<pid_t, std::string> perProcessStatus = {
180             {1, pidStatusStr(1, 0)},
181             // Process 1000 terminated.
182             {2000, pidStatusStr(2000, 10001234)},
183             {3000, pidStatusStr(3000, 10001234)},
184     };
185 
186     std::unordered_map<pid_t, std::string> perThreadStat = {
187             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
188             // Process 2000 terminated.
189             {3000, "3000 (disk I/O) R 1 0 0 0 0 0 0 0 2400 0 0 0 0 0 0 0 2 0 67890\n"},
190             // TID 3300 terminated.
191     };
192 
193     std::unordered_map<uid_t, UidProcStats> expected =
194             {{0,
195               UidProcStats{.totalMajorFaults = 220,
196                            .totalTasksCount = 1,
197                            .ioBlockedTasksCount = 0,
198                            .processStatsByPid = {{1, {"init", 0, 220, 1, 0}}}}},
199              {10001234,
200               UidProcStats{.totalMajorFaults = 11500,
201                            .totalTasksCount = 2,
202                            .ioBlockedTasksCount = 0,
203                            .processStatsByPid = {{2000, {"logd", 4567, 1200, 1, 0}},
204                                                  {3000, {"disk I/O", 67890, 10'300, 1, 0}}}}}};
205 
206     TemporaryDir procDir;
207     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
208                                         perThreadStat));
209 
210     UidProcStatsCollector collector(procDir.path);
211 
212     ASSERT_TRUE(collector.enabled())
213             << "Files under the path `" << procDir.path << "` are inaccessible";
214     ASSERT_RESULT_OK(collector.collect());
215 
216     auto actual = collector.deltaStats();
217     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
218             << "Proc pid contents doesn't match.\nExpected:\n"
219             << toString(expected) << "\nActual:\n"
220             << toString(actual);
221 }
222 
TEST(UidProcStatsCollectorTest,TestHandlesPidTidReuse)223 TEST(UidProcStatsCollectorTest, TestHandlesPidTidReuse) {
224     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
225             {1, {1, 367, 453, 589}},
226             {1000, {1000}},
227             {2345, {2345}},
228     };
229 
230     std::unordered_map<pid_t, std::string> perProcessStat = {
231             {1, "1 (init) S 0 0 0 0 0 0 0 0 1200 0 0 0 0 0 0 0 4 0 0\n"},
232             {1000, "1000 (system_server) R 1 0 0 0 0 0 0 0 250 0 0 0 0 0 0 0 1 0 1000\n"},
233             {2345, "2345 (logd) R 1 0 0 0 0 0 0 0 54354 0 0 0 0 0 0 0 1 0 456\n"},
234     };
235 
236     std::unordered_map<pid_t, std::string> perProcessStatus = {
237             {1, pidStatusStr(1, 0)},
238             {1000, pidStatusStr(1000, 10001234)},
239             {2345, pidStatusStr(2345, 10001234)},
240     };
241 
242     std::unordered_map<pid_t, std::string> perThreadStat = {
243             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 4 0 0\n"},
244             {367, "367 (init) S 0 0 0 0 0 0 0 0 400 0 0 0 0 0 0 0 4 0 100\n"},
245             {453, "453 (init) S 0 0 0 0 0 0 0 0 100 0 0 0 0 0 0 0 4 0 275\n"},
246             {589, "589 (init) D 0 0 0 0 0 0 0 0 500 0 0 0 0 0 0 0 4 0 600\n"},
247             {1000, "1000 (system_server) R 1 0 0 0 0 0 0 0 250 0 0 0 0 0 0 0 1 0 1000\n"},
248             {2345, "2345 (logd) R 1 0 0 0 0 0 0 0 54354 0 0 0 0 0 0 0 1 0 456\n"},
249     };
250 
251     std::unordered_map<uid_t, UidProcStats> expected =
252             {{0,
253               UidProcStats{.totalMajorFaults = 1200,
254                            .totalTasksCount = 4,
255                            .ioBlockedTasksCount = 1,
256                            .processStatsByPid = {{1, {"init", 0, 1200, 4, 1}}}}},
257              {10001234,
258               UidProcStats{.totalMajorFaults = 54'604,
259                            .totalTasksCount = 2,
260                            .ioBlockedTasksCount = 0,
261                            .processStatsByPid = {{1000, {"system_server", 1000, 250, 1, 0}},
262                                                  {2345, {"logd", 456, 54'354, 1, 0}}}}}};
263 
264     TemporaryDir firstSnapshot;
265     ASSERT_RESULT_OK(populateProcPidDir(firstSnapshot.path, pidToTids, perProcessStat,
266                                         perProcessStatus, perThreadStat));
267 
268     UidProcStatsCollector collector(firstSnapshot.path);
269 
270     ASSERT_TRUE(collector.enabled())
271             << "Files under the path `" << firstSnapshot.path << "` are inaccessible";
272     ASSERT_RESULT_OK(collector.collect());
273 
274     auto actual = collector.deltaStats();
275 
276     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
277             << "First snapshot doesn't match.\nExpected:\n"
278             << toString(expected) << "\nActual:\n"
279             << toString(actual);
280 
281     pidToTids = {
282             {1, {1, 589}},       // TID 589 reused by the same process.
283             {367, {367, 2000}},  // TID 367 reused as a PID. PID 2000 reused as a TID.
284             // PID 1000 reused as a new PID. TID 453 reused by a different PID.
285             {1000, {1000, 453}},
286     };
287 
288     perProcessStat = {
289             {1, "1 (init) S 0 0 0 0 0 0 0 0 1800 0 0 0 0 0 0 0 2 0 0\n"},
290             {367, "367 (system_server) R 1 0 0 0 0 0 0 0 100 0 0 0 0 0 0 0 2 0 3450\n"},
291             {1000, "1000 (logd) R 1 0 0 0 0 0 0 0 2000 0 0 0 0 0 0 0 2 0 4650\n"},
292     };
293 
294     perProcessStatus = {
295             {1, pidStatusStr(1, 0)},
296             {367, pidStatusStr(367, 10001234)},
297             {1000, pidStatusStr(1000, 10001234)},
298     };
299 
300     perThreadStat = {
301             {1, "1 (init) S 0 0 0 0 0 0 0 0 500 0 0 0 0 0 0 0 2 0 0\n"},
302             {589, "589 (init) S 0 0 0 0 0 0 0 0 300 0 0 0 0 0 0 0 2 0 2345\n"},
303             {367, "367 (system_server) R 1 0 0 0 0 0 0 0 50 0 0 0 0 0 0 0 2 0 3450\n"},
304             {2000, "2000 (system_server) R 1 0 0 0 0 0 0 0 50 0 0 0 0 0 0 0 2 0 3670\n"},
305             {1000, "1000 (logd) R 1 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 4650\n"},
306             {453, "453 (logd) D 1 0 0 0 0 0 0 0 1800 0 0 0 0 0 0 0 2 0 4770\n"},
307     };
308 
309     expected = {{0,
310                  UidProcStats{.totalMajorFaults = 600,
311                               .totalTasksCount = 2,
312                               .ioBlockedTasksCount = 0,
313                               .processStatsByPid = {{1, {"init", 0, 600, 2, 0}}}}},
314                 {10001234,
315                  UidProcStats{.totalMajorFaults = 2100,
316                               .totalTasksCount = 4,
317                               .ioBlockedTasksCount = 1,
318                               .processStatsByPid = {{367, {"system_server", 3450, 100, 2, 0}},
319                                                     {1000, {"logd", 4650, 2000, 2, 1}}}}}};
320 
321     TemporaryDir secondSnapshot;
322     ASSERT_RESULT_OK(populateProcPidDir(secondSnapshot.path, pidToTids, perProcessStat,
323                                         perProcessStatus, perThreadStat));
324 
325     collector.mPath = secondSnapshot.path;
326 
327     ASSERT_TRUE(collector.enabled())
328             << "Files under the path `" << secondSnapshot.path << "` are inaccessible";
329     ASSERT_RESULT_OK(collector.collect());
330 
331     actual = collector.deltaStats();
332 
333     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
334             << "Second snapshot doesn't match.\nExpected:\n"
335             << toString(expected) << "\nActual:\n"
336             << toString(actual);
337 }
338 
TEST(UidProcStatsCollectorTest,TestErrorOnCorruptedProcessStatFile)339 TEST(UidProcStatsCollectorTest, TestErrorOnCorruptedProcessStatFile) {
340     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
341             {1, {1}},
342     };
343 
344     std::unordered_map<pid_t, std::string> perProcessStat = {
345             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 CORRUPTED DATA\n"},
346     };
347 
348     std::unordered_map<pid_t, std::string> perProcessStatus = {
349             {1, pidStatusStr(1, 0)},
350     };
351 
352     std::unordered_map<pid_t, std::string> perThreadStat = {
353             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
354     };
355 
356     TemporaryDir procDir;
357     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
358                                         perThreadStat));
359 
360     UidProcStatsCollector collector(procDir.path);
361 
362     ASSERT_TRUE(collector.enabled())
363             << "Files under the path `" << procDir.path << "` are inaccessible";
364     ASSERT_FALSE(collector.collect().ok()) << "No error returned for invalid process stat file";
365 }
366 
TEST(UidProcStatsCollectorTest,TestErrorOnCorruptedProcessStatusFile)367 TEST(UidProcStatsCollectorTest, TestErrorOnCorruptedProcessStatusFile) {
368     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
369             {1, {1}},
370     };
371 
372     std::unordered_map<pid_t, std::string> perProcessStat = {
373             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
374     };
375 
376     std::unordered_map<pid_t, std::string> perProcessStatus = {
377             {1, "Pid:\t1\nTgid:\t1\nCORRUPTED DATA\n"},
378     };
379 
380     std::unordered_map<pid_t, std::string> perThreadStat = {
381             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
382     };
383 
384     TemporaryDir procDir;
385     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
386                                         perThreadStat));
387 
388     UidProcStatsCollector collector(procDir.path);
389 
390     ASSERT_TRUE(collector.enabled())
391             << "Files under the path `" << procDir.path << "` are inaccessible";
392     ASSERT_FALSE(collector.collect().ok()) << "No error returned for invalid process status file";
393 }
394 
TEST(UidProcStatsCollectorTest,TestErrorOnCorruptedThreadStatFile)395 TEST(UidProcStatsCollectorTest, TestErrorOnCorruptedThreadStatFile) {
396     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
397             {1, {1, 234}},
398     };
399 
400     std::unordered_map<pid_t, std::string> perProcessStat = {
401             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 678\n"},
402     };
403 
404     std::unordered_map<pid_t, std::string> perProcessStatus = {
405             {1, pidStatusStr(1, 0)},
406     };
407 
408     std::unordered_map<pid_t, std::string> perThreadStat = {
409             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 678\n"},
410             {234, "234 (init) D 0 0 0 0 0 0 0 0 200 0 0 0 CORRUPTED DATA\n"},
411     };
412 
413     TemporaryDir procDir;
414     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
415                                         perThreadStat));
416 
417     UidProcStatsCollector collector(procDir.path);
418 
419     ASSERT_TRUE(collector.enabled())
420             << "Files under the path `" << procDir.path << "` are inaccessible";
421     ASSERT_FALSE(collector.collect().ok()) << "No error returned for invalid thread stat file";
422 }
423 
TEST(UidProcStatsCollectorTest,TestHandlesSpaceInCommName)424 TEST(UidProcStatsCollectorTest, TestHandlesSpaceInCommName) {
425     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
426             {1, {1}},
427     };
428 
429     std::unordered_map<pid_t, std::string> perProcessStat = {
430             {1, "1 (random process name with space) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
431     };
432 
433     std::unordered_map<pid_t, std::string> perProcessStatus = {
434             {1, pidStatusStr(1, 0)},
435     };
436 
437     std::unordered_map<pid_t, std::string> perThreadStat = {
438             {1, "1 (random process name with space) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
439     };
440 
441     std::unordered_map<uid_t, UidProcStats> expected = {
442             {0,
443              UidProcStats{.totalMajorFaults = 200,
444                           .totalTasksCount = 1,
445                           .ioBlockedTasksCount = 0,
446                           .processStatsByPid = {
447                                   {1, {"random process name with space", 0, 200, 1, 0}}}}}};
448 
449     TemporaryDir procDir;
450     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
451                                         perThreadStat));
452 
453     UidProcStatsCollector collector(procDir.path);
454 
455     ASSERT_TRUE(collector.enabled())
456             << "Files under the path `" << procDir.path << "` are inaccessible";
457     ASSERT_RESULT_OK(collector.collect());
458 
459     auto actual = collector.deltaStats();
460 
461     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
462             << "Proc pid contents doesn't match.\nExpected:\n"
463             << toString(expected) << "\nActual:\n"
464             << toString(actual);
465 }
466 
TEST(UidProcStatsCollectorTest,TestUidProcStatsCollectorContentsFromDevice)467 TEST(UidProcStatsCollectorTest, TestUidProcStatsCollectorContentsFromDevice) {
468     UidProcStatsCollector collector;
469     ASSERT_TRUE(collector.enabled()) << "/proc/[pid]/.* files are inaccessible";
470     ASSERT_RESULT_OK(collector.collect());
471 
472     const auto& processStats = collector.deltaStats();
473 
474     // The below check should pass because there should be at least one process.
475     EXPECT_GT(processStats.size(), 0);
476 }
477 
478 }  // namespace watchdog
479 }  // namespace automotive
480 }  // namespace android
481