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