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 <android_car_feature.h>
26 #include <inttypes.h>
27 
28 #include <algorithm>
29 #include <string>
30 
31 namespace android {
32 namespace automotive {
33 namespace watchdog {
34 
35 using ::android::automotive::watchdog::testing::populateProcPidDir;
36 using ::android::base::StringAppendF;
37 using ::android::base::StringPrintf;
38 using ::android::car::feature::car_watchdog_memory_profiling;
39 using ::testing::UnorderedPointwise;
40 
41 namespace {
42 
43 constexpr uint64_t kTestPrivateDirtyKb = 100;
44 
45 MATCHER(UidProcStatsByUidEq, "") {
46     const auto& actual = std::get<0>(arg);
47     const auto& expected = std::get<1>(arg);
48     return actual.first == expected.first &&
49             ExplainMatchResult(UidProcStatsEq(expected.second), actual.second, result_listener);
50 }
51 
pidStatusStr(pid_t pid,uid_t uid)52 std::string pidStatusStr(pid_t pid, uid_t uid) {
53     return StringPrintf("Pid:\t%" PRIu32 "\nTgid:\t%" PRIu32 "\nUid:\t%" PRIu32 "\n", pid, pid,
54                         uid);
55 }
56 
smapsRollupStr(uint64_t rssKb,uint64_t pssKb,uint64_t ussKb,uint64_t swapPssKb)57 std::string smapsRollupStr(uint64_t rssKb, uint64_t pssKb, uint64_t ussKb, uint64_t swapPssKb) {
58     std::string buffer;
59     StringAppendF(&buffer,
60                   "5592470000-7ffc9a9000 ---p 00000000 00:00 0                              "
61                   "[rollup]\n");
62     // clang-format off
63     StringAppendF(&buffer, "Rss: %" PRIu64 " kB\n", rssKb);
64     StringAppendF(&buffer, "Pss: %" PRIu64 " kB\n", pssKb);
65     StringAppendF(&buffer, "Pss_Anon:           1628 kB\n"
66                            "Pss_File:            360 kB\n"
67                            "Pss_Shmem:           303 kB\n"
68                            "Shared_Clean:       2344 kB\n"
69                            "Shared_Dirty:        688 kB\n");
70     /**
71      * Private_Dirty is 100 KB and ussKB = Private_Dirty + Private_Clean. So, ensure that ussKb
72      * is at least 100 KB before proceeding.
73      */
74     EXPECT_GT(ussKb, kTestPrivateDirtyKb);
75     StringAppendF(&buffer, "Private_Clean:      %" PRIu64 " kB\n", ussKb - kTestPrivateDirtyKb);
76     StringAppendF(&buffer, "Private_Dirty:      %" PRIu64 " kB\n", kTestPrivateDirtyKb);
77     StringAppendF(&buffer, "Referenced:         4908 kB\n"
78                            "Anonymous:          1628 kB\n"
79                            "LazyFree:              0 kB\n"
80                            "AnonHugePages:         0 kB\n"
81                            "ShmemPmdMapped:        0 kB\n"
82                            "FilePmdMapped:         0 kB\n"
83                            "Shared_Hugetlb:        0 kB\n"
84                            "Private_Hugetlb:       0 kB\n"
85                            "Swap:               5860 kB\n");
86     StringAppendF(&buffer, "SwapPss:            %" PRIu64 " kB\n", swapPssKb);
87     StringAppendF(&buffer, "Locked:                0 kB");
88     // clang-format on
89     return buffer;
90 }
91 
toString(const std::unordered_map<uid_t,UidProcStats> & uidProcStatsByUid)92 std::string toString(const std::unordered_map<uid_t, UidProcStats>& uidProcStatsByUid) {
93     std::string buffer;
94     StringAppendF(&buffer, "Number of UIDs: %" PRIi32 "\n",
95                   static_cast<int>(uidProcStatsByUid.size()));
96     for (const auto& [uid, stats] : uidProcStatsByUid) {
97         StringAppendF(&buffer, "{UID: %d, %s}", uid, stats.toString().c_str());
98     }
99     return buffer;
100 }
101 
ticksToMillis(int32_t clockTicks)102 int64_t ticksToMillis(int32_t clockTicks) {
103     return (clockTicks * 1000) / sysconf(_SC_CLK_TCK);
104 }
105 
applyFeatureFilter(std::unordered_map<uid_t,UidProcStats> * uidProcStatsByUid)106 void applyFeatureFilter(std::unordered_map<uid_t, UidProcStats>* uidProcStatsByUid) {
107     if (car_watchdog_memory_profiling()) {
108         return;
109     }
110     for (auto& [uid, uidProcStats] : *uidProcStatsByUid) {
111         uidProcStats.totalRssKb = 0;
112         uidProcStats.totalPssKb = 0;
113         for (auto& [pid, processStats] : uidProcStats.processStatsByPid) {
114             processStats.rssKb = 0;
115             processStats.pssKb = 0;
116             processStats.ussKb = 0;
117             processStats.swapPssKb = 0;
118         }
119     }
120 }
121 
isSmapsRollupSupported(std::string rootPath)122 bool isSmapsRollupSupported(std::string rootPath) {
123     std::string path = StringPrintf((rootPath + kSmapsRollupFileFormat).c_str(), 1);
124     return access(path.c_str(), R_OK) == 0;
125 }
126 
127 }  // namespace
128 
TEST(UidProcStatsCollectorTest,TestValidStatFiles)129 TEST(UidProcStatsCollectorTest, TestValidStatFiles) {
130     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
131             {1, {1, 453}},
132             {1000, {1000, 1100}},
133     };
134 
135     std::unordered_map<pid_t, std::string> perProcessStat = {
136             {1, "1 (init) S 0 0 0 0 0 0 0 0 220 0 6 4 0 0 0 0 2 0 19\n"},
137             {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"},
138     };
139 
140     std::unordered_map<pid_t, std::string> perProcessStatus = {
141             {1, pidStatusStr(1, 0)},
142             {1000, pidStatusStr(1000, 10001234)},
143     };
144 
145     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
146             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
147             {1000,
148              smapsRollupStr(/*rssKb=*/2000, /*pssKb=*/1635, /*ussKb=*/1286, /*swapPssKb=*/600)},
149     };
150 
151     std::unordered_map<pid_t, std::string> perThreadStat = {
152             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 3 2 0 0 0 0 2 0 19\n"},
153             {453, "453 (init) D 0 0 0 0 0 0 0 0 20 0 3 2 0 0 0 0 2 0 275\n"},
154             {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"},
155             {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"},
156     };
157 
158     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
159             {1, "cpu0\n300000 5\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
160             {453, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 5\ncpu7\n2000000 0"},
161             {1000,
162              "cpu0\n300000 0\n1700000 1000\ncpu4\n710000 1000\n1800000 3000\ncpu7\n2000000 6000"},
163             {1100, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 1000\ncpu7\n2000000 0"},
164     };
165 
166     std::unordered_map<uid_t, UidProcStats> expected =
167             {{0,
168               UidProcStats{.cpuTimeMillis = ticksToMillis(10),
169                            .cpuCycles = 105'000'000,
170                            .totalMajorFaults = 220,
171                            .totalTasksCount = 2,
172                            .ioBlockedTasksCount = 1,
173                            .totalRssKb = 1000,
174                            .totalPssKb = 865,
175                            .processStatsByPid =
176                                    {{1,
177                                      {/*comm=*/"init", /*startTimeMillis=*/ticksToMillis(19),
178                                       /*cpuTimeMillis=*/ticksToMillis(10),
179                                       /*totalCpuCycles=*/105'000'000, /*totalMajorFaults=*/220,
180                                       /*totalTasksCount=*/2, /*ioBlockedTasksCount=*/1,
181                                       /*cpuCyclesByTid=*/{{1, 15'000'000}, {453, 90'000'000}},
182                                       /*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656,
183                                       /*swapPssKb=*/200}}}}},
184              {10001234,
185               UidProcStats{.cpuTimeMillis = ticksToMillis(12'000),
186                            .cpuCycles = 216100000000,
187                            .totalMajorFaults = 600,
188                            .totalTasksCount = 2,
189                            .ioBlockedTasksCount = 2,
190                            .totalRssKb = 2000,
191                            .totalPssKb = 1635,
192                            .processStatsByPid = {{1000,
193                                                   {/*comm=*/"system_server",
194                                                    /*startTimeMillis=*/ticksToMillis(13'400),
195                                                    /*cpuTimeMillis=*/ticksToMillis(12'000),
196                                                    /*totalCpuCycles=*/216100000000,
197                                                    /*totalMajorFaults=*/600,
198                                                    /*totalTasksCount=*/2,
199                                                    /*ioBlockedTasksCount=*/2, /*cpuCyclesByTid=*/
200                                                    {{1000, 198100000000}, {1100, 18000000000}},
201                                                    /*rssKb=*/2000,
202                                                    /*pssKb=*/1635,
203                                                    /*ussKb=*/1286,
204                                                    /*swapPssKb=*/600}}}}}};
205     applyFeatureFilter(&expected);
206 
207     TemporaryDir firstSnapshot;
208     ASSERT_RESULT_OK(populateProcPidDir(firstSnapshot.path, pidToTids, perProcessStat,
209                                         perProcessStatus, perProcessSmapsRollup,
210                                         /*processStatm=*/{}, perThreadStat, perThreadTimeInState));
211 
212     UidProcStatsCollector collector(firstSnapshot.path, isSmapsRollupSupported(firstSnapshot.path));
213     collector.init();
214 
215     ASSERT_TRUE(collector.enabled())
216             << "Files under the path `" << firstSnapshot.path << "` are inaccessible";
217     ASSERT_RESULT_OK(collector.collect());
218 
219     auto actual = collector.deltaStats();
220 
221     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
222             << "First snapshot doesn't match.\nExpected:\n"
223             << toString(expected) << "\nActual:\n"
224             << toString(actual);
225 
226     pidToTids = {
227             {1, {1, 453}}, {1000, {1000, 1400}},  // TID 1100 terminated and 1400 instantiated.
228     };
229 
230     perProcessStat = {
231             {1, "1 (init) S 0 0 0 0 0 0 0 0 920 0 10 10 0 0 0 0 2 0 19\n"},
232             {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"},
233     };
234 
235     perProcessSmapsRollup = {
236             {1, smapsRollupStr(/*rssKb=*/3000, /*pssKb=*/1865, /*ussKb=*/1656, /*swapPssKb=*/900)},
237             {1000,
238              smapsRollupStr(/*rssKb=*/2010, /*pssKb=*/1645, /*ussKb=*/1296, /*swapPssKb=*/610)},
239     };
240 
241     perThreadStat = {
242             {1, "1 (init) S 0 0 0 0 0 0 0 0 600 0 5 5 0 0 0 0 2 0 19\n"},
243             {453, "453 (init) S 0 0 0 0 0 0 0 0 320 0 5 5 0 0 0 0 2 0 275\n"},
244             {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"},
245             // TID 1100 hits +400 major page faults before terminating. This is counted against
246             // PID 1000's perProcessStat.
247             {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"},
248     };
249 
250     perThreadTimeInState = {
251             {1, "cpu0\n300000 5\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 10"},
252             {453, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 5\ncpu7\n2000000 0"},
253             {1000,
254              "cpu0\n300000 0\n1700000 1000\ncpu4\n710000 1000\n1800000 3000\ncpu7\n2000000 6000"},
255             {1400, "cpu0\n300000 6000\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
256     };
257 
258     expected = {{0,
259                  {.cpuTimeMillis = ticksToMillis(10),
260                   .cpuCycles = 200'000'000,
261                   .totalMajorFaults = 700,
262                   .totalTasksCount = 2,
263                   .ioBlockedTasksCount = 0,
264                   .totalRssKb = 3000,
265                   .totalPssKb = 1865,
266                   .processStatsByPid = {{1,
267                                          {/*comm=*/"init", /*startTimeMillis=*/ticksToMillis(19),
268                                           /*cpuTimeMillis=*/ticksToMillis(10),
269                                           /*totalCpuCycles=*/200'000'000, /*totalMajorFaults=*/700,
270                                           /*totalTasksCount=*/2, /*ioBlockedTasksCount=*/0,
271                                           /*cpuCyclesByTid=*/{{1, 200'000'000}, {453, 0}},
272                                           /*rssKb=*/3000,
273                                           /*pssKb=*/1865,
274                                           /*ussKb=*/1656,
275                                           /*swapPssKb=*/900}}}}},
276                 {10001234,
277                  {.cpuTimeMillis = ticksToMillis(6'000),
278                   .cpuCycles = 18'000'000'000,
279                   .totalMajorFaults = 950,
280                   .totalTasksCount = 2,
281                   .ioBlockedTasksCount = 0,
282                   .totalRssKb = 2010,
283                   .totalPssKb = 1645,
284                   .processStatsByPid = {
285                           {1000,
286                            {/*comm=*/"system_server", /*startTimeMillis=*/ticksToMillis(13'400),
287                             /*cpuTimeMillis=*/ticksToMillis(6'000),
288                             /*totalCpuCycles=*/18'000'000'000, /*totalMajorFaults=*/950,
289                             /*totalTasksCount=*/2, /*ioBlockedTasksCount=*/0,
290                             /*cpuCyclesByTid=*/{{1000, 0}, {1400, 18'000'000'000}},
291                             /*rssKb=*/2010,
292                             /*pssKb=*/1645,
293                             /*ussKb=*/1296,
294                             /*swapPssKb=*/610}}}}}};
295     applyFeatureFilter(&expected);
296 
297     TemporaryDir secondSnapshot;
298     ASSERT_RESULT_OK(populateProcPidDir(secondSnapshot.path, pidToTids, perProcessStat,
299                                         perProcessStatus, perProcessSmapsRollup,
300                                         /*processStatm=*/{}, perThreadStat, perThreadTimeInState));
301 
302     collector.mPath = secondSnapshot.path;
303 
304     ASSERT_TRUE(collector.enabled())
305             << "Files under the path `" << secondSnapshot.path << "` are inaccessible";
306     ASSERT_RESULT_OK(collector.collect());
307 
308     actual = collector.deltaStats();
309     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
310             << "Second snapshot doesn't match.\nExpected:\n"
311             << toString(expected) << "\nActual:\n"
312             << toString(actual);
313 }
314 
TEST(UidProcStatsCollectorTest,TestHandlesProcessTerminationBetweenScanningAndParsing)315 TEST(UidProcStatsCollectorTest, TestHandlesProcessTerminationBetweenScanningAndParsing) {
316     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
317             {1, {1}},
318             {100, {100}},          // Process terminates after scanning PID directory.
319             {1000, {1000}},        // Process terminates after reading stat file.
320             {2000, {2000}},        // Process terminates after scanning task directory.
321             {3000, {3000, 3300}},  // TID 3300 terminates after scanning task directory.
322     };
323 
324     std::unordered_map<pid_t, std::string> perProcessStat = {
325             {1, "1 (init) S 0 0 0 0 0 0 0 0 220 0 10 10 0 0 0 0 1 0 19\n"},
326             // Process 100 terminated.
327             {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"},
328             {2000, "2000 (logd) R 1 0 0 0 0 0 0 0 1200 0 30 30 0 0 0 0 1 0 4567\n"},
329             {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"},
330     };
331 
332     std::unordered_map<pid_t, std::string> perProcessStatus = {
333             {1, pidStatusStr(1, 0)},
334             // Process 1000 terminated.
335             {2000, pidStatusStr(2000, 10001234)},
336             {3000, pidStatusStr(3000, 10001234)},
337     };
338 
339     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
340             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
341             {2000,
342              smapsRollupStr(/*rssKb=*/2000, /*pssKb=*/1635, /*ussKb=*/1286, /*swapPssKb=*/600)},
343             {3000,
344              smapsRollupStr(/*rssKb=*/5642, /*pssKb=*/2312, /*ussKb=*/944, /*swapPssKb=*/500)},
345     };
346 
347     std::unordered_map<pid_t, std::string> perThreadStat = {
348             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
349             // Process 2000 terminated.
350             {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"},
351             // TID 3300 terminated.
352     };
353 
354     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
355             {1, "cpu0\n300000 10\n1700000 10\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
356             // Process 3000 terminated.
357     };
358 
359     std::unordered_map<uid_t, UidProcStats> expected =
360             {{0,
361               UidProcStats{.cpuTimeMillis = ticksToMillis(20),
362                            .cpuCycles = 200'000'000,
363                            .totalMajorFaults = 220,
364                            .totalTasksCount = 1,
365                            .ioBlockedTasksCount = 0,
366                            .totalRssKb = 1000,
367                            .totalPssKb = 865,
368                            .processStatsByPid =
369                                    {{1,
370                                      {/*comm=*/"init", /*startTimeMillis=*/ticksToMillis(19),
371                                       /*cpuTimeMillis=*/ticksToMillis(20),
372                                       /*totalCpuCycles=*/200'000'000, /*totalMajorFaults=*/220,
373                                       /*totalTasksCount=*/1, /*ioBlockedTasksCount=*/0,
374                                       /*cpuCyclesByTid=*/{{1, 200'000'000}},
375                                       /*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656,
376                                       /*swapPssKb=*/200}}}}},
377              {10001234,
378               UidProcStats{.cpuTimeMillis = ticksToMillis(140),
379                            .cpuCycles = 0,
380                            .totalMajorFaults = 11500,
381                            .totalTasksCount = 2,
382                            .ioBlockedTasksCount = 0,
383                            .totalRssKb = 7642,
384                            .totalPssKb = 3947,
385                            .processStatsByPid =
386                                    {{2000,
387                                      {/*comm=*/"logd", /*startTimeMillis=*/ticksToMillis(4567),
388                                       /*cpuTimeMillis=*/ticksToMillis(60), /*totalCpuCycles=*/0,
389                                       /*totalMajorFaults=*/1200, /*totalTasksCount=*/1,
390                                       /*ioBlockedTasksCount=*/0, /*cpuCyclesByTid=*/{},
391                                       /*rssKb=*/2000, /*pssKb=*/1635, /*ussKb=*/1286,
392                                       /*swapPssKb=*/600}},
393                                     {3000,
394                                      {/*comm=*/"disk I/O",
395                                       /*startTimeMillis=*/ticksToMillis(67890),
396                                       /*cpuTimeMillis=*/ticksToMillis(80), /*totalCpuCycles=*/0,
397                                       /*totalMajorFaults=*/10'300, /*totalTasksCount=*/1,
398                                       /*ioBlockedTasksCount=*/0, /*cpuCyclesByTid=*/{},
399                                       /*rssKb=*/5642, /*pssKb=*/2312, /*ussKb=*/944,
400                                       /*swapPssKb=*/500}}}}}};
401     applyFeatureFilter(&expected);
402 
403     TemporaryDir procDir;
404     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
405                                         perProcessSmapsRollup, /*processStatm=*/{}, perThreadStat,
406                                         perThreadTimeInState));
407 
408     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
409     collector.init();
410 
411     ASSERT_TRUE(collector.enabled())
412             << "Files under the path `" << procDir.path << "` are inaccessible";
413     ASSERT_RESULT_OK(collector.collect());
414 
415     auto actual = collector.deltaStats();
416     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
417             << "Proc pid contents doesn't match.\nExpected:\n"
418             << toString(expected) << "\nActual:\n"
419             << toString(actual);
420 }
421 
TEST(UidProcStatsCollectorTest,TestHandlesPidTidReuse)422 TEST(UidProcStatsCollectorTest, TestHandlesPidTidReuse) {
423     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
424             {1, {1, 367, 453, 589}},
425             {1000, {1000}},
426             {2345, {2345}},
427     };
428 
429     std::unordered_map<pid_t, std::string> perProcessStat = {
430             {1, "1 (init) S 0 0 0 0 0 0 0 0 1200 0 40 40 0 0 0 0 4 0 19\n"},
431             {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"},
432             {2345, "2345 (logd) R 1 0 0 0 0 0 0 0 54354 0 10 10 0 0 0 0 1 0 456\n"},
433     };
434 
435     std::unordered_map<pid_t, std::string> perProcessStatus = {
436             {1, pidStatusStr(1, 0)},
437             {1000, pidStatusStr(1000, 10001234)},
438             {2345, pidStatusStr(2345, 10001234)},
439     };
440 
441     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
442             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
443             {1000,
444              smapsRollupStr(/*rssKb=*/2000, /*pssKb=*/1635, /*ussKb=*/1286, /*swapPssKb=*/600)},
445             {2345,
446              smapsRollupStr(/*rssKb=*/5642, /*pssKb=*/2312, /*ussKb=*/944, /*swapPssKb=*/500)},
447     };
448 
449     std::unordered_map<pid_t, std::string> perThreadStat = {
450             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 4 0 19\n"},
451             {367, "367 (init) S 0 0 0 0 0 0 0 0 400 0 10 10 0 0 0 0 4 0 100\n"},
452             {453, "453 (init) S 0 0 0 0 0 0 0 0 100 0 10 10 0 0 0 0 4 0 275\n"},
453             {589, "589 (init) D 0 0 0 0 0 0 0 0 500 0 10 10 0 0 0 0 4 0 600\n"},
454             {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"},
455             {2345, "2345 (logd) R 1 0 0 0 0 0 0 0 54354 0 10 10 0 0 0 0 1 0 456\n"},
456     };
457 
458     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
459             {1, "cpu0\n300000 20\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
460             {367, "cpu0\n300000 0\n1700000 20\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
461             {453, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 20\ncpu7\n2000000 0"},
462             {589, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 20"},
463             {1000, "cpu0\n300000 20\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
464             {2345, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 20\ncpu7\n2000000 0"},
465     };
466 
467     std::unordered_map<uid_t, UidProcStats> expected =
468             {{0,
469               UidProcStats{.cpuTimeMillis = ticksToMillis(80),
470                            .cpuCycles = 1'160'000'000,
471                            .totalMajorFaults = 1200,
472                            .totalTasksCount = 4,
473                            .ioBlockedTasksCount = 1,
474                            .totalRssKb = 1000,
475                            .totalPssKb = 865,
476                            .processStatsByPid = {{1,
477                                                   {/*comm=*/"init",
478                                                    /*startTimeMillis=*/ticksToMillis(19),
479                                                    /*cpuTimeMillis=*/ticksToMillis(80),
480                                                    /*totalCpuCycles=*/1'160'000'000,
481                                                    /*totalMajorFaults=*/1200,
482                                                    /*totalTasksCount=*/4,
483                                                    /*ioBlockedTasksCount=*/1, /*cpuCyclesByTid=*/
484                                                    {{1, 60'000'000},
485                                                     {367, 340'000'000},
486                                                     {453, 360'000'000},
487                                                     {589, 400'000'000}},
488                                                    /*rssKb=*/1000,
489                                                    /*pssKb=*/865,
490                                                    /*ussKb=*/656,
491                                                    /*swapPssKb=*/200}}}}},
492              {10001234,
493               UidProcStats{.cpuTimeMillis = ticksToMillis(40),
494                            .cpuCycles = 420'000'000,
495                            .totalMajorFaults = 54'604,
496                            .totalTasksCount = 2,
497                            .ioBlockedTasksCount = 0,
498                            .totalRssKb = 7642,
499                            .totalPssKb = 3947,
500                            .processStatsByPid =
501                                    {{1000,
502                                      {/*comm=*/"system_server",
503                                       /*startTimeMillis=*/ticksToMillis(1000),
504                                       /*cpuTimeMillis=*/ticksToMillis(20),
505                                       /*totalCpuCycles=*/60'000'000, /*totalMajorFaults=*/250,
506                                       /*totalTasksCount=*/1, /*ioBlockedTasksCount=*/0,
507                                       /*cpuCyclesByTid=*/{{1000, 60'000'000}},
508                                       /*rssKb=*/2000, /*pssKb=*/1635, /*ussKb=*/1286,
509                                       /*swapPssKb=*/600}},
510                                     {2345,
511                                      {/*comm=*/"logd", /*startTimeMillis=*/ticksToMillis(456),
512                                       /*cpuTimeMillis=*/ticksToMillis(20),
513                                       /*totalCpuCycles=*/360'000'000,
514                                       /*totalMajorFaults=*/54'354,
515                                       /*totalTasksCount=*/1, /*ioBlockedTasksCount=*/0,
516                                       /*cpuCyclesByTid=*/{{2345, 360'000'000}},
517                                       /*rssKb=*/5642, /*pssKb=*/2312, /*ussKb=*/944,
518                                       /*swapPssKb=*/500}}}}}};
519     applyFeatureFilter(&expected);
520 
521     TemporaryDir firstSnapshot;
522     ASSERT_RESULT_OK(populateProcPidDir(firstSnapshot.path, pidToTids, perProcessStat,
523                                         perProcessStatus, perProcessSmapsRollup,
524                                         /*processStatm=*/{}, perThreadStat, perThreadTimeInState));
525 
526     UidProcStatsCollector collector(firstSnapshot.path, isSmapsRollupSupported(firstSnapshot.path));
527     collector.init();
528 
529     ASSERT_TRUE(collector.enabled())
530             << "Files under the path `" << firstSnapshot.path << "` are inaccessible";
531     ASSERT_RESULT_OK(collector.collect());
532 
533     auto actual = collector.deltaStats();
534 
535     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
536             << "First snapshot doesn't match.\nExpected:\n"
537             << toString(expected) << "\nActual:\n"
538             << toString(actual);
539 
540     pidToTids = {
541             {1, {1, 589}},       // TID 589 reused by the same process.
542             {367, {367, 2000}},  // TID 367 reused as a PID. PID 2000 reused as a TID.
543             // PID 1000 reused as a new PID. TID 453 reused by a different PID.
544             {1000, {1000, 453}},
545     };
546 
547     perProcessStat = {
548             {1, "1 (init) S 0 0 0 0 0 0 0 0 1800 0 60 60 0 0 0 0 2 0 19\n"},
549             {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"},
550             {1000, "1000 (logd) R 1 0 0 0 0 0 0 0 2000 0 20 20 0 0 0 0 2 0 4650\n"},
551     };
552 
553     perProcessStatus = {
554             {1, pidStatusStr(1, 0)},
555             {367, pidStatusStr(367, 10001234)},
556             {1000, pidStatusStr(1000, 10001234)},
557     };
558 
559     perProcessSmapsRollup = {
560             {1, smapsRollupStr(/*rssKb=*/1500, /*pssKb=*/965, /*ussKb=*/756, /*swapPssKb=*/300)},
561             {367,
562              smapsRollupStr(/*rssKb=*/2000, /*pssKb=*/1635, /*ussKb=*/1286, /*swapPssKb=*/600)},
563             {1000,
564              smapsRollupStr(/*rssKb=*/5642, /*pssKb=*/2312, /*ussKb=*/944, /*swapPssKb=*/500)},
565     };
566 
567     perThreadStat = {
568             {1, "1 (init) S 0 0 0 0 0 0 0 0 500 0 20 20 0 0 0 0 2 0 19\n"},
569             {589, "589 (init) S 0 0 0 0 0 0 0 0 300 0 10 10 0 0 0 0 2 0 2345\n"},
570             {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"},
571             {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"},
572             {1000, "1000 (logd) R 1 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 2 0 4650\n"},
573             {453, "453 (logd) D 1 0 0 0 0 0 0 0 1800 0 10 10 0 0 0 0 2 0 4770\n"},
574     };
575 
576     perThreadTimeInState = {
577             {1, "cpu0\n300000 20\n1700000 20\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
578             {589, "cpu0\n300000 20\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 20"},
579             {367, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 30\n1800000 0\ncpu7\n2000000 0"},
580             {2000, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 30"},
581             {1000, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 20\ncpu7\n2000000 0"},
582             {453, "cpu0\n300000 20\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
583     };
584 
585     expected = {{0,
586                  UidProcStats{.cpuTimeMillis = ticksToMillis(40),
587                               .cpuCycles = 400'000'000,
588                               .totalMajorFaults = 600,
589                               .totalTasksCount = 2,
590                               .ioBlockedTasksCount = 0,
591                               .totalRssKb = 1500,
592                               .totalPssKb = 965,
593                               .processStatsByPid =
594                                       {{1,
595                                         {/*comm=*/"init", /*startTimeMillis=*/ticksToMillis(19),
596                                          /*cpuTimeMillis=*/ticksToMillis(40),
597                                          /*totalCpuCycles=*/400'000'000, /*totalMajorFaults=*/600,
598                                          /*totalTasksCount=*/2, /*ioBlockedTasksCount=*/0,
599                                          /*cpuCyclesByTid=*/{{1, 340'000'000}, {589, 60'000'000}},
600                                          /*rssKb=*/1500, /*pssKb=*/965, /*ussKb=*/756,
601                                          /*swapPssKb=*/300}}}}},
602                 {10001234,
603                  UidProcStats{.cpuTimeMillis = ticksToMillis(100),
604                               .cpuCycles = 1'233'000'000,
605                               .totalMajorFaults = 2100,
606                               .totalTasksCount = 4,
607                               .ioBlockedTasksCount = 1,
608                               .totalRssKb = 7642,
609                               .totalPssKb = 3947,
610                               .processStatsByPid = {{367,
611                                                      {/*comm=*/"system_server",
612                                                       /*startTimeMillis=*/ticksToMillis(3450),
613                                                       /*cpuTimeMillis=*/ticksToMillis(60),
614                                                       /*totalCpuCycles=*/813'000'000,
615                                                       /*totalMajorFaults=*/100,
616                                                       /*totalTasksCount=*/2,
617                                                       /*ioBlockedTasksCount=*/0, /*cpuCyclesByTid=*/
618                                                       {{367, 213'000'000}, {2000, 600'000'000}},
619                                                       /*rssKb=*/2000,
620                                                       /*pssKb=*/1635,
621                                                       /*ussKb=*/1286,
622                                                       /*swapPssKb=*/600}},
623                                                     {1000,
624                                                      {/*comm=*/"logd",
625                                                       /*startTimeMillis=*/ticksToMillis(4650),
626                                                       /*cpuTimeMillis=*/ticksToMillis(40),
627                                                       /*totalCpuCycles=*/420'000'000,
628                                                       /*totalMajorFaults=*/2000,
629                                                       /*totalTasksCount=*/2,
630                                                       /*ioBlockedTasksCount=*/1, /*cpuCyclesByTid=*/
631                                                       {{1000, 360'000'000}, {453, 60'000'000}},
632                                                       /*rssKb=*/5642,
633                                                       /*pssKb=*/2312,
634                                                       /*ussKb=*/944,
635                                                       /*swapPssKb=*/500}}}}}};
636     applyFeatureFilter(&expected);
637 
638     TemporaryDir secondSnapshot;
639     ASSERT_RESULT_OK(populateProcPidDir(secondSnapshot.path, pidToTids, perProcessStat,
640                                         perProcessStatus, perProcessSmapsRollup,
641                                         /*processStatm=*/{}, perThreadStat, perThreadTimeInState));
642 
643     collector.mPath = secondSnapshot.path;
644 
645     ASSERT_TRUE(collector.enabled())
646             << "Files under the path `" << secondSnapshot.path << "` are inaccessible";
647     ASSERT_RESULT_OK(collector.collect());
648 
649     actual = collector.deltaStats();
650 
651     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
652             << "Second snapshot doesn't match.\nExpected:\n"
653             << toString(expected) << "\nActual:\n"
654             << toString(actual);
655 }
656 
TEST(UidProcStatsCollectorTest,TestHandlesNoSmapsRollupKernelSupport)657 TEST(UidProcStatsCollectorTest, TestHandlesNoSmapsRollupKernelSupport) {
658     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
659             {1, {1}},
660     };
661 
662     std::unordered_map<pid_t, std::string> perProcessStat = {
663             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
664     };
665 
666     std::unordered_map<pid_t, std::string> perProcessStatus = {
667             {1, pidStatusStr(1, 0)},
668     };
669 
670     std::unordered_map<pid_t, std::string> perProcessStatm = {
671             {1, "2969783 1481 938 530 0 5067 0"},
672     };
673 
674     std::unordered_map<pid_t, std::string> perThreadStat = {
675             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
676     };
677 
678     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
679             {1, "cpu0\n300000 0\n1700000 20\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
680     };
681 
682     std::unordered_map<uid_t, UidProcStats> expected = {
683             {0,
684              UidProcStats{.cpuTimeMillis = ticksToMillis(20),
685                           .cpuCycles = 340'000'000,
686                           .totalMajorFaults = 200,
687                           .totalTasksCount = 1,
688                           .ioBlockedTasksCount = 0,
689                           .totalRssKb = 5924,
690                           .totalPssKb = 0,
691                           .processStatsByPid = {
692                                   {1,
693                                    {/*comm=*/"init", /*startTimeMillis=*/ticksToMillis(19),
694                                     /*cpuTimeMillis=*/ticksToMillis(20),
695                                     /*totalCpuCycles=*/340'000'000,
696                                     /*totalMajorFaults=*/200, /*totalTasksCount=*/1,
697                                     /*ioBlockedTasksCount=*/0,
698                                     /*cpuCyclesByTid=*/{{1, 340'000'000}},
699                                     /*rssKb=*/5924, /*pssKb=*/0, /*ussKb=*/2172,
700                                     /*swapPssKb=*/0}}}}}};
701     applyFeatureFilter(&expected);
702 
703     TemporaryDir procDir;
704     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
705                                         /*processSmapsRollup=*/{}, perProcessStatm, perThreadStat,
706                                         perThreadTimeInState));
707 
708     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
709     collector.init();
710 
711     ASSERT_TRUE(collector.enabled())
712             << "Files under the path `" << procDir.path << "` are inaccessible";
713     ASSERT_RESULT_OK(collector.collect());
714 
715     auto actual = collector.deltaStats();
716 
717     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
718             << "Proc pid contents doesn't match.\nExpected:\n"
719             << toString(expected) << "\nActual:\n"
720             << toString(actual);
721 }
722 
TEST(UidProcStatsCollectorTest,TestErrorOnCorruptedProcessStatFile)723 TEST(UidProcStatsCollectorTest, TestErrorOnCorruptedProcessStatFile) {
724     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
725             {1, {1}},
726     };
727 
728     std::unordered_map<pid_t, std::string> perProcessStat = {
729             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 CORRUPTED DATA\n"},
730     };
731 
732     std::unordered_map<pid_t, std::string> perProcessStatus = {
733             {1, pidStatusStr(1, 0)},
734     };
735 
736     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
737             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
738     };
739 
740     std::unordered_map<pid_t, std::string> perThreadStat = {
741             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
742     };
743 
744     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
745             {1, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
746     };
747 
748     TemporaryDir procDir;
749     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
750                                         perProcessSmapsRollup, /*processStatm=*/{}, perThreadStat,
751                                         perThreadTimeInState));
752 
753     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
754     collector.init();
755 
756     ASSERT_TRUE(collector.enabled())
757             << "Files under the path `" << procDir.path << "` are inaccessible";
758     ASSERT_FALSE(collector.collect().ok()) << "No error returned for invalid process stat file";
759 }
760 
TEST(UidProcStatsCollectorTest,TestErrorOnCorruptedProcessStatusFile)761 TEST(UidProcStatsCollectorTest, TestErrorOnCorruptedProcessStatusFile) {
762     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
763             {1, {1}},
764     };
765 
766     std::unordered_map<pid_t, std::string> perProcessStat = {
767             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
768     };
769 
770     std::unordered_map<pid_t, std::string> perProcessStatus = {
771             {1, "Pid:\t1\nTgid:\t1\nCORRUPTED DATA\n"},
772     };
773 
774     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
775             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
776     };
777 
778     std::unordered_map<pid_t, std::string> perThreadStat = {
779             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
780     };
781 
782     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
783             {1, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
784     };
785 
786     TemporaryDir procDir;
787     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
788                                         perProcessSmapsRollup, /*processStatm=*/{}, perThreadStat,
789                                         perThreadTimeInState));
790 
791     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
792     collector.init();
793 
794     ASSERT_TRUE(collector.enabled())
795             << "Files under the path `" << procDir.path << "` are inaccessible";
796     ASSERT_FALSE(collector.collect().ok()) << "No error returned for invalid process status file";
797 }
798 
TEST(UidProcStatsCollectorTest,TestErrorOnProcessStatusFileWithNoUid)799 TEST(UidProcStatsCollectorTest, TestErrorOnProcessStatusFileWithNoUid) {
800     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
801             {1, {1}},
802     };
803 
804     std::unordered_map<pid_t, std::string> perProcessStat = {
805             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
806     };
807 
808     std::unordered_map<pid_t, std::string> perProcessStatus = {
809             {1, "Pid:\t1\nTgid:\t1\n"},
810     };
811 
812     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
813             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
814     };
815 
816     std::unordered_map<pid_t, std::string> perThreadStat = {
817             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
818     };
819 
820     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
821             {1, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
822     };
823 
824     TemporaryDir procDir;
825     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
826                                         perProcessSmapsRollup, /*processStatm=*/{}, perThreadStat,
827                                         perThreadTimeInState));
828 
829     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
830     collector.init();
831 
832     ASSERT_TRUE(collector.enabled())
833             << "Files under the path `" << procDir.path << "` are inaccessible";
834     ASSERT_FALSE(collector.collect().ok())
835             << "No error returned for process status file without uid";
836 }
837 
TEST(UidProcStatsCollectorTest,TestErrorOnProcessStatusFileWithNoTgid)838 TEST(UidProcStatsCollectorTest, TestErrorOnProcessStatusFileWithNoTgid) {
839     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
840             {1, {1}},
841     };
842 
843     std::unordered_map<pid_t, std::string> perProcessStat = {
844             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
845     };
846 
847     std::unordered_map<pid_t, std::string> perProcessStatus = {
848             {1, "Pid:\t1\nUid:\t1\n"},
849     };
850 
851     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
852             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
853     };
854 
855     std::unordered_map<pid_t, std::string> perThreadStat = {
856             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 19\n"},
857     };
858 
859     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
860             {1, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
861     };
862 
863     TemporaryDir procDir;
864     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
865                                         perProcessSmapsRollup, /*processStatm=*/{}, perThreadStat,
866                                         perThreadTimeInState));
867 
868     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
869     collector.init();
870 
871     ASSERT_TRUE(collector.enabled())
872             << "Files under the path `" << procDir.path << "` are inaccessible";
873     ASSERT_FALSE(collector.collect().ok())
874             << "No error returned for process status file without tgid";
875 }
876 
TEST(UidProcStatsCollectorTest,TestErrorOnCorruptedThreadStatFile)877 TEST(UidProcStatsCollectorTest, TestErrorOnCorruptedThreadStatFile) {
878     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
879             {1, {1, 234}},
880     };
881 
882     std::unordered_map<pid_t, std::string> perProcessStat = {
883             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 678\n"},
884     };
885 
886     std::unordered_map<pid_t, std::string> perProcessStatus = {
887             {1, pidStatusStr(1, 0)},
888     };
889 
890     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
891             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
892     };
893 
894     std::unordered_map<pid_t, std::string> perThreadStat = {
895             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 678\n"},
896             {234, "234 (init) D 0 0 0 0 0 0 0 0 200 0 0 0 CORRUPTED DATA\n"},
897     };
898 
899     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
900             {1, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
901     };
902 
903     TemporaryDir procDir;
904     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
905                                         perProcessSmapsRollup, /*processStatm=*/{}, perThreadStat,
906                                         perThreadTimeInState));
907 
908     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
909     collector.init();
910 
911     ASSERT_TRUE(collector.enabled())
912             << "Files under the path `" << procDir.path << "` are inaccessible";
913     ASSERT_FALSE(collector.collect().ok()) << "No error returned for invalid thread stat file";
914 }
915 
TEST(UidProcStatsCollectorTest,TestErrorOnCorruptedThreadTimeInStateFile)916 TEST(UidProcStatsCollectorTest, TestErrorOnCorruptedThreadTimeInStateFile) {
917     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
918             {1, {1, 234}},
919     };
920 
921     std::unordered_map<pid_t, std::string> perProcessStat = {
922             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 678\n"},
923     };
924 
925     std::unordered_map<pid_t, std::string> perProcessStatus = {
926             {1, pidStatusStr(1, 0)},
927     };
928 
929     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
930             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
931     };
932 
933     std::unordered_map<pid_t, std::string> perThreadStat = {
934             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 678\n"},
935             {234, "234 (init) D 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 500\n"},
936     };
937 
938     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
939             {1, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 0\n"},
940             {234, "cpu0\n300000 0\n1700000 10\ncpu4\n710000 0\n1800000 CORRUPTED\n DATA"},
941     };
942 
943     TemporaryDir procDir;
944     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
945                                         perProcessSmapsRollup, /*processStatm=*/{}, perThreadStat,
946                                         perThreadTimeInState));
947 
948     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
949     collector.init();
950 
951     ASSERT_TRUE(collector.enabled())
952             << "Files under the path `" << procDir.path << "` are inaccessible";
953     ASSERT_FALSE(collector.collect().ok()) << "No error returned for invalid thread stat file";
954 }
955 
TEST(UidProcStatsCollectorTest,TestHandlesSpaceInCommName)956 TEST(UidProcStatsCollectorTest, TestHandlesSpaceInCommName) {
957     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
958             {1, {1}},
959     };
960 
961     std::unordered_map<pid_t, std::string> perProcessStat = {
962             {1,
963              "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"},
964     };
965 
966     std::unordered_map<pid_t, std::string> perProcessStatus = {
967             {1, pidStatusStr(1, 0)},
968     };
969 
970     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
971             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
972     };
973 
974     std::unordered_map<pid_t, std::string> perThreadStat = {
975             {1,
976              "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"},
977     };
978 
979     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
980             {1, "cpu0\n300000 0\n1700000 20\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
981     };
982 
983     std::unordered_map<uid_t, UidProcStats> expected = {
984             {0,
985              UidProcStats{.cpuTimeMillis = ticksToMillis(20),
986                           .cpuCycles = 340'000'000,
987                           .totalMajorFaults = 200,
988                           .totalTasksCount = 1,
989                           .ioBlockedTasksCount = 0,
990                           .totalRssKb = 1000,
991                           .totalPssKb = 865,
992                           .processStatsByPid = {
993                                   {1,
994                                    {/*comm=*/"random process name with space",
995                                     /*startTimeMillis=*/ticksToMillis(19),
996                                     /*cpuTimeMillis=*/ticksToMillis(20),
997                                     /*totalCpuCycles=*/340'000'000, /*totalMajorFaults=*/200,
998                                     /*totalTasksCount=*/1, /*ioBlockedTasksCount=*/0,
999                                     /*cpuCyclesByTid=*/{{1, 340'000'000}},
1000                                     /*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656,
1001                                     /*swapPssKb=*/200}}}}}};
1002     applyFeatureFilter(&expected);
1003 
1004     TemporaryDir procDir;
1005     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
1006                                         perProcessSmapsRollup, /*processStatm=*/{}, perThreadStat,
1007                                         perThreadTimeInState));
1008 
1009     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
1010     collector.init();
1011 
1012     ASSERT_TRUE(collector.enabled())
1013             << "Files under the path `" << procDir.path << "` are inaccessible";
1014     ASSERT_RESULT_OK(collector.collect());
1015 
1016     auto actual = collector.deltaStats();
1017 
1018     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
1019             << "Proc pid contents doesn't match.\nExpected:\n"
1020             << toString(expected) << "\nActual:\n"
1021             << toString(actual);
1022 }
1023 
TEST(UidProcStatsCollectorTest,TestHandlesTimeInStateFileDisabledWithNoFile)1024 TEST(UidProcStatsCollectorTest, TestHandlesTimeInStateFileDisabledWithNoFile) {
1025     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
1026             {1, {1}},
1027     };
1028 
1029     std::unordered_map<pid_t, std::string> perProcessStat = {
1030             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
1031     };
1032 
1033     std::unordered_map<pid_t, std::string> perProcessStatus = {
1034             {1, pidStatusStr(1, 0)},
1035     };
1036 
1037     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
1038             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
1039     };
1040 
1041     std::unordered_map<pid_t, std::string> perThreadStat = {
1042             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
1043     };
1044 
1045     // No time_in_state file present in procfs
1046 
1047     std::unordered_map<uid_t, UidProcStats> expected = {
1048             {0,
1049              UidProcStats{.cpuTimeMillis = ticksToMillis(20),
1050                           .cpuCycles = 0,
1051                           .totalMajorFaults = 200,
1052                           .totalTasksCount = 1,
1053                           .ioBlockedTasksCount = 0,
1054                           .totalRssKb = 1000,
1055                           .totalPssKb = 865,
1056                           .processStatsByPid = {
1057                                   {1,
1058                                    {/*comm=*/"init", /*startTimeMillis=*/ticksToMillis(19),
1059                                     /*cpuTimeMillis=*/ticksToMillis(20), /*totalCpuCycles=*/0,
1060                                     /*totalMajorFaults=*/200, /*totalTasksCount=*/1,
1061                                     /*ioBlockedTasksCount=*/0, /*cpuCyclesByTid=*/{},
1062                                     /*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656,
1063                                     /*swapPssKb=*/200}}}}}};
1064 
1065     applyFeatureFilter(&expected);
1066 
1067     TemporaryDir procDir;
1068     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
1069                                         perProcessSmapsRollup, /*processStatm=*/{}, perThreadStat,
1070                                         {}));
1071 
1072     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
1073     collector.init();
1074 
1075     ASSERT_TRUE(collector.enabled())
1076             << "Files under the path `" << procDir.path << "` are inaccessible";
1077     ASSERT_RESULT_OK(collector.collect());
1078 
1079     auto actual = collector.deltaStats();
1080 
1081     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
1082             << "Proc pid contents doesn't match.\nExpected:\n"
1083             << toString(expected) << "\nActual:\n"
1084             << toString(actual);
1085 }
1086 
TEST(UidProcStatsCollectorTest,TestHandlesTimeInStateFileDisabledWithEmptyFile)1087 TEST(UidProcStatsCollectorTest, TestHandlesTimeInStateFileDisabledWithEmptyFile) {
1088     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
1089             {1, {1}},
1090     };
1091 
1092     std::unordered_map<pid_t, std::string> perProcessStat = {
1093             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
1094     };
1095 
1096     std::unordered_map<pid_t, std::string> perProcessStatus = {
1097             {1, pidStatusStr(1, 0)},
1098     };
1099 
1100     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
1101             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
1102     };
1103 
1104     std::unordered_map<pid_t, std::string> perThreadStat = {
1105             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
1106     };
1107 
1108     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
1109             {1, ""},
1110     };
1111 
1112     std::unordered_map<uid_t, UidProcStats> expected = {
1113             {0,
1114              UidProcStats{.cpuTimeMillis = ticksToMillis(20),
1115                           .cpuCycles = 0,
1116                           .totalMajorFaults = 200,
1117                           .totalTasksCount = 1,
1118                           .ioBlockedTasksCount = 0,
1119                           .totalRssKb = 1000,
1120                           .totalPssKb = 865,
1121                           .processStatsByPid = {
1122                                   {1,
1123                                    {/*comm=*/"init", /*startTimeMillis=*/ticksToMillis(19),
1124                                     /*cpuTimeMillis=*/ticksToMillis(20), /*totalCpuCycles=*/0,
1125                                     /*totalMajorFaults=*/200, /*totalTasksCount=*/1,
1126                                     /*ioBlockedTasksCount=*/0, /*cpuCyclesByTid=*/{},
1127                                     /*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656,
1128                                     /*swapPssKb=*/200}}}}}};
1129     applyFeatureFilter(&expected);
1130 
1131     TemporaryDir procDir;
1132     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
1133                                         perProcessSmapsRollup, /*processStatm=*/{}, perThreadStat,
1134                                         {}));
1135 
1136     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
1137     collector.init();
1138 
1139     ASSERT_TRUE(collector.enabled())
1140             << "Files under the path `" << procDir.path << "` are inaccessible";
1141     ASSERT_RESULT_OK(collector.collect());
1142 
1143     auto actual = collector.deltaStats();
1144 
1145     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
1146             << "Proc pid contents doesn't match.\nExpected:\n"
1147             << toString(expected) << "\nActual:\n"
1148             << toString(actual);
1149 }
1150 
TEST(UidProcStatsCollectorTest,TestHandlesTimeInStateFileDisabledWithZeroCpuCycles)1151 TEST(UidProcStatsCollectorTest, TestHandlesTimeInStateFileDisabledWithZeroCpuCycles) {
1152     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
1153             {1, {1}},
1154     };
1155 
1156     std::unordered_map<pid_t, std::string> perProcessStat = {
1157             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
1158     };
1159 
1160     std::unordered_map<pid_t, std::string> perProcessStatus = {
1161             {1, pidStatusStr(1, 0)},
1162     };
1163 
1164     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
1165             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
1166     };
1167 
1168     std::unordered_map<pid_t, std::string> perThreadStat = {
1169             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
1170     };
1171 
1172     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
1173             {1, "cpu0\n300000 0\n1700000 0\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
1174     };
1175 
1176     std::unordered_map<uid_t, UidProcStats> expected = {
1177             {0,
1178              UidProcStats{.cpuTimeMillis = ticksToMillis(20),
1179                           .cpuCycles = 0,
1180                           .totalMajorFaults = 200,
1181                           .totalTasksCount = 1,
1182                           .ioBlockedTasksCount = 0,
1183                           .totalRssKb = 1000,
1184                           .totalPssKb = 865,
1185                           .processStatsByPid = {
1186                                   {1,
1187                                    {/*comm=*/"init", /*startTimeMillis=*/ticksToMillis(19),
1188                                     /*cpuTimeMillis=*/ticksToMillis(20), /*totalCpuCycles=*/0,
1189                                     /*totalMajorFaults=*/200, /*totalTasksCount=*/1,
1190                                     /*ioBlockedTasksCount=*/0, /*cpuCyclesByTid=*/{},
1191                                     /*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656,
1192                                     /*swapPssKb=*/200}}}}}};
1193     applyFeatureFilter(&expected);
1194 
1195     TemporaryDir procDir;
1196     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
1197                                         perProcessSmapsRollup, /*processStatm=*/{}, perThreadStat,
1198                                         {}));
1199 
1200     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
1201     collector.init();
1202 
1203     ASSERT_TRUE(collector.enabled())
1204             << "Files under the path `" << procDir.path << "` are inaccessible";
1205     ASSERT_RESULT_OK(collector.collect());
1206 
1207     auto actual = collector.deltaStats();
1208 
1209     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
1210             << "Proc pid contents doesn't match.\nExpected:\n"
1211             << toString(expected) << "\nActual:\n"
1212             << toString(actual);
1213 }
1214 
TEST(UidProcStatsCollectorTest,TestHandlesNoTimeInStateFileDuringCollection)1215 TEST(UidProcStatsCollectorTest, TestHandlesNoTimeInStateFileDuringCollection) {
1216     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
1217             {1, {1, 234}},
1218     };
1219 
1220     std::unordered_map<pid_t, std::string> perProcessStat = {
1221             {1, "1 (init) S 0 0 0 0 0 0 0 0 210 0 15 15 0 0 0 0 2 0 19\n"},
1222     };
1223 
1224     std::unordered_map<pid_t, std::string> perProcessStatus = {
1225             {1, pidStatusStr(1, 0)},
1226     };
1227 
1228     std::unordered_map<pid_t, std::string> perProcessSmapsRollup = {
1229             {1, smapsRollupStr(/*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656, /*swapPssKb=*/200)},
1230     };
1231 
1232     std::unordered_map<pid_t, std::string> perThreadStat = {
1233             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 2 0 19\n"},
1234             {234, "1 (init) S 0 0 0 0 0 0 0 0 10 0 5 5 0 0 0 0 2 0 19\n"},
1235     };
1236 
1237     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
1238             {1, "cpu0\n300000 0\n1700000 20\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
1239             // No time_in_state file present for TID 234
1240     };
1241 
1242     std::unordered_map<uid_t, UidProcStats> expected = {
1243             {0,
1244              UidProcStats{.cpuTimeMillis = ticksToMillis(30),
1245                           .cpuCycles = 340'000'000,
1246                           .totalMajorFaults = 210,
1247                           .totalTasksCount = 2,
1248                           .ioBlockedTasksCount = 0,
1249                           .totalRssKb = 1000,
1250                           .totalPssKb = 865,
1251                           .processStatsByPid = {
1252                                   {1,
1253                                    {/*comm=*/"init", /*startTimeMillis=*/ticksToMillis(19),
1254                                     /*cpuTimeMillis=*/ticksToMillis(30),
1255                                     /*totalCpuCycles=*/340'000'000, /*totalMajorFaults=*/210,
1256                                     /*totalTasksCount=*/2, /*ioBlockedTasksCount=*/0,
1257                                     /*cpuCyclesByTid=*/{{1, 340'000'000}},
1258                                     /*rssKb=*/1000, /*pssKb=*/865, /*ussKb=*/656,
1259                                     /*swapPssKb=*/200}}}}}};
1260     applyFeatureFilter(&expected);
1261 
1262     TemporaryDir procDir;
1263     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
1264                                         perProcessSmapsRollup, /*processStatm=*/{}, perThreadStat,
1265                                         perThreadTimeInState));
1266 
1267     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
1268     collector.init();
1269 
1270     ASSERT_TRUE(collector.enabled())
1271             << "Files under the path `" << procDir.path << "` are inaccessible";
1272     ASSERT_RESULT_OK(collector.collect());
1273 
1274     auto actual = collector.deltaStats();
1275 
1276     EXPECT_THAT(actual, UnorderedPointwise(UidProcStatsByUidEq(), expected))
1277             << "Proc pid contents doesn't match.\nExpected:\n"
1278             << toString(expected) << "\nActual:\n"
1279             << toString(actual);
1280 }
1281 
TEST(UidProcStatsCollectorTest,TestCollectorStatusOnMissingSmapsRollupAndStatmFiles)1282 TEST(UidProcStatsCollectorTest, TestCollectorStatusOnMissingSmapsRollupAndStatmFiles) {
1283     std::unordered_map<pid_t, std::vector<pid_t>> pidToTids = {
1284             {1, {1}},
1285     };
1286 
1287     std::unordered_map<pid_t, std::string> perProcessStat = {
1288             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
1289     };
1290 
1291     std::unordered_map<pid_t, std::string> perProcessStatus = {
1292             {1, pidStatusStr(1, 0)},
1293     };
1294 
1295     std::unordered_map<pid_t, std::string> perThreadStat = {
1296             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 10 10 0 0 0 0 1 0 19\n"},
1297     };
1298 
1299     std::unordered_map<pid_t, std::string> perThreadTimeInState = {
1300             {1, "cpu0\n300000 0\n1700000 20\ncpu4\n710000 0\n1800000 0\ncpu7\n2000000 0"},
1301     };
1302 
1303     TemporaryDir procDir;
1304     ASSERT_RESULT_OK(populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
1305                                         /*processSmapsRollup=*/{}, /*processStatm=*/{},
1306                                         perThreadStat, perThreadTimeInState));
1307 
1308     UidProcStatsCollector collector(procDir.path, isSmapsRollupSupported(procDir.path));
1309     collector.init();
1310 
1311     ASSERT_EQ(!car_watchdog_memory_profiling(), collector.enabled())
1312             << "Collector status when memory profiling feature is "
1313             << (car_watchdog_memory_profiling() ? "enabled" : "disabled")
1314             << " and per-process smaps rollup / statm are missing";
1315 }
1316 
TEST(UidProcStatsCollectorTest,TestUidProcStatsCollectorContentsFromDevice)1317 TEST(UidProcStatsCollectorTest, TestUidProcStatsCollectorContentsFromDevice) {
1318     UidProcStatsCollector collector;
1319     collector.init();
1320 
1321     ASSERT_TRUE(collector.enabled()) << "/proc/[pid]/.* files are inaccessible";
1322     ASSERT_RESULT_OK(collector.collect());
1323 
1324     const auto& processStats = collector.deltaStats();
1325 
1326     // The below check should pass because there should be at least one process.
1327     EXPECT_GT(processStats.size(), static_cast<size_t>(0));
1328 }
1329 
1330 }  // namespace watchdog
1331 }  // namespace automotive
1332 }  // namespace android
1333