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