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