• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 #define LOG_TAG "carwatchdogd"
18 #define DEBUG false  // STOPSHIP if true.
19 
20 #include "UidProcStatsCollector.h"
21 
22 #include <android-base/file.h>
23 #include <android-base/parseint.h>
24 #include <android-base/result.h>
25 #include <android-base/stringprintf.h>
26 #include <android-base/strings.h>
27 #include <log/log.h>
28 
29 #include <dirent.h>
30 
31 #include <string>
32 #include <string_view>
33 #include <unordered_map>
34 #include <vector>
35 
36 namespace android {
37 namespace automotive {
38 namespace watchdog {
39 
40 using ::android::base::EndsWith;
41 using ::android::base::Error;
42 using ::android::base::ParseInt;
43 using ::android::base::ParseUint;
44 using ::android::base::ReadFileToString;
45 using ::android::base::Result;
46 using ::android::base::Split;
47 using ::android::base::StartsWith;
48 using ::android::base::StringAppendF;
49 using ::android::base::Trim;
50 
51 namespace {
52 
53 constexpr uint64_t kMaxUint64 = std::numeric_limits<uint64_t>::max();
54 
55 constexpr const char* kProcPidStatFileFormat = "/proc/%" PRIu32 "/stat";
56 constexpr const char* kProcPidStatusFileFormat = "/proc/%" PRIu32 "/status";
57 
58 enum ReadError {
59     ERR_INVALID_FILE = 0,
60     ERR_FILE_OPEN_READ = 1,
61     NUM_ERRORS = 2,
62 };
63 
addUint64(const uint64_t & l,const uint64_t & r)64 uint64_t addUint64(const uint64_t& l, const uint64_t& r) {
65     return (kMaxUint64 - l) > r ? (l + r) : kMaxUint64;
66 }
67 
68 /**
69  * /proc/PID/stat or /proc/PID/task/TID/stat format:
70  * <pid> <comm> <state> <ppid> <pgrp ID> <session ID> <tty_nr> <tpgid> <flags> <minor faults>
71  * <children minor faults> <major faults> <children major faults> <user mode time>
72  * <system mode time> <children user mode time> <children kernel mode time> <priority> <nice value>
73  * <num threads> <start time since boot> <virtual memory size> <resident set size> <rss soft limit>
74  * <start code addr> <end code addr> <start stack addr> <ESP value> <EIP> <bitmap of pending sigs>
75  * <bitmap of blocked sigs> <bitmap of ignored sigs> <waiting channel> <num pages swapped>
76  * <cumulative pages swapped> <exit signal> <processor #> <real-time prio> <agg block I/O delays>
77  * <guest time> <children guest time> <start data addr> <end data addr> <start break addr>
78  * <cmd line args start addr> <amd line args end addr> <env start addr> <env end addr> <exit code>
79  * Example line: 1 (init) S 0 0 0 0 0 0 0 0 220 0 0 0 0 0 0 0 2 0 0 ...etc...
80  */
parsePidStatLine(const std::string & line,PidStat * pidStat)81 bool parsePidStatLine(const std::string& line, PidStat* pidStat) {
82     std::vector<std::string> fields = Split(line, " ");
83 
84     /* Note: Regex parsing for the below logic increased the time taken to run the
85      * UidProcStatsCollectorTest#TestProcPidStatContentsFromDevice from 151.7ms to 1.3 seconds.
86      *
87      * Comm string is enclosed with ( ) brackets and may contain space(s). Thus calculate the
88      * commEndOffset based on the field that contains the closing bracket.
89      */
90     size_t commEndOffset = 0;
91     for (size_t i = 1; i < fields.size(); ++i) {
92         pidStat->comm += fields[i];
93         if (EndsWith(fields[i], ")")) {
94             commEndOffset = i - 1;
95             break;
96         }
97         pidStat->comm += " ";
98     }
99 
100     if (pidStat->comm.front() != '(' || pidStat->comm.back() != ')') {
101         ALOGD("Comm string `%s` not enclosed in brackets", pidStat->comm.c_str());
102         return false;
103     }
104     pidStat->comm.erase(pidStat->comm.begin());
105     pidStat->comm.erase(pidStat->comm.end() - 1);
106 
107     int64_t systemCpuTime = 0;
108     if (fields.size() < 22 + commEndOffset ||
109         !ParseUint(fields[11 + commEndOffset], &pidStat->majorFaults) ||
110         !ParseInt(fields[13 + commEndOffset], &pidStat->cpuTimeMillis) ||
111         !ParseInt(fields[14 + commEndOffset], &systemCpuTime) ||
112         !ParseInt(fields[21 + commEndOffset], &pidStat->startTimeMillis)) {
113         ALOGD("Invalid proc pid stat contents: \"%s\"", line.c_str());
114         return false;
115     }
116     pidStat->cpuTimeMillis += systemCpuTime;
117     pidStat->state = fields[2 + commEndOffset];
118     return true;
119 }
120 
readPidStatFile(const std::string & path,int32_t millisPerClockTick)121 Result<PidStat> readPidStatFile(const std::string& path, int32_t millisPerClockTick) {
122     std::string buffer;
123     if (!ReadFileToString(path, &buffer)) {
124         return Error(ERR_FILE_OPEN_READ) << "ReadFileToString failed for " << path;
125     }
126     std::vector<std::string> lines = Split(std::move(buffer), "\n");
127     if (lines.size() != 1 && (lines.size() != 2 || !lines[1].empty())) {
128         return Error(ERR_INVALID_FILE) << path << " contains " << lines.size() << " lines != 1";
129     }
130     PidStat pidStat;
131     if (!parsePidStatLine(std::move(lines[0]), &pidStat)) {
132         return Error(ERR_INVALID_FILE) << "Failed to parse the contents of " << path;
133     }
134     pidStat.startTimeMillis = pidStat.startTimeMillis * millisPerClockTick;
135     pidStat.cpuTimeMillis = pidStat.cpuTimeMillis * millisPerClockTick;
136     return pidStat;
137 }
138 
getLinesWithTags(std::string_view buffer,const std::vector<std::string> & tags)139 std::vector<std::string> getLinesWithTags(std::string_view buffer,
140                                           const std::vector<std::string>& tags) {
141     std::vector<std::string> result;
142     std::vector<std::string> notFoundTags(tags);
143     size_t base = 0;
144     std::string_view sub;
145     for (size_t found = 0; !notFoundTags.empty() && found != buffer.npos;) {
146         found = buffer.find_first_of('\n', base);
147         sub = buffer.substr(base, found - base);
148         bool hasTag = false;
149         for (auto it = notFoundTags.begin(); it != notFoundTags.end();) {
150             if (sub.find(*it) != sub.npos) {
151                 notFoundTags.erase(it);
152                 hasTag = true;
153             } else {
154                 ++it;
155             }
156         }
157         if (hasTag) {
158             result.push_back(std::string{sub});
159         }
160         base = found + 1;
161     }
162     return result;
163 }
164 
readKeyValueFile(const std::string & path,const std::string & delimiter,const std::vector<std::string> & tags)165 Result<std::unordered_map<std::string, std::string>> readKeyValueFile(
166         const std::string& path, const std::string& delimiter,
167         const std::vector<std::string>& tags) {
168     std::string buffer;
169     if (!ReadFileToString(path, &buffer)) {
170         return Error(ERR_FILE_OPEN_READ) << "ReadFileToString failed for " << path;
171     }
172     std::vector<std::string> lines = getLinesWithTags(buffer, tags);
173     std::unordered_map<std::string, std::string> contents;
174     for (size_t i = 0; i < lines.size(); ++i) {
175         if (lines[i].empty()) {
176             continue;
177         }
178         std::vector<std::string> elements = Split(lines[i], delimiter);
179         if (elements.size() < 2) {
180             return Error(ERR_INVALID_FILE)
181                     << "Line \"" << lines[i] << "\" doesn't contain the delimiter \"" << delimiter
182                     << "\" in file " << path;
183         }
184         std::string key = elements[0];
185         std::string value = Trim(lines[i].substr(key.length() + delimiter.length()));
186         if (contents.find(key) != contents.end()) {
187             return Error(ERR_INVALID_FILE)
188                     << "Duplicate " << key << " line: \"" << lines[i] << "\" in file " << path;
189         }
190         contents[key] = value;
191     }
192     return contents;
193 }
194 
195 /**
196  * Returns UID and TGID from the given pid status file.
197  *
198  * /proc/PID/status file format:
199  * Tgid:    <Thread group ID of the process>
200  * Uid:     <Read UID>   <Effective UID>   <Saved set UID>   <Filesystem UID>
201  *
202  * Note: Included only the fields that are parsed from the file.
203  */
readPidStatusFile(const std::string & path)204 Result<std::tuple<uid_t, pid_t>> readPidStatusFile(const std::string& path) {
205     auto result = readKeyValueFile(path, ":\t", {"Uid", "Tgid"});
206     if (!result.ok()) {
207         return Error(result.error().code()) << result.error();
208     }
209     auto contents = result.value();
210     if (contents.empty()) {
211         return Error(ERR_INVALID_FILE) << "Empty file " << path;
212     }
213     int64_t uid = 0;
214     int64_t tgid = 0;
215     if (contents.find("Uid") == contents.end() ||
216         !ParseInt(Split(contents["Uid"], "\t")[0], &uid)) {
217         return Error(ERR_INVALID_FILE) << "Failed to read 'UID' from file " << path;
218     }
219     if (contents.find("Tgid") == contents.end() || !ParseInt(contents["Tgid"], &tgid)) {
220         return Error(ERR_INVALID_FILE) << "Failed to read 'Tgid' from file " << path;
221     }
222     return std::make_tuple(uid, tgid);
223 }
224 
225 /**
226  * Returns the total CPU cycles from the given time_in_state file.
227  *
228  * /proc/PID/task/TID/time_in_state file format:
229  * cpuX
230  * <CPU freq (kHz)> <time spent at freq (clock ticks)>
231  * <CPU freq (kHz)> <time spent at freq (clock ticks)>
232  * ...
233  * cpuY
234  * <CPU freq (kHz)> <time spent at freq (clock ticks)>
235  * <CPU freq (kHz)> <time spent at freq (clock ticks)>
236  * ...
237  *
238  * Note: Each 'cpuX' header refers to a particular CPU freq policy. A policy can contain multiple
239  * cores. Since we gather the time spent at a frequency at the thread level, their is no need
240  * aggregate the time across cores because threads only run in one core at a time.
241  */
readTimeInStateFile(const std::string & path)242 Result<uint64_t> readTimeInStateFile(const std::string& path) {
243     const auto mul = [](const uint64_t& l, const uint64_t& r) -> uint64_t {
244         if (l == 0 || r == 0) {
245             return 0;
246         }
247         return (kMaxUint64 / r) > l ? (l * r) : kMaxUint64;
248     };
249 
250     std::string buffer;
251     if (!ReadFileToString(path, &buffer)) {
252         return Error(ERR_FILE_OPEN_READ) << "ReadFileToString failed for " << path;
253     }
254     std::string delimiter = " ";
255     uint64_t oneTenthCpuCycles = 0;
256     const uint64_t cyclesPerKHzClockTicks = 1000 / sysconf(_SC_CLK_TCK);
257     std::vector<std::string> lines = Split(buffer, "\n");
258     for (size_t i = 0; i < lines.size(); ++i) {
259         if (lines[i].empty() || StartsWith(lines[i], "cpu")) {
260             continue;
261         }
262         std::vector<std::string> elements = Split(lines[i], delimiter);
263         if (elements.size() < 2) {
264             return Error(ERR_INVALID_FILE)
265                     << "Line \"" << lines[i] << "\" doesn't contain the delimiter \"" << delimiter
266                     << "\" in file " << path;
267         }
268         uint64_t freqKHz;
269         uint64_t clockTicks;
270         if (!ParseUint(elements[0], &freqKHz) || !ParseUint(Trim(elements[1]), &clockTicks)) {
271             return Error(ERR_INVALID_FILE)
272                     << "Line \"" << lines[i] << "\" has invalid format in file " << path;
273         }
274         oneTenthCpuCycles = addUint64(oneTenthCpuCycles, mul(freqKHz, clockTicks));
275     }
276     // The frequency is in kHz and the time is in clock ticks (10ms). In order to obtain cycles,
277     // one has to scale the frequency by 1000 to obtain Hz and the time by 1/sysconf(_SC_CLK_TCK)
278     // to obtain seconds which results in scaling the result by |cyclesPerKHzClockTicks|.
279     return mul(oneTenthCpuCycles, cyclesPerKHzClockTicks);
280 }
281 
282 }  // namespace
283 
toString() const284 std::string ProcessStats::toString() const {
285     std::string buffer;
286     StringAppendF(&buffer,
287                   "{comm: %s, startTimeMillis: %" PRIu64 ", cpuTimeMillis: %" PRIu64
288                   ", totalCpuCycles: %" PRIu64 ", totalMajorFaults: %" PRIu64
289                   ", totalTasksCount: %d, ioBlockedTasksCount: %d, cpuCyclesByTid: {",
290                   comm.c_str(), startTimeMillis, cpuTimeMillis, totalCpuCycles, totalMajorFaults,
291                   totalTasksCount, ioBlockedTasksCount);
292     for (const auto& [tid, cpuCycles] : cpuCyclesByTid) {
293         StringAppendF(&buffer, "{tid: %d, cpuCycles: %" PRIu64 "},", tid, cpuCycles);
294     }
295     StringAppendF(&buffer, "}");
296     return buffer;
297 }
298 
toString() const299 std::string UidProcStats::toString() const {
300     std::string buffer;
301     StringAppendF(&buffer,
302                   "UidProcStats{cpuTimeMillis: %" PRIu64 ", cpuCycles: %" PRIu64
303                   ", totalMajorFaults: %" PRIu64 ", totalTasksCount: %d, ioBlockedTasksCount: %d"
304                   ", processStatsByPid: {",
305                   cpuTimeMillis, cpuCycles, totalMajorFaults, totalTasksCount, ioBlockedTasksCount);
306     for (const auto& [pid, processStats] : processStatsByPid) {
307         StringAppendF(&buffer, "{pid: %" PRIi32 ", processStats: %s},", pid,
308                       processStats.toString().c_str());
309     }
310     StringAppendF(&buffer, "}");
311     return buffer;
312 }
313 
init()314 void UidProcStatsCollector::init() {
315     // Note: Verify proc file access outside the constructor. Otherwise, the unittests of
316     // dependent classes would call the constructor before mocking and get killed due to
317     // sepolicy violation.
318     std::string pidStatPath = StringPrintf((mPath + kStatFileFormat).c_str(), PID_FOR_INIT);
319     std::string tidStatPath = StringPrintf((mPath + kTaskDirFormat + kStatFileFormat).c_str(),
320                                            PID_FOR_INIT, PID_FOR_INIT);
321     std::string pidStatusPath = StringPrintf((mPath + kStatusFileFormat).c_str(), PID_FOR_INIT);
322     std::string tidTimeInStatePath =
323             StringPrintf((mPath + kTaskDirFormat + kTimeInStateFormat).c_str(), PID_FOR_INIT,
324                          PID_FOR_INIT);
325 
326     Mutex::Autolock lock(mMutex);
327     mEnabled = access(pidStatPath.c_str(), R_OK) == 0 && access(tidStatPath.c_str(), R_OK) == 0 &&
328             access(pidStatusPath.c_str(), R_OK) == 0;
329 
330     mTimeInStateEnabled = access(tidTimeInStatePath.c_str(), R_OK) == 0;
331     if (mTimeInStateEnabled) {
332         auto tidCpuCycles = readTimeInStateFile(tidTimeInStatePath);
333         mTimeInStateEnabled = tidCpuCycles.ok() && *tidCpuCycles > 0;
334     }
335     if (!mTimeInStateEnabled) {
336         ALOGW("Time in state files are not enabled. Missing time in state file at path: %s",
337               tidTimeInStatePath.c_str());
338     }
339 }
340 
collect()341 Result<void> UidProcStatsCollector::collect() {
342     if (!mEnabled) {
343         return Error() << "Can not access PID stat files under " << kProcDirPath;
344     }
345 
346     Mutex::Autolock lock(mMutex);
347     auto uidProcStatsByUid = readUidProcStatsLocked();
348     if (!uidProcStatsByUid.ok()) {
349         return Error() << uidProcStatsByUid.error();
350     }
351 
352     mDeltaStats.clear();
353     for (const auto& [uid, currUidStats] : *uidProcStatsByUid) {
354         if (const auto& it = mLatestStats.find(uid); it == mLatestStats.end()) {
355             mDeltaStats[uid] = currUidStats;
356             continue;
357         }
358         const auto& prevUidStats = mLatestStats[uid];
359         UidProcStats deltaUidStats = {
360                 .totalTasksCount = currUidStats.totalTasksCount,
361                 .ioBlockedTasksCount = currUidStats.ioBlockedTasksCount,
362         };
363         // Generate the delta stats since the previous collection. Delta stats are generated by
364         // calculating the difference between the |prevUidStats| and the |currUidStats|.
365         for (const auto& [pid, processStats] : currUidStats.processStatsByPid) {
366             ProcessStats deltaProcessStats = processStats;
367             if (const auto& it = prevUidStats.processStatsByPid.find(pid);
368                 it != prevUidStats.processStatsByPid.end() &&
369                 it->second.startTimeMillis == deltaProcessStats.startTimeMillis) {
370                 auto prevProcessStats = it->second;
371                 if (prevProcessStats.cpuTimeMillis <= deltaProcessStats.cpuTimeMillis) {
372                     deltaProcessStats.cpuTimeMillis -= prevProcessStats.cpuTimeMillis;
373                 }
374                 if (prevProcessStats.totalMajorFaults <= deltaProcessStats.totalMajorFaults) {
375                     deltaProcessStats.totalMajorFaults -= prevProcessStats.totalMajorFaults;
376                 }
377                 // Generate the process delta CPU cycles by iterating through the thread-level CPU
378                 // cycles and calculating the sum of the deltas of each thread.
379                 deltaProcessStats.totalCpuCycles = 0;
380                 for (const auto& [tid, threadCpuCycles] : processStats.cpuCyclesByTid) {
381                     uint64_t deltaThreadCpuCycles = threadCpuCycles;
382                     if (const auto& cIt = prevProcessStats.cpuCyclesByTid.find(tid);
383                         cIt != prevProcessStats.cpuCyclesByTid.end() &&
384                         cIt->second <= deltaThreadCpuCycles) {
385                         deltaThreadCpuCycles -= cIt->second;
386                     }
387                     deltaProcessStats.cpuCyclesByTid[tid] = deltaThreadCpuCycles;
388                     deltaProcessStats.totalCpuCycles =
389                             addUint64(deltaProcessStats.totalCpuCycles, deltaThreadCpuCycles);
390                 }
391             }
392             deltaUidStats.cpuTimeMillis += deltaProcessStats.cpuTimeMillis;
393             deltaUidStats.cpuCycles =
394                     addUint64(deltaUidStats.cpuCycles, deltaProcessStats.totalCpuCycles);
395             deltaUidStats.totalMajorFaults += deltaProcessStats.totalMajorFaults;
396             deltaUidStats.processStatsByPid[pid] = deltaProcessStats;
397         }
398         mDeltaStats[uid] = std::move(deltaUidStats);
399     }
400     mLatestStats = std::move(*uidProcStatsByUid);
401     return {};
402 }
403 
readUidProcStatsLocked() const404 Result<std::unordered_map<uid_t, UidProcStats>> UidProcStatsCollector::readUidProcStatsLocked()
405         const {
406     std::unordered_map<uid_t, UidProcStats> uidProcStatsByUid;
407     auto procDirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(mPath.c_str()), closedir);
408     if (!procDirp) {
409         return Error() << "Failed to open " << mPath << " directory";
410     }
411     for (dirent* pidDir = nullptr; (pidDir = readdir(procDirp.get())) != nullptr;) {
412         pid_t pid = 0;
413         if (pidDir->d_type != DT_DIR || !ParseInt(pidDir->d_name, &pid)) {
414             continue;
415         }
416         auto result = readProcessStatsLocked(pid);
417         if (!result.ok()) {
418             if (result.error().code() != ERR_FILE_OPEN_READ) {
419                 return Error() << result.error();
420             }
421             /* |ERR_FILE_OPEN_READ| is a soft-error because PID may disappear between scanning and
422              * reading directory/files.
423              */
424             if (DEBUG) {
425                 ALOGD("%s", result.error().message().c_str());
426             }
427             continue;
428         }
429         uid_t uid = std::get<0>(*result);
430         ProcessStats processStats = std::get<ProcessStats>(*result);
431         if (uidProcStatsByUid.find(uid) == uidProcStatsByUid.end()) {
432             uidProcStatsByUid[uid] = {};
433         }
434         UidProcStats* uidProcStats = &uidProcStatsByUid[uid];
435         uidProcStats->cpuTimeMillis += processStats.cpuTimeMillis;
436         uidProcStats->cpuCycles = addUint64(uidProcStats->cpuCycles, processStats.totalCpuCycles);
437         uidProcStats->totalMajorFaults += processStats.totalMajorFaults;
438         uidProcStats->totalTasksCount += processStats.totalTasksCount;
439         uidProcStats->ioBlockedTasksCount += processStats.ioBlockedTasksCount;
440         uidProcStats->processStatsByPid[pid] = std::move(processStats);
441     }
442     return uidProcStatsByUid;
443 }
444 
readProcessStatsLocked(pid_t pid) const445 Result<std::tuple<uid_t, ProcessStats>> UidProcStatsCollector::readProcessStatsLocked(
446         pid_t pid) const {
447     // 1. Read top-level pid stats.
448     std::string path = StringPrintf((mPath + kStatFileFormat).c_str(), pid);
449     auto pidStat = readPidStatFile(path, mMillisPerClockTick);
450     if (!pidStat.ok()) {
451         return Error(pidStat.error().code())
452                 << "Failed to read top-level per-process stat file '%s': %s"
453                 << pidStat.error().message().c_str();
454     }
455 
456     // 2. Read aggregated process status.
457     pid_t tgid = -1;
458     uid_t uid = -1;
459     path = StringPrintf((mPath + kStatusFileFormat).c_str(), pid);
460     if (auto result = readPidStatusFile(path); !result.ok()) {
461         if (result.error().code() != ERR_FILE_OPEN_READ) {
462             return Error() << "Failed to read pid status for pid " << pid << ": "
463                            << result.error().message().c_str();
464         }
465         for (const auto& [curUid, uidProcStats] : mLatestStats) {
466             if (const auto it = uidProcStats.processStatsByPid.find(pid);
467                 it != uidProcStats.processStatsByPid.end() &&
468                 it->second.startTimeMillis == pidStat->startTimeMillis) {
469                 tgid = pid;
470                 uid = curUid;
471                 break;
472             }
473         }
474     } else {
475         uid = std::get<0>(*result);
476         tgid = std::get<1>(*result);
477     }
478 
479     if (uid == static_cast<uid_t>(-1) || tgid != pid) {
480         return Error(ERR_FILE_OPEN_READ)
481                 << "Skipping PID '" << pid << "' because either Tgid != PID or invalid UID";
482     }
483 
484     ProcessStats processStats = {
485             .comm = std::move(pidStat->comm),
486             .startTimeMillis = pidStat->startTimeMillis,
487             .cpuTimeMillis = pidStat->cpuTimeMillis,
488             .totalCpuCycles = 0,
489             .totalTasksCount = 1,
490             /* Top-level process stats has the aggregated major page faults count and this should be
491              * persistent across thread creation/termination. Thus use the value from this field.
492              */
493             .totalMajorFaults = pidStat->majorFaults,
494             .ioBlockedTasksCount = pidStat->state == "D" ? 1 : 0,
495             .cpuCyclesByTid = {},
496     };
497 
498     // 3. Read per-thread stats.
499     std::string taskDir = StringPrintf((mPath + kTaskDirFormat).c_str(), pid);
500     bool didReadMainThread = false;
501     auto taskDirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(taskDir.c_str()), closedir);
502     for (dirent* tidDir = nullptr;
503          taskDirp != nullptr && (tidDir = readdir(taskDirp.get())) != nullptr;) {
504         pid_t tid = 0;
505         if (tidDir->d_type != DT_DIR || !ParseInt(tidDir->d_name, &tid)) {
506             continue;
507         }
508 
509         if (tid != pid) {
510             path = StringPrintf((taskDir + kStatFileFormat).c_str(), tid);
511             auto tidStat = readPidStatFile(path, mMillisPerClockTick);
512             if (!tidStat.ok()) {
513                 if (tidStat.error().code() != ERR_FILE_OPEN_READ) {
514                     return Error() << "Failed to read per-thread stat file: "
515                                    << tidStat.error().message().c_str();
516                 }
517                 /* Maybe the thread terminated before reading the file so skip this thread and
518                  * continue with scanning the next thread's stat.
519                  */
520                 continue;
521             }
522 
523             processStats.ioBlockedTasksCount += tidStat->state == "D" ? 1 : 0;
524             processStats.totalTasksCount += 1;
525         }
526 
527         if (!mTimeInStateEnabled) {
528             continue;
529         }
530 
531         path = StringPrintf((taskDir + kTimeInStateFormat).c_str(), tid);
532         auto tidCpuCycles = readTimeInStateFile(path);
533         if (!tidCpuCycles.ok() || *tidCpuCycles <= 0) {
534             if (!tidCpuCycles.ok() && tidCpuCycles.error().code() != ERR_FILE_OPEN_READ) {
535                 return Error() << "Failed to read per-thread time_in_state file: "
536                                << tidCpuCycles.error().message().c_str();
537             }
538             // time_in_state file might not be supported by the Kernel (when the Kernel configs
539             // CPU_FREQ_STAT or CPU_FREQ_TIMES are not be enabled or the governor doesn't report the
540             // CPU transition states to the Kernel CPU frequency node). Or non-positive CPU cycles
541             // calculated. Or maybe the thread terminated before reading the file so skip this
542             // thread and continue with scanning the next thread's stat.
543             continue;
544         }
545 
546         processStats.totalCpuCycles = addUint64(processStats.totalCpuCycles, *tidCpuCycles);
547         processStats.cpuCyclesByTid[tid] = *tidCpuCycles;
548     }
549     return std::make_tuple(uid, processStats);
550 }
551 
readStatFileForPid(pid_t pid)552 Result<PidStat> UidProcStatsCollector::readStatFileForPid(pid_t pid) {
553     std::string path = StringPrintf(kProcPidStatFileFormat, pid);
554     return readPidStatFile(path, 1000 / sysconf(_SC_CLK_TCK));
555 }
556 
readPidStatusFileForPid(pid_t pid)557 Result<std::tuple<uid_t, pid_t>> UidProcStatsCollector::readPidStatusFileForPid(pid_t pid) {
558     std::string path = StringPrintf(kProcPidStatusFileFormat, pid);
559     return readPidStatusFile(path);
560 }
561 
562 }  // namespace watchdog
563 }  // namespace automotive
564 }  // namespace android
565