• 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<pid_t, std::string> perThreadTimeInState = {
93             {1, "cpu0\n300000 5\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
94             {453, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 5\ncpu7\n2000000 0"},
95             {1000,
96              "cpu0\n300000 0\n1700000 1000\ncpu4\n710000 1000\n1800000 3000\ncpu7\n2000000 6000"},
97             {1100, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 1000\ncpu7\n2000000 0"},
98     };
99 
100     std::unordered_map<uid_t, UidProcStats> expected =
101             {{0,
102               UidProcStats{.cpuTimeMillis = ticksToMillis(10),
103                            .cpuCycles = 105000000,
104                            .totalMajorFaults = 220,
105                            .totalTasksCount = 2,
106                            .ioBlockedTasksCount = 1,
107                            .processStatsByPid = {{1,
108                                                   {"init",
109                                                    ticksToMillis(19),
110                                                    ticksToMillis(10),
111                                                    105000000,
112                                                    220,
113                                                    2,
114                                                    1,
115                                                    {{1, 15000000}, {453, 90000000}}}}}}},
116              {10001234,
117               UidProcStats{.cpuTimeMillis = ticksToMillis(12'000),
118                            .cpuCycles = 216100000000,
119                            .totalMajorFaults = 600,
120                            .totalTasksCount = 2,
121                            .ioBlockedTasksCount = 2,
122                            .processStatsByPid = {
123                                    {1000,
124                                     {"system_server",
125                                      ticksToMillis(13'400),
126                                      ticksToMillis(12'000),
127                                      216100000000,
128                                      600,
129                                      2,
130                                      2,
131                                      {{1000, 198100000000}, {1100, 18000000000}}}}}}}};
132 
133     TemporaryDir firstSnapshot;
134     ASSERT_RESULT_OK(populateProcPidDir(firstSnapshot.path, pidToTids, perProcessStat,
135                                         perProcessStatus, perThreadStat, perThreadTimeInState));
136 
137     UidProcStatsCollector collector(firstSnapshot.path);
138     collector.init();
139 
140     ASSERT_TRUE(collector.enabled())
141             << "Files under the path `" << firstSnapshot.path << "` are inaccessible";
142     ASSERT_RESULT_OK(collector.collect());
143 
144     auto actual = collector.deltaStats();
145 
146     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
147             << "First snapshot doesn't match.\nExpected:\n"
148             << toString(expected) << "\nActual:\n"
149             << toString(actual);
150 
151     pidToTids = {
152             {1, {1, 453}}, {1000, {1000, 1400}},  // TID 1100 terminated and 1400 instantiated.
153     };
154 
155     perProcessStat = {
156             {1, "1 (init) S 0 0 0 0 0 0 0 0 920 0 10 10 0 0 0 0 2 0 19\n"},
157             {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"},
158     };
159 
160     perThreadStat = {
161             {1, "1 (init) S 0 0 0 0 0 0 0 0 600 0 5 5 0 0 0 0 2 0 19\n"},
162             {453, "453 (init) S 0 0 0 0 0 0 0 0 320 0 5 5 0 0 0 0 2 0 275\n"},
163             {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"},
164             // TID 1100 hits +400 major page faults before terminating. This is counted against
165             // PID 1000's perProcessStat.
166             {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"},
167     };
168 
169     perThreadTimeInState = {
170             {1, "cpu0\n300000 5\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 10"},
171             {453, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 5\ncpu7\n2000000 0"},
172             {1000,
173              "cpu0\n300000 0\n1700000 1000\ncpu4\n710000 1000\n1800000 3000\ncpu7\n2000000 6000"},
174             {1400, "cpu0\n300000 6000\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
175     };
176 
177     expected = {{0,
178                  {.cpuTimeMillis = ticksToMillis(10),
179                   .cpuCycles = 200'000'000,
180                   .totalMajorFaults = 700,
181                   .totalTasksCount = 2,
182                   .ioBlockedTasksCount = 0,
183                   .processStatsByPid = {{1,
184                                          {"init",
185                                           ticksToMillis(19),
186                                           ticksToMillis(10),
187                                           200'000'000,
188                                           700,
189                                           2,
190                                           0,
191                                           {{1, 200'000'000}, {453, 0}}}}}}},
192                 {10001234,
193                  {.cpuTimeMillis = ticksToMillis(6'000),
194                   .cpuCycles = 18'000'000'000,
195                   .totalMajorFaults = 950,
196                   .totalTasksCount = 2,
197                   .ioBlockedTasksCount = 0,
198                   .processStatsByPid = {{1000,
199                                          {"system_server",
200                                           ticksToMillis(13'400),
201                                           ticksToMillis(6'000),
202                                           18'000'000'000,
203                                           950,
204                                           2,
205                                           0,
206                                           {{1000, 0}, {1400, 18'000'000'000}}}}}}}};
207 
208     TemporaryDir secondSnapshot;
209     ASSERT_RESULT_OK(populateProcPidDir(secondSnapshot.path, pidToTids, perProcessStat,
210                                         perProcessStatus, perThreadStat, perThreadTimeInState));
211 
212     collector.mPath = secondSnapshot.path;
213 
214     ASSERT_TRUE(collector.enabled())
215             << "Files under the path `" << secondSnapshot.path << "` are inaccessible";
216     ASSERT_RESULT_OK(collector.collect());
217 
218     actual = collector.deltaStats();
219     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
220             << "Second snapshot doesn't match.\nExpected:\n"
221             << toString(expected) << "\nActual:\n"
222             << toString(actual);
223 }
224 
TEST(UidProcStatsCollectorTest,TestHandlesProcessTerminationBetweenScanningAndParsing)225 TEST(UidProcStatsCollectorTest, TestHandlesProcessTerminationBetweenScanningAndParsing) {
226     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
227             {1, {1}},
228             {100, {100}},          // Process terminates after scanning PID directory.
229             {1000, {1000}},        // Process terminates after reading stat file.
230             {2000, {2000}},        // Process terminates after scanning task directory.
231             {3000, {3000, 3300}},  // TID 3300 terminates after scanning task directory.
232     };
233 
234     std::unordered_map<pid_t, std::string> perProcessStat = {
235             {1, "1 (init) S 0 0 0 0 0 0 0 0 220 0 10 10 0 0 0 0 1 0 19\n"},
236             // Process 100 terminated.
237             {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"},
238             {2000, "2000 (logd) R 1 0 0 0 0 0 0 0 1200 0 30 30 0 0 0 0 1 0 4567\n"},
239             {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"},
240     };
241 
242     std::unordered_map<pid_t, std::string> perProcessStatus = {
243             {1, pidStatusStr(1, 0)},
244             // Process 1000 terminated.
245             {2000, pidStatusStr(2000, 10001234)},
246             {3000, pidStatusStr(3000, 10001234)},
247     };
248 
249     std::unordered_map<pid_t, std::string> perThreadStat = {
250             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
251             // Process 2000 terminated.
252             {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"},
253             // TID 3300 terminated.
254     };
255 
256     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
257             {1, "cpu0\n300000 10\n1700000 10\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
258             // Process 3000 terminated.
259     };
260 
261     std::unordered_map<uid_t, UidProcStats> expected =
262             {{0,
263               UidProcStats{.cpuTimeMillis = ticksToMillis(20),
264                            .cpuCycles = 200'000'000,
265                            .totalMajorFaults = 220,
266                            .totalTasksCount = 1,
267                            .ioBlockedTasksCount = 0,
268                            .processStatsByPid = {{1,
269                                                   {"init",
270                                                    ticksToMillis(19),
271                                                    ticksToMillis(20),
272                                                    200'000'000,
273                                                    220,
274                                                    1,
275                                                    0,
276                                                    {{1, 200'000'000}}}}}}},
277              {10001234,
278               UidProcStats{.cpuTimeMillis = ticksToMillis(140),
279                            .cpuCycles = 0,
280                            .totalMajorFaults = 11500,
281                            .totalTasksCount = 2,
282                            .ioBlockedTasksCount = 0,
283                            .processStatsByPid = {{2000,
284                                                   {"logd",
285                                                    ticksToMillis(4567),
286                                                    ticksToMillis(60),
287                                                    0,
288                                                    1200,
289                                                    1,
290                                                    0,
291                                                    {}}},
292                                                  {3000,
293                                                   {"disk I/O",
294                                                    ticksToMillis(67890),
295                                                    ticksToMillis(80),
296                                                    0,
297                                                    10'300,
298                                                    1,
299                                                    0,
300                                                    {}}}}}}};
301 
302     TemporaryDir procDir;
303     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
304                                         perThreadStat, perThreadTimeInState));
305 
306     UidProcStatsCollector collector(procDir.path);
307     collector.init();
308 
309     ASSERT_TRUE(collector.enabled())
310             << "Files under the path `" << procDir.path << "` are inaccessible";
311     ASSERT_RESULT_OK(collector.collect());
312 
313     auto actual = collector.deltaStats();
314     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
315             << "Proc pid contents doesn't match.\nExpected:\n"
316             << toString(expected) << "\nActual:\n"
317             << toString(actual);
318 }
319 
TEST(UidProcStatsCollectorTest,TestHandlesPidTidReuse)320 TEST(UidProcStatsCollectorTest, TestHandlesPidTidReuse) {
321     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
322             {1, {1, 367, 453, 589}},
323             {1000, {1000}},
324             {2345, {2345}},
325     };
326 
327     std::unordered_map<pid_t, std::string> perProcessStat = {
328             {1, "1 (init) S 0 0 0 0 0 0 0 0 1200 0 40 40 0 0 0 0 4 0 19\n"},
329             {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"},
330             {2345, "2345 (logd) R 1 0 0 0 0 0 0 0 54354 0 10 10 0 0 0 0 1 0 456\n"},
331     };
332 
333     std::unordered_map<pid_t, std::string> perProcessStatus = {
334             {1, pidStatusStr(1, 0)},
335             {1000, pidStatusStr(1000, 10001234)},
336             {2345, pidStatusStr(2345, 10001234)},
337     };
338 
339     std::unordered_map<pid_t, std::string> perThreadStat = {
340             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 4 0 19\n"},
341             {367, "367 (init) S 0 0 0 0 0 0 0 0 400 0 10 10 0 0 0 0 4 0 100\n"},
342             {453, "453 (init) S 0 0 0 0 0 0 0 0 100 0 10 10 0 0 0 0 4 0 275\n"},
343             {589, "589 (init) D 0 0 0 0 0 0 0 0 500 0 10 10 0 0 0 0 4 0 600\n"},
344             {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"},
345             {2345, "2345 (logd) R 1 0 0 0 0 0 0 0 54354 0 10 10 0 0 0 0 1 0 456\n"},
346     };
347 
348     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
349             {1, "cpu0\n300000 20\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
350             {367, "cpu0\n300000 0\n1700000 20\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
351             {453, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 20\ncpu7\n2000000 0"},
352             {589, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 20"},
353             {1000, "cpu0\n300000 20\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
354             {2345, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 20\ncpu7\n2000000 0"},
355     };
356 
357     std::unordered_map<uid_t, UidProcStats> expected =
358             {{0,
359               UidProcStats{.cpuTimeMillis = ticksToMillis(80),
360                            .cpuCycles = 1'160'000'000,
361                            .totalMajorFaults = 1200,
362                            .totalTasksCount = 4,
363                            .ioBlockedTasksCount = 1,
364                            .processStatsByPid = {{1,
365                                                   {"init",
366                                                    ticksToMillis(19),
367                                                    ticksToMillis(80),
368                                                    1'160'000'000,
369                                                    1200,
370                                                    4,
371                                                    1,
372                                                    {{1, 60'000'000},
373                                                     {367, 340'000'000},
374                                                     {453, 360'000'000},
375                                                     {589, 400'000'000}}}}}}},
376              {10001234,
377               UidProcStats{.cpuTimeMillis = ticksToMillis(40),
378                            .cpuCycles = 420'000'000,
379                            .totalMajorFaults = 54'604,
380                            .totalTasksCount = 2,
381                            .ioBlockedTasksCount = 0,
382                            .processStatsByPid = {{1000,
383                                                   {"system_server",
384                                                    ticksToMillis(1000),
385                                                    ticksToMillis(20),
386                                                    60'000'000,
387                                                    250,
388                                                    1,
389                                                    0,
390                                                    {{1000, 60'000'000}}}},
391                                                  {2345,
392                                                   {"logd",
393                                                    ticksToMillis(456),
394                                                    ticksToMillis(20),
395                                                    360'000'000,
396                                                    54'354,
397                                                    1,
398                                                    0,
399                                                    {{2345, 360'000'000}}}}}}}};
400 
401     TemporaryDir firstSnapshot;
402     ASSERT_RESULT_OK(populateProcPidDir(firstSnapshot.path, pidToTids, perProcessStat,
403                                         perProcessStatus, perThreadStat, perThreadTimeInState));
404 
405     UidProcStatsCollector collector(firstSnapshot.path);
406     collector.init();
407 
408     ASSERT_TRUE(collector.enabled())
409             << "Files under the path `" << firstSnapshot.path << "` are inaccessible";
410     ASSERT_RESULT_OK(collector.collect());
411 
412     auto actual = collector.deltaStats();
413 
414     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
415             << "First snapshot doesn't match.\nExpected:\n"
416             << toString(expected) << "\nActual:\n"
417             << toString(actual);
418 
419     pidToTids = {
420             {1, {1, 589}},       // TID 589 reused by the same process.
421             {367, {367, 2000}},  // TID 367 reused as a PID. PID 2000 reused as a TID.
422             // PID 1000 reused as a new PID. TID 453 reused by a different PID.
423             {1000, {1000, 453}},
424     };
425 
426     perProcessStat = {
427             {1, "1 (init) S 0 0 0 0 0 0 0 0 1800 0 60 60 0 0 0 0 2 0 19\n"},
428             {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"},
429             {1000, "1000 (logd) R 1 0 0 0 0 0 0 0 2000 0 20 20 0 0 0 0 2 0 4650\n"},
430     };
431 
432     perProcessStatus = {
433             {1, pidStatusStr(1, 0)},
434             {367, pidStatusStr(367, 10001234)},
435             {1000, pidStatusStr(1000, 10001234)},
436     };
437 
438     perThreadStat = {
439             {1, "1 (init) S 0 0 0 0 0 0 0 0 500 0 20 20 0 0 0 0 2 0 19\n"},
440             {589, "589 (init) S 0 0 0 0 0 0 0 0 300 0 10 10 0 0 0 0 2 0 2345\n"},
441             {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"},
442             {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"},
443             {1000, "1000 (logd) R 1 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 2 0 4650\n"},
444             {453, "453 (logd) D 1 0 0 0 0 0 0 0 1800 0 10 10 0 0 0 0 2 0 4770\n"},
445     };
446 
447     perThreadTimeInState = {
448             {1, "cpu0\n300000 20\n1700000 20\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
449             {589, "cpu0\n300000 20\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 20"},
450             {367, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 30\n1800000 0\ncpu7\n2000000 0"},
451             {2000, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 30"},
452             {1000, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 20\ncpu7\n2000000 0"},
453             {453, "cpu0\n300000 20\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
454     };
455 
456     expected = {{0,
457                  UidProcStats{.cpuTimeMillis = ticksToMillis(40),
458                               .cpuCycles = 400'000'000,
459                               .totalMajorFaults = 600,
460                               .totalTasksCount = 2,
461                               .ioBlockedTasksCount = 0,
462                               .processStatsByPid = {{1,
463                                                      {"init",
464                                                       ticksToMillis(19),
465                                                       ticksToMillis(40),
466                                                       400'000'000,
467                                                       600,
468                                                       2,
469                                                       0,
470                                                       {{1, 340'000'000}, {589, 60'000'000}}}}}}},
471                 {10001234,
472                  UidProcStats{.cpuTimeMillis = ticksToMillis(100),
473                               .cpuCycles = 1'233'000'000,
474                               .totalMajorFaults = 2100,
475                               .totalTasksCount = 4,
476                               .ioBlockedTasksCount = 1,
477                               .processStatsByPid = {{367,
478                                                      {"system_server",
479                                                       ticksToMillis(3450),
480                                                       ticksToMillis(60),
481                                                       813'000'000,
482                                                       100,
483                                                       2,
484                                                       0,
485                                                       {{367, 213'000'000}, {2000, 600'000'000}}}},
486                                                     {1000,
487                                                      {"logd",
488                                                       ticksToMillis(4650),
489                                                       ticksToMillis(40),
490                                                       420'000'000,
491                                                       2000,
492                                                       2,
493                                                       1,
494                                                       {{1000, 360'000'000},
495                                                        {453, 60'000'000}}}}}}}};
496 
497     TemporaryDir secondSnapshot;
498     ASSERT_RESULT_OK(populateProcPidDir(secondSnapshot.path, pidToTids, perProcessStat,
499                                         perProcessStatus, perThreadStat, perThreadTimeInState));
500 
501     collector.mPath = secondSnapshot.path;
502 
503     ASSERT_TRUE(collector.enabled())
504             << "Files under the path `" << secondSnapshot.path << "` are inaccessible";
505     ASSERT_RESULT_OK(collector.collect());
506 
507     actual = collector.deltaStats();
508 
509     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
510             << "Second snapshot doesn't match.\nExpected:\n"
511             << toString(expected) << "\nActual:\n"
512             << toString(actual);
513 }
514 
TEST(UidProcStatsCollectorTest,TestErrorOnCorruptedProcessStatFile)515 TEST(UidProcStatsCollectorTest, TestErrorOnCorruptedProcessStatFile) {
516     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
517             {1, {1}},
518     };
519 
520     std::unordered_map<pid_t, std::string> perProcessStat = {
521             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 CORRUPTED DATA\n"},
522     };
523 
524     std::unordered_map<pid_t, std::string> perProcessStatus = {
525             {1, pidStatusStr(1, 0)},
526     };
527 
528     std::unordered_map<pid_t, std::string> perThreadStat = {
529             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
530     };
531 
532     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
533             {1, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
534     };
535 
536     TemporaryDir procDir;
537     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
538                                         perThreadStat, perThreadTimeInState));
539 
540     UidProcStatsCollector collector(procDir.path);
541     collector.init();
542 
543     ASSERT_TRUE(collector.enabled())
544             << "Files under the path `" << procDir.path << "` are inaccessible";
545     ASSERT_FALSE(collector.collect().ok()) << "No error returned for invalid process stat file";
546 }
547 
TEST(UidProcStatsCollectorTest,TestErrorOnCorruptedProcessStatusFile)548 TEST(UidProcStatsCollectorTest, TestErrorOnCorruptedProcessStatusFile) {
549     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
550             {1, {1}},
551     };
552 
553     std::unordered_map<pid_t, std::string> perProcessStat = {
554             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
555     };
556 
557     std::unordered_map<pid_t, std::string> perProcessStatus = {
558             {1, "Pid:\t1\nTgid:\t1\nCORRUPTED DATA\n"},
559     };
560 
561     std::unordered_map<pid_t, std::string> perThreadStat = {
562             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
563     };
564 
565     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
566             {1, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
567     };
568 
569     TemporaryDir procDir;
570     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
571                                         perThreadStat, perThreadTimeInState));
572 
573     UidProcStatsCollector collector(procDir.path);
574     collector.init();
575 
576     ASSERT_TRUE(collector.enabled())
577             << "Files under the path `" << procDir.path << "` are inaccessible";
578     ASSERT_FALSE(collector.collect().ok()) << "No error returned for invalid process status file";
579 }
580 
TEST(UidProcStatsCollectorTest,TestErrorOnProcessStatusFileWithNoUid)581 TEST(UidProcStatsCollectorTest, TestErrorOnProcessStatusFileWithNoUid) {
582     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
583             {1, {1}},
584     };
585 
586     std::unordered_map<pid_t, std::string> perProcessStat = {
587             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
588     };
589 
590     std::unordered_map<pid_t, std::string> perProcessStatus = {
591             {1, "Pid:\t1\nTgid:\t1\n"},
592     };
593 
594     std::unordered_map<pid_t, std::string> perThreadStat = {
595             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
596     };
597 
598     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
599             {1, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
600     };
601 
602     TemporaryDir procDir;
603     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
604                                         perThreadStat, perThreadTimeInState));
605 
606     UidProcStatsCollector collector(procDir.path);
607     collector.init();
608 
609     ASSERT_TRUE(collector.enabled())
610             << "Files under the path `" << procDir.path << "` are inaccessible";
611     ASSERT_FALSE(collector.collect().ok())
612             << "No error returned for process status file without uid";
613 }
614 
TEST(UidProcStatsCollectorTest,TestErrorOnProcessStatusFileWithNoTgid)615 TEST(UidProcStatsCollectorTest, TestErrorOnProcessStatusFileWithNoTgid) {
616     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
617             {1, {1}},
618     };
619 
620     std::unordered_map<pid_t, std::string> perProcessStat = {
621             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
622     };
623 
624     std::unordered_map<pid_t, std::string> perProcessStatus = {
625             {1, "Pid:\t1\nUid:\t1\n"},
626     };
627 
628     std::unordered_map<pid_t, std::string> perThreadStat = {
629             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
630     };
631 
632     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
633             {1, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
634     };
635 
636     TemporaryDir procDir;
637     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
638                                         perThreadStat, perThreadTimeInState));
639 
640     UidProcStatsCollector collector(procDir.path);
641     collector.init();
642 
643     ASSERT_TRUE(collector.enabled())
644             << "Files under the path `" << procDir.path << "` are inaccessible";
645     ASSERT_FALSE(collector.collect().ok())
646             << "No error returned for process status file without tgid";
647 }
648 
TEST(UidProcStatsCollectorTest,TestErrorOnCorruptedThreadStatFile)649 TEST(UidProcStatsCollectorTest, TestErrorOnCorruptedThreadStatFile) {
650     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
651             {1, {1, 234}},
652     };
653 
654     std::unordered_map<pid_t, std::string> perProcessStat = {
655             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 678\n"},
656     };
657 
658     std::unordered_map<pid_t, std::string> perProcessStatus = {
659             {1, pidStatusStr(1, 0)},
660     };
661 
662     std::unordered_map<pid_t, std::string> perThreadStat = {
663             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 678\n"},
664             {234, "234 (init) D 0 0 0 0 0 0 0 0 200 0 0 0 CORRUPTED DATA\n"},
665     };
666 
667     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
668             {1, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
669     };
670 
671     TemporaryDir procDir;
672     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
673                                         perThreadStat, perThreadTimeInState));
674 
675     UidProcStatsCollector collector(procDir.path);
676     collector.init();
677 
678     ASSERT_TRUE(collector.enabled())
679             << "Files under the path `" << procDir.path << "` are inaccessible";
680     ASSERT_FALSE(collector.collect().ok()) << "No error returned for invalid thread stat file";
681 }
682 
TEST(UidProcStatsCollectorTest,TestErrorOnCorruptedThreadTimeInStateFile)683 TEST(UidProcStatsCollectorTest, TestErrorOnCorruptedThreadTimeInStateFile) {
684     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
685             {1, {1, 234}},
686     };
687 
688     std::unordered_map<pid_t, std::string> perProcessStat = {
689             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 678\n"},
690     };
691 
692     std::unordered_map<pid_t, std::string> perProcessStatus = {
693             {1, pidStatusStr(1, 0)},
694     };
695 
696     std::unordered_map<pid_t, std::string> perThreadStat = {
697             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 678\n"},
698             {234, "234 (init) D 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 500\n"},
699     };
700 
701     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
702             {1, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 0\n"},
703             {234, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 CORRUPTED\n DATA"},
704     };
705 
706     TemporaryDir procDir;
707     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
708                                         perThreadStat, perThreadTimeInState));
709 
710     UidProcStatsCollector collector(procDir.path);
711     collector.init();
712 
713     ASSERT_TRUE(collector.enabled())
714             << "Files under the path `" << procDir.path << "` are inaccessible";
715     ASSERT_FALSE(collector.collect().ok()) << "No error returned for invalid thread stat file";
716 }
717 
TEST(UidProcStatsCollectorTest,TestHandlesSpaceInCommName)718 TEST(UidProcStatsCollectorTest, TestHandlesSpaceInCommName) {
719     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
720             {1, {1}},
721     };
722 
723     std::unordered_map<pid_t, std::string> perProcessStat = {
724             {1,
725              "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"},
726     };
727 
728     std::unordered_map<pid_t, std::string> perProcessStatus = {
729             {1, pidStatusStr(1, 0)},
730     };
731 
732     std::unordered_map<pid_t, std::string> perThreadStat = {
733             {1,
734              "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"},
735     };
736 
737     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
738             {1, "cpu0\n300000 0\n1700000 20\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
739     };
740 
741     std::unordered_map<uid_t, UidProcStats> expected = {
742             {0,
743              UidProcStats{.cpuTimeMillis = ticksToMillis(20),
744                           .cpuCycles = 340'000'000,
745                           .totalMajorFaults = 200,
746                           .totalTasksCount = 1,
747                           .ioBlockedTasksCount = 0,
748                           .processStatsByPid = {{1,
749                                                  {"random process name with space",
750                                                   ticksToMillis(19),
751                                                   ticksToMillis(20),
752                                                   340'000'000,
753                                                   200,
754                                                   1,
755                                                   0,
756                                                   {{1, 340'000'000}}}}}}}};
757 
758     TemporaryDir procDir;
759     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
760                                         perThreadStat, perThreadTimeInState));
761 
762     UidProcStatsCollector collector(procDir.path);
763     collector.init();
764 
765     ASSERT_TRUE(collector.enabled())
766             << "Files under the path `" << procDir.path << "` are inaccessible";
767     ASSERT_RESULT_OK(collector.collect());
768 
769     auto actual = collector.deltaStats();
770 
771     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
772             << "Proc pid contents doesn't match.\nExpected:\n"
773             << toString(expected) << "\nActual:\n"
774             << toString(actual);
775 }
776 
TEST(UidProcStatsCollectorTest,TestHandlesTimeInStateFileDisabledWithNoFile)777 TEST(UidProcStatsCollectorTest, TestHandlesTimeInStateFileDisabledWithNoFile) {
778     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
779             {1, {1}},
780     };
781 
782     std::unordered_map<pid_t, std::string> perProcessStat = {
783             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
784     };
785 
786     std::unordered_map<pid_t, std::string> perProcessStatus = {
787             {1, pidStatusStr(1, 0)},
788     };
789 
790     std::unordered_map<pid_t, std::string> perThreadStat = {
791             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
792     };
793 
794     // No time_in_state file present in procfs
795 
796     std::unordered_map<uid_t, UidProcStats> expected = {
797             {0,
798              UidProcStats{.cpuTimeMillis = ticksToMillis(20),
799                           .cpuCycles = 0,
800                           .totalMajorFaults = 200,
801                           .totalTasksCount = 1,
802                           .ioBlockedTasksCount = 0,
803                           .processStatsByPid = {{1,
804                                                  {"init",
805                                                   ticksToMillis(19),
806                                                   ticksToMillis(20),
807                                                   0,
808                                                   200,
809                                                   1,
810                                                   0,
811                                                   {}}}}}}};
812 
813     TemporaryDir procDir;
814     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
815                                         perThreadStat, {}));
816 
817     UidProcStatsCollector collector(procDir.path);
818     collector.init();
819 
820     ASSERT_TRUE(collector.enabled())
821             << "Files under the path `" << procDir.path << "` are inaccessible";
822     ASSERT_RESULT_OK(collector.collect());
823 
824     auto actual = collector.deltaStats();
825 
826     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
827             << "Proc pid contents doesn't match.\nExpected:\n"
828             << toString(expected) << "\nActual:\n"
829             << toString(actual);
830 }
831 
TEST(UidProcStatsCollectorTest,TestHandlesTimeInStateFileDisabledWithEmptyFile)832 TEST(UidProcStatsCollectorTest, TestHandlesTimeInStateFileDisabledWithEmptyFile) {
833     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
834             {1, {1}},
835     };
836 
837     std::unordered_map<pid_t, std::string> perProcessStat = {
838             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
839     };
840 
841     std::unordered_map<pid_t, std::string> perProcessStatus = {
842             {1, pidStatusStr(1, 0)},
843     };
844 
845     std::unordered_map<pid_t, std::string> perThreadStat = {
846             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
847     };
848 
849     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
850             {1, ""},
851     };
852 
853     std::unordered_map<uid_t, UidProcStats> expected = {
854             {0,
855              UidProcStats{.cpuTimeMillis = ticksToMillis(20),
856                           .cpuCycles = 0,
857                           .totalMajorFaults = 200,
858                           .totalTasksCount = 1,
859                           .ioBlockedTasksCount = 0,
860                           .processStatsByPid = {{1,
861                                                  {"init",
862                                                   ticksToMillis(19),
863                                                   ticksToMillis(20),
864                                                   0,
865                                                   200,
866                                                   1,
867                                                   0,
868                                                   {}}}}}}};
869 
870     TemporaryDir procDir;
871     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
872                                         perThreadStat, {}));
873 
874     UidProcStatsCollector collector(procDir.path);
875     collector.init();
876 
877     ASSERT_TRUE(collector.enabled())
878             << "Files under the path `" << procDir.path << "` are inaccessible";
879     ASSERT_RESULT_OK(collector.collect());
880 
881     auto actual = collector.deltaStats();
882 
883     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
884             << "Proc pid contents doesn't match.\nExpected:\n"
885             << toString(expected) << "\nActual:\n"
886             << toString(actual);
887 }
888 
TEST(UidProcStatsCollectorTest,TestHandlesTimeInStateFileDisabledWithZeroCpuCycles)889 TEST(UidProcStatsCollectorTest, TestHandlesTimeInStateFileDisabledWithZeroCpuCycles) {
890     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
891             {1, {1}},
892     };
893 
894     std::unordered_map<pid_t, std::string> perProcessStat = {
895             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
896     };
897 
898     std::unordered_map<pid_t, std::string> perProcessStatus = {
899             {1, pidStatusStr(1, 0)},
900     };
901 
902     std::unordered_map<pid_t, std::string> perThreadStat = {
903             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
904     };
905 
906     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
907             {1, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
908     };
909 
910     std::unordered_map<uid_t, UidProcStats> expected = {
911             {0,
912              UidProcStats{.cpuTimeMillis = ticksToMillis(20),
913                           .cpuCycles = 0,
914                           .totalMajorFaults = 200,
915                           .totalTasksCount = 1,
916                           .ioBlockedTasksCount = 0,
917                           .processStatsByPid = {{1,
918                                                  {"init",
919                                                   ticksToMillis(19),
920                                                   ticksToMillis(20),
921                                                   0,
922                                                   200,
923                                                   1,
924                                                   0,
925                                                   {}}}}}}};
926 
927     TemporaryDir procDir;
928     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
929                                         perThreadStat, {}));
930 
931     UidProcStatsCollector collector(procDir.path);
932     collector.init();
933 
934     ASSERT_TRUE(collector.enabled())
935             << "Files under the path `" << procDir.path << "` are inaccessible";
936     ASSERT_RESULT_OK(collector.collect());
937 
938     auto actual = collector.deltaStats();
939 
940     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
941             << "Proc pid contents doesn't match.\nExpected:\n"
942             << toString(expected) << "\nActual:\n"
943             << toString(actual);
944 }
945 
TEST(UidProcStatsCollectorTest,TestHandlesNoTimeInStateFileDuringCollection)946 TEST(UidProcStatsCollectorTest, TestHandlesNoTimeInStateFileDuringCollection) {
947     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
948             {1, {1, 234}},
949     };
950 
951     std::unordered_map<pid_t, std::string> perProcessStat = {
952             {1, "1 (init) S 0 0 0 0 0 0 0 0 210 0 15 15 0 0 0 0 2 0 19\n"},
953     };
954 
955     std::unordered_map<pid_t, std::string> perProcessStatus = {
956             {1, pidStatusStr(1, 0)},
957     };
958 
959     std::unordered_map<pid_t, std::string> perThreadStat = {
960             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 2 0 19\n"},
961             {234, "1 (init) S 0 0 0 0 0 0 0 0 10 0 5 5 0 0 0 0 2 0 19\n"},
962     };
963 
964     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
965             {1, "cpu0\n300000 0\n1700000 20\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
966             // No time_in_state file present for TID 234
967     };
968 
969     std::unordered_map<uid_t, UidProcStats> expected = {
970             {0,
971              UidProcStats{.cpuTimeMillis = ticksToMillis(30),
972                           .cpuCycles = 340'000'000,
973                           .totalMajorFaults = 210,
974                           .totalTasksCount = 2,
975                           .ioBlockedTasksCount = 0,
976                           .processStatsByPid = {{1,
977                                                  {"init",
978                                                   ticksToMillis(19),
979                                                   ticksToMillis(30),
980                                                   340'000'000,
981                                                   210,
982                                                   2,
983                                                   0,
984                                                   {{1, 340'000'000}}}}}}}};
985 
986     TemporaryDir procDir;
987     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
988                                         perThreadStat, perThreadTimeInState));
989 
990     UidProcStatsCollector collector(procDir.path);
991     collector.init();
992 
993     ASSERT_TRUE(collector.enabled())
994             << "Files under the path `" << procDir.path << "` are inaccessible";
995     ASSERT_RESULT_OK(collector.collect());
996 
997     auto actual = collector.deltaStats();
998 
999     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
1000             << "Proc pid contents doesn't match.\nExpected:\n"
1001             << toString(expected) << "\nActual:\n"
1002             << toString(actual);
1003 }
1004 
TEST(UidProcStatsCollectorTest,TestUidProcStatsCollectorContentsFromDevice)1005 TEST(UidProcStatsCollectorTest, TestUidProcStatsCollectorContentsFromDevice) {
1006     UidProcStatsCollector collector;
1007     collector.init();
1008 
1009     ASSERT_TRUE(collector.enabled()) << "/proc/[pid]/.* files are inaccessible";
1010     ASSERT_RESULT_OK(collector.collect());
1011 
1012     const auto& processStats = collector.deltaStats();
1013 
1014     // The below check should pass because there should be at least one process.
1015     EXPECT_GT(processStats.size(), static_cast<size_t>(0));
1016 }
1017 
1018 }  // namespace watchdog
1019 }  // namespace automotive
1020 }  // namespace android
1021