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 "ProcPidStat.h"
18 
19 #include <android-base/file.h>
20 #include <android-base/stringprintf.h>
21 #include <inttypes.h>
22 
23 #include <algorithm>
24 #include <string>
25 
26 #include "ProcPidDir.h"
27 #include "gmock/gmock.h"
28 
29 namespace android {
30 namespace automotive {
31 namespace watchdog {
32 
33 using android::base::StringAppendF;
34 using android::base::StringPrintf;
35 using testing::populateProcPidDir;
36 
37 namespace {
38 
toString(const PidStat & stat)39 std::string toString(const PidStat& stat) {
40     return StringPrintf("PID: %" PRIu32 ", PPID: %" PRIu32 ", Comm: %s, State: %s, "
41                         "Major page faults: %" PRIu64 ", Num threads: %" PRIu32
42                         ", Start time: %" PRIu64,
43                         stat.pid, stat.ppid, stat.comm.c_str(), stat.state.c_str(),
44                         stat.majorFaults, stat.numThreads, stat.startTime);
45 }
46 
toString(const ProcessStats & stats)47 std::string toString(const ProcessStats& stats) {
48     std::string buffer;
49     StringAppendF(&buffer, "Tgid: %" PRIi64 ", UID: %" PRIi64 ", %s\n", stats.tgid, stats.uid,
50                   toString(stats.process).c_str());
51     StringAppendF(&buffer, "\tThread stats:\n");
52     for (const auto& it : stats.threads) {
53         StringAppendF(&buffer, "\t\t%s\n", toString(it.second).c_str());
54     }
55     StringAppendF(&buffer, "\n");
56     return buffer;
57 }
58 
toString(const std::vector<ProcessStats> & stats)59 std::string toString(const std::vector<ProcessStats>& stats) {
60     std::string buffer;
61     StringAppendF(&buffer, "Number of processes: %d\n", static_cast<int>(stats.size()));
62     for (const auto& it : stats) {
63         StringAppendF(&buffer, "%s", toString(it).c_str());
64     }
65     return buffer;
66 }
67 
isEqual(const PidStat & lhs,const PidStat & rhs)68 bool isEqual(const PidStat& lhs, const PidStat& rhs) {
69     return lhs.pid == rhs.pid && lhs.comm == rhs.comm && lhs.state == rhs.state &&
70             lhs.ppid == rhs.ppid && lhs.majorFaults == rhs.majorFaults &&
71             lhs.numThreads == rhs.numThreads && lhs.startTime == rhs.startTime;
72 }
73 
isEqual(std::vector<ProcessStats> * lhs,std::vector<ProcessStats> * rhs)74 bool isEqual(std::vector<ProcessStats>* lhs, std::vector<ProcessStats>* rhs) {
75     if (lhs->size() != rhs->size()) {
76         return false;
77     }
78     std::sort(lhs->begin(), lhs->end(), [&](const ProcessStats& l, const ProcessStats& r) -> bool {
79         return l.process.pid < r.process.pid;
80     });
81     std::sort(rhs->begin(), rhs->end(), [&](const ProcessStats& l, const ProcessStats& r) -> bool {
82         return l.process.pid < r.process.pid;
83     });
84     return std::equal(lhs->begin(), lhs->end(), rhs->begin(),
85                       [&](const ProcessStats& l, const ProcessStats& r) -> bool {
86                           if (l.tgid != r.tgid || l.uid != r.uid ||
87                               !isEqual(l.process, r.process) ||
88                               l.threads.size() != r.threads.size()) {
89                               return false;
90                           }
91                           for (const auto& lIt : l.threads) {
92                               const auto& rIt = r.threads.find(lIt.first);
93                               if (rIt == r.threads.end()) {
94                                   return false;
95                               }
96                               if (!isEqual(lIt.second, rIt->second)) {
97                                   return false;
98                               }
99                           }
100                           return true;
101                       });
102 }
103 
104 }  // namespace
105 
TEST(ProcPidStatTest,TestValidStatFiles)106 TEST(ProcPidStatTest, TestValidStatFiles) {
107     std::unordered_map<uint32_t, std::vector<uint32_t>> pidToTids = {
108             {1, {1, 453}},
109             {1000, {1000, 1100}},
110     };
111 
112     std::unordered_map<uint32_t, std::string> perProcessStat = {
113             {1, "1 (init) S 0 0 0 0 0 0 0 0 220 0 0 0 0 0 0 0 2 0 0\n"},
114             {1000, "1000 (system_server) R 1 0 0 0 0 0 0 0 600 0 0 0 0 0 0 0 2 0 1000\n"},
115     };
116 
117     std::unordered_map<uint32_t, std::string> perProcessStatus = {
118             {1, "Pid:\t1\nTgid:\t1\nUid:\t0\t0\t0\t0\n"},
119             {1000, "Pid:\t1000\nTgid:\t1000\nUid:\t10001234\t10001234\t10001234\t10001234\n"},
120     };
121 
122     std::unordered_map<uint32_t, std::string> perThreadStat = {
123             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 0\n"},
124             {453, "453 (init) S 0 0 0 0 0 0 0 0 20 0 0 0 0 0 0 0 2 0 275\n"},
125             {1000, "1000 (system_server) R 1 0 0 0 0 0 0 0 250 0 0 0 0 0 0 0 2 0 1000\n"},
126             {1100, "1100 (system_server) S 1 0 0 0 0 0 0 0 350 0 0 0 0 0 0 0 2 0 1200\n"},
127     };
128 
129     std::vector<ProcessStats> expected = {
130             {
131                     .tgid = 1,
132                     .uid = 0,
133                     .process = {1, "init", "S", 0, 220, 2, 0},
134                     .threads =
135                             {
136                                     {1, {1, "init", "S", 0, 200, 2, 0}},
137                                     {453, {453, "init", "S", 0, 20, 2, 275}},
138                             },
139             },
140             {
141                     .tgid = 1000,
142                     .uid = 10001234,
143                     .process = {1000, "system_server", "R", 1, 600, 2, 1000},
144                     .threads =
145                             {
146                                     {1000, {1000, "system_server", "R", 1, 250, 2, 1000}},
147                                     {1100, {1100, "system_server", "S", 1, 350, 2, 1200}},
148                             },
149             },
150     };
151 
152     TemporaryDir firstSnapshot;
153     auto ret = populateProcPidDir(firstSnapshot.path, pidToTids, perProcessStat, perProcessStatus,
154                                   perThreadStat);
155     ASSERT_TRUE(ret) << "Failed to populate proc pid dir: " << ret.error();
156 
157     ProcPidStat procPidStat(firstSnapshot.path);
158     ASSERT_TRUE(procPidStat.enabled())
159             << "Files under the path `" << firstSnapshot.path << "` are inaccessible";
160 
161     auto actual = procPidStat.collect();
162     ASSERT_TRUE(actual) << "Failed to collect proc pid stat: " << actual.error();
163 
164     EXPECT_TRUE(isEqual(&expected, &actual.value())) << "First snapshot doesn't match.\nExpected:\n"
165                                                      << toString(expected) << "\nActual:\n"
166                                                      << toString(*actual);
167 
168     pidToTids = {
169             {1, {1, 453}}, {1000, {1000, 1400}},  // TID 1100 terminated and 1400 instantiated.
170     };
171 
172     perProcessStat = {
173             {1, "1 (init) S 0 0 0 0 0 0 0 0 920 0 0 0 0 0 0 0 2 0 0\n"},
174             {1000, "1000 (system_server) R 1 0 0 0 0 0 0 0 1550 0 0 0 0 0 0 0 2 0 1000\n"},
175     };
176 
177     perThreadStat = {
178             {1, "1 (init) S 0 0 0 0 0 0 0 0 600 0 0 0 0 0 0 0 2 0 0\n"},
179             {453, "453 (init) S 0 0 0 0 0 0 0 0 320 0 0 0 0 0 0 0 2 0 275\n"},
180             {1000, "1000 (system_server) R 1 0 0 0 0 0 0 0 600 0 0 0 0 0 0 0 2 0 1000\n"},
181             // TID 1100 hits +400 major page faults before terminating. This is counted against
182             // PID 1000's perProcessStat.
183             {1400, "1400 (system_server) S 1 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 8977476\n"},
184     };
185 
186     expected = {
187             {
188                     .tgid = 1,
189                     .uid = 0,
190                     .process = {1, "init", "S", 0, 700, 2, 0},
191                     .threads =
192                             {
193                                     {1, {1, "init", "S", 0, 400, 2, 0}},
194                                     {453, {453, "init", "S", 0, 300, 2, 275}},
195                             },
196             },
197             {
198                     .tgid = 1000,
199                     .uid = 10001234,
200                     .process = {1000, "system_server", "R", 1, 950, 2, 1000},
201                     .threads =
202                             {
203                                     {1000, {1000, "system_server", "R", 1, 350, 2, 1000}},
204                                     {1400, {1400, "system_server", "S", 1, 200, 2, 8977476}},
205                             },
206             },
207     };
208 
209     TemporaryDir secondSnapshot;
210     ret = populateProcPidDir(secondSnapshot.path, pidToTids, perProcessStat, perProcessStatus,
211                              perThreadStat);
212     ASSERT_TRUE(ret) << "Failed to populate proc pid dir: " << ret.error();
213 
214     procPidStat.mPath = secondSnapshot.path;
215     ASSERT_TRUE(procPidStat.enabled())
216             << "Files under the path `" << secondSnapshot.path << "` are inaccessible";
217 
218     actual = procPidStat.collect();
219     ASSERT_TRUE(actual) << "Failed to collect proc pid stat: " << actual.error();
220 
221     EXPECT_TRUE(isEqual(&expected, &actual.value()))
222             << "Second snapshot doesn't match.\nExpected:\n"
223             << toString(expected) << "\nActual:\n"
224             << toString(*actual);
225 }
226 
TEST(ProcPidStatTest,TestHandlesProcessTerminationBetweenScanningAndParsing)227 TEST(ProcPidStatTest, TestHandlesProcessTerminationBetweenScanningAndParsing) {
228     std::unordered_map<uint32_t, std::vector<uint32_t>> pidToTids = {
229             {1, {1}},
230             {100, {100}},          // Process terminates after scanning PID directory.
231             {1000, {1000}},        // Process terminates after reading stat file.
232             {2000, {2000}},        // Process terminates after scanning task directory.
233             {3000, {3000, 3300}},  // TID 3300 terminates after scanning task directory.
234     };
235 
236     std::unordered_map<uint32_t, std::string> perProcessStat = {
237             {1, "1 (init) S 0 0 0 0 0 0 0 0 220 0 0 0 0 0 0 0 1 0 0\n"},
238             // Process 100 terminated.
239             {1000, "1000 (system_server) R 1 0 0 0 0 0 0 0 600 0 0 0 0 0 0 0 1 0 1000\n"},
240             {2000, "2000 (logd) R 1 0 0 0 0 0 0 0 1200 0 0 0 0 0 0 0 1 0 4567\n"},
241             {3000, "3000 (disk I/O) R 1 0 0 0 0 0 0 0 10300 0 0 0 0 0 0 0 2 0 67890\n"},
242     };
243 
244     std::unordered_map<uint32_t, std::string> perProcessStatus = {
245             {1, "Pid:\t1\nTgid:\t1\nUid:\t0\t0\t0\t0\n"},
246             // Process 1000 terminated.
247             {2000, "Pid:\t2000\nTgid:\t2000\nUid:\t10001234\t10001234\t10001234\t10001234\n"},
248             {3000, "Pid:\t3000\nTgid:\t3000\nUid:\t10001234\t10001234\t10001234\t10001234\n"},
249     };
250 
251     std::unordered_map<uint32_t, std::string> perThreadStat = {
252             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
253             // Process 2000 terminated.
254             {3000, "3000 (disk I/O) R 1 0 0 0 0 0 0 0 2400 0 0 0 0 0 0 0 2 0 67890\n"},
255             // TID 3300 terminated.
256     };
257 
258     std::vector<ProcessStats> expected = {
259             {
260                     .tgid = 1,
261                     .uid = 0,
262                     .process = {1, "init", "S", 0, 220, 1, 0},
263                     .threads = {{1, {1, "init", "S", 0, 200, 1, 0}}},
264             },
265             {
266                     .tgid = -1,
267                     .uid = -1,
268                     .process = {1000, "system_server", "R", 1, 600, 1, 1000},
269                     // Stats common between process and main-thread are copied when
270                     // main-thread stats are not available.
271                     .threads = {{1000, {1000, "system_server", "R", 1, 0, 1, 1000}}},
272             },
273             {
274                     .tgid = 2000,
275                     .uid = 10001234,
276                     .process = {2000, "logd", "R", 1, 1200, 1, 4567},
277                     .threads = {{2000, {2000, "logd", "R", 1, 0, 1, 4567}}},
278             },
279             {
280                     .tgid = 3000,
281                     .uid = 10001234,
282                     .process = {3000, "disk I/O", "R", 1, 10300, 2, 67890},
283                     .threads = {{3000, {3000, "disk I/O", "R", 1, 2400, 2, 67890}}},
284             },
285     };
286 
287     TemporaryDir procDir;
288     const auto& ret = populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
289                                          perThreadStat);
290     ASSERT_TRUE(ret) << "Failed to populate proc pid dir: " << ret.error();
291 
292     ProcPidStat procPidStat(procDir.path);
293     ASSERT_TRUE(procPidStat.enabled())
294             << "Files under the path `" << procDir.path << "` are inaccessible";
295 
296     auto actual = procPidStat.collect();
297     ASSERT_TRUE(actual) << "Failed to collect proc pid stat: " << actual.error();
298 
299     EXPECT_TRUE(isEqual(&expected, &actual.value()))
300             << "Proc pid contents doesn't match.\nExpected:\n"
301             << toString(expected) << "\nActual:\n"
302             << toString(*actual);
303 }
304 
TEST(ProcPidStatTest,TestHandlesPidTidReuse)305 TEST(ProcPidStatTest, TestHandlesPidTidReuse) {
306     std::unordered_map<uint32_t, std::vector<uint32_t>> pidToTids = {
307             {1, {1, 367, 453, 589}},
308             {1000, {1000}},
309             {2345, {2345}},
310     };
311 
312     std::unordered_map<uint32_t, std::string> perProcessStat = {
313             {1, "1 (init) S 0 0 0 0 0 0 0 0 1200 0 0 0 0 0 0 0 4 0 0\n"},
314             {1000, "1000 (system_server) R 1 0 0 0 0 0 0 0 250 0 0 0 0 0 0 0 1 0 1000\n"},
315             {2345, "2345 (logd) R 1 0 0 0 0 0 0 0 54354 0 0 0 0 0 0 0 1 0 456\n"},
316     };
317 
318     std::unordered_map<uint32_t, std::string> perProcessStatus = {
319             {1, "Pid:\t1\nTgid:\t1\nUid:\t0\t0\t0\t0\n"},
320             {1000, "Pid:\t1000\nTgid:\t1000\nUid:\t10001234\t10001234\t10001234\t10001234\n"},
321             {2345, "Pid:\t2345\nTgid:\t2345\nUid:\t10001234\t10001234\t10001234\t10001234\n"},
322     };
323 
324     std::unordered_map<uint32_t, std::string> perThreadStat = {
325             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 4 0 0\n"},
326             {367, "367 (init) S 0 0 0 0 0 0 0 0 400 0 0 0 0 0 0 0 4 0 100\n"},
327             {453, "453 (init) S 0 0 0 0 0 0 0 0 100 0 0 0 0 0 0 0 4 0 275\n"},
328             {589, "589 (init) S 0 0 0 0 0 0 0 0 500 0 0 0 0 0 0 0 4 0 600\n"},
329             {1000, "1000 (system_server) R 1 0 0 0 0 0 0 0 250 0 0 0 0 0 0 0 1 0 1000\n"},
330             {2345, "2345 (logd) R 1 0 0 0 0 0 0 0 54354 0 0 0 0 0 0 0 1 0 456\n"},
331     };
332 
333     std::vector<ProcessStats> expected = {
334             {
335                     .tgid = 1,
336                     .uid = 0,
337                     .process = {1, "init", "S", 0, 1200, 4, 0},
338                     .threads =
339                             {
340                                     {1, {1, "init", "S", 0, 200, 4, 0}},
341                                     {367, {367, "init", "S", 0, 400, 4, 100}},
342                                     {453, {453, "init", "S", 0, 100, 4, 275}},
343                                     {589, {589, "init", "S", 0, 500, 4, 600}},
344                             },
345             },
346             {
347                     .tgid = 1000,
348                     .uid = 10001234,
349                     .process = {1000, "system_server", "R", 1, 250, 1, 1000},
350                     .threads = {{1000, {1000, "system_server", "R", 1, 250, 1, 1000}}},
351             },
352             {
353                     .tgid = 2345,
354                     .uid = 10001234,
355                     .process = {2345, "logd", "R", 1, 54354, 1, 456},
356                     .threads = {{2345, {2345, "logd", "R", 1, 54354, 1, 456}}},
357             },
358     };
359 
360     TemporaryDir firstSnapshot;
361     auto ret = populateProcPidDir(firstSnapshot.path, pidToTids, perProcessStat, perProcessStatus,
362                                   perThreadStat);
363     ASSERT_TRUE(ret) << "Failed to populate proc pid dir: " << ret.error();
364 
365     ProcPidStat procPidStat(firstSnapshot.path);
366     ASSERT_TRUE(procPidStat.enabled())
367             << "Files under the path `" << firstSnapshot.path << "` are inaccessible";
368 
369     auto actual = procPidStat.collect();
370     ASSERT_TRUE(actual) << "Failed to collect proc pid stat: " << actual.error();
371 
372     EXPECT_TRUE(isEqual(&expected, &actual.value())) << "First snapshot doesn't match.\nExpected:\n"
373                                                      << toString(expected) << "\nActual:\n"
374                                                      << toString(*actual);
375 
376     pidToTids = {
377             {1, {1, 589}},       // TID 589 reused by the same process.
378             {367, {367, 2000}},  // TID 367 reused as a PID. PID 2000 reused as a TID.
379             // PID 1000 reused as a new PID. TID 453 reused by a different PID.
380             {1000, {1000, 453}},
381     };
382 
383     perProcessStat = {
384             {1, "1 (init) S 0 0 0 0 0 0 0 0 1800 0 0 0 0 0 0 0 2 0 0\n"},
385             {367, "367 (system_server) R 1 0 0 0 0 0 0 0 100 0 0 0 0 0 0 0 2 0 3450\n"},
386             {1000, "1000 (logd) R 1 0 0 0 0 0 0 0 2000 0 0 0 0 0 0 0 2 0 4650\n"},
387     };
388 
389     perProcessStatus = {
390             {1, "Pid:\t1\nTgid:\t1\nUid:\t0\t0\t0\t0\n"},
391             {367, "Pid:\t367\nTgid:\t367\nUid:\t10001234\t10001234\t10001234\t10001234\n"},
392             {1000, "Pid:\t1000\nTgid:\t1000\nUid:\t10001234\t10001234\t10001234\t10001234\n"},
393     };
394 
395     perThreadStat = {
396             {1, "1 (init) S 0 0 0 0 0 0 0 0 500 0 0 0 0 0 0 0 2 0 0\n"},
397             {589, "589 (init) S 0 0 0 0 0 0 0 0 300 0 0 0 0 0 0 0 2 0 2345\n"},
398             {367, "367 (system_server) R 1 0 0 0 0 0 0 0 50 0 0 0 0 0 0 0 2 0 3450\n"},
399             {2000, "2000 (system_server) R 1 0 0 0 0 0 0 0 50 0 0 0 0 0 0 0 2 0 3670\n"},
400             {1000, "1000 (logd) R 1 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 4650\n"},
401             {453, "453 (logd) D 1 0 0 0 0 0 0 0 1800 0 0 0 0 0 0 0 2 0 4770\n"},
402     };
403 
404     expected = {
405             {
406                     .tgid = 1,
407                     .uid = 0,
408                     .process = {1, "init", "S", 0, 600, 2, 0},
409                     .threads =
410                             {
411                                     {1, {1, "init", "S", 0, 300, 2, 0}},
412                                     {589, {589, "init", "S", 0, 300, 2, 2345}},
413                             },
414             },
415             {
416                     .tgid = 367,
417                     .uid = 10001234,
418                     .process = {367, "system_server", "R", 1, 100, 2, 3450},
419                     .threads =
420                             {
421                                     {367, {367, "system_server", "R", 1, 50, 2, 3450}},
422                                     {2000, {2000, "system_server", "R", 1, 50, 2, 3670}},
423                             },
424             },
425             {
426                     .tgid = 1000,
427                     .uid = 10001234,
428                     .process = {1000, "logd", "R", 1, 2000, 2, 4650},
429                     .threads =
430                             {
431                                     {1000, {1000, "logd", "R", 1, 200, 2, 4650}},
432                                     {453, {453, "logd", "D", 1, 1800, 2, 4770}},
433                             },
434             },
435     };
436 
437     TemporaryDir secondSnapshot;
438     ret = populateProcPidDir(secondSnapshot.path, pidToTids, perProcessStat, perProcessStatus,
439                              perThreadStat);
440     ASSERT_TRUE(ret) << "Failed to populate proc pid dir: " << ret.error();
441 
442     procPidStat.mPath = secondSnapshot.path;
443     ASSERT_TRUE(procPidStat.enabled())
444             << "Files under the path `" << secondSnapshot.path << "` are inaccessible";
445 
446     actual = procPidStat.collect();
447     ASSERT_TRUE(actual) << "Failed to collect proc pid stat: " << actual.error();
448 
449     EXPECT_TRUE(isEqual(&expected, &actual.value()))
450             << "Second snapshot doesn't match.\nExpected:\n"
451             << toString(expected) << "\nActual:\n"
452             << toString(*actual);
453 }
454 
TEST(ProcPidStatTest,TestErrorOnCorruptedProcessStatFile)455 TEST(ProcPidStatTest, TestErrorOnCorruptedProcessStatFile) {
456     std::unordered_map<uint32_t, std::vector<uint32_t>> pidToTids = {
457             {1, {1}},
458     };
459 
460     std::unordered_map<uint32_t, std::string> perProcessStat = {
461             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 CORRUPTED DATA\n"},
462     };
463 
464     std::unordered_map<uint32_t, std::string> perProcessStatus = {
465             {1, "Pid:\t1\nTgid:\t1\nUid:\t0\t0\t0\t0\n"},
466     };
467 
468     std::unordered_map<uint32_t, std::string> perThreadStat = {
469             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
470     };
471 
472     TemporaryDir procDir;
473     const auto& ret = populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
474                                          perThreadStat);
475     ASSERT_TRUE(ret) << "Failed to populate proc pid dir: " << ret.error();
476 
477     ProcPidStat procPidStat(procDir.path);
478     ASSERT_TRUE(procPidStat.enabled())
479             << "Files under the path `" << procDir.path << "` are inaccessible";
480 
481     const auto& actual = procPidStat.collect();
482     ASSERT_FALSE(actual) << "No error returned for invalid process stat file";
483 }
484 
TEST(ProcPidStatTest,TestErrorOnCorruptedProcessStatusFile)485 TEST(ProcPidStatTest, TestErrorOnCorruptedProcessStatusFile) {
486     std::unordered_map<uint32_t, std::vector<uint32_t>> pidToTids = {
487             {1, {1}},
488     };
489 
490     std::unordered_map<uint32_t, std::string> perProcessStat = {
491             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
492     };
493 
494     std::unordered_map<uint32_t, std::string> perProcessStatus = {
495             {1, "Pid:\t1\nTgid:\t1\nCORRUPTED DATA\n"},
496     };
497 
498     std::unordered_map<uint32_t, std::string> perThreadStat = {
499             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
500     };
501 
502     TemporaryDir procDir;
503     const auto& ret = populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
504                                          perThreadStat);
505     ASSERT_TRUE(ret) << "Failed to populate proc pid dir: " << ret.error();
506 
507     ProcPidStat procPidStat(procDir.path);
508     ASSERT_TRUE(procPidStat.enabled())
509             << "Files under the path `" << procDir.path << "` are inaccessible";
510 
511     const auto& actual = procPidStat.collect();
512     ASSERT_FALSE(actual) << "No error returned for invalid process status file";
513 }
514 
TEST(ProcPidStatTest,TestErrorOnCorruptedThreadStatFile)515 TEST(ProcPidStatTest, TestErrorOnCorruptedThreadStatFile) {
516     std::unordered_map<uint32_t, std::vector<uint32_t>> pidToTids = {
517             {1, {1}},
518     };
519 
520     std::unordered_map<uint32_t, std::string> perProcessStat = {
521             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
522     };
523 
524     std::unordered_map<uint32_t, std::string> perProcessStatus = {
525             {1, "Pid:\t1\nTgid:\t1\nUid:\t0\t0\t0\t0\n"},
526     };
527 
528     std::unordered_map<uint32_t, std::string> perThreadStat = {
529             {1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 CORRUPTED DATA\n"},
530     };
531 
532     TemporaryDir procDir;
533     const auto& ret = populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
534                                          perThreadStat);
535     ASSERT_TRUE(ret) << "Failed to populate proc pid dir: " << ret.error();
536 
537     ProcPidStat procPidStat(procDir.path);
538     ASSERT_TRUE(procPidStat.enabled())
539             << "Files under the path `" << procDir.path << "` are inaccessible";
540 
541     const auto& actual = procPidStat.collect();
542     ASSERT_FALSE(actual) << "No error returned for invalid thread stat file";
543 }
544 
TEST(ProcPidStatTest,TestHandlesSpaceInCommName)545 TEST(ProcPidStatTest, TestHandlesSpaceInCommName) {
546     std::unordered_map<uint32_t, std::vector<uint32_t>> pidToTids = {
547             {1, {1}},
548     };
549 
550     std::unordered_map<uint32_t, std::string> perProcessStat = {
551             {1, "1 (random process name with space) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
552     };
553 
554     std::unordered_map<uint32_t, std::string> perProcessStatus = {
555             {1, "Pid:\t1\nTgid:\t1\nUid:\t0\t0\t0\t0\n"},
556     };
557 
558     std::unordered_map<uint32_t, std::string> perThreadStat = {
559             {1, "1 (random process name with space) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 1 0 0\n"},
560     };
561 
562     std::vector<ProcessStats> expected = {
563             {
564                     .tgid = 1,
565                     .uid = 0,
566                     .process = {1, "random process name with space", "S", 0, 200, 1, 0},
567                     .threads = {{1, {1, "random process name with space", "S", 0, 200, 1, 0}}},
568             },
569     };
570 
571     TemporaryDir procDir;
572     const auto& ret = populateProcPidDir(procDir.path, pidToTids, perProcessStat, perProcessStatus,
573                                          perThreadStat);
574     ASSERT_TRUE(ret) << "Failed to populate proc pid dir: " << ret.error();
575 
576     ProcPidStat procPidStat(procDir.path);
577     ASSERT_TRUE(procPidStat.enabled())
578             << "Files under the path `" << procDir.path << "` are inaccessible";
579 
580     auto actual = procPidStat.collect();
581     ASSERT_TRUE(actual) << "Failed to collect proc pid stat: " << actual.error();
582 
583     EXPECT_TRUE(isEqual(&expected, &actual.value()))
584             << "Proc pid contents doesn't match.\nExpected:\n"
585             << toString(expected) << "\nActual:\n"
586             << toString(*actual);
587 }
588 
TEST(ProcPidStatTest,TestProcPidStatContentsFromDevice)589 TEST(ProcPidStatTest, TestProcPidStatContentsFromDevice) {
590     // TODO(b/148486340): Enable the test after appropriate SELinux privileges are available to
591     // read the proc file.
592     /*ProcPidStat procPidStat;
593     ASSERT_TRUE(procPidStat.enabled()) << "/proc/[pid]/.* files are inaccessible";
594 
595     const auto& processStats = procPidStat.collect();
596     ASSERT_TRUE(processStats) << "Failed to collect proc pid stat: " << processStats.error();
597 
598     // The below check should pass because there should be at least one process.
599     EXPECT_GT(processStats->size(), 0);*/
600 }
601 
602 }  // namespace watchdog
603 }  // namespace automotive
604 }  // namespace android
605