1 /*
2 * Copyright (c) 2022, 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
19 #include "UidCpuStatsCollector.h"
20
21 #include <android-base/file.h>
22 #include <android-base/parseint.h>
23 #include <android-base/strings.h>
24
25 #include <inttypes.h>
26
27 #include <string>
28 #include <unordered_map>
29 #include <vector>
30
31 namespace android {
32 namespace automotive {
33 namespace watchdog {
34
35 namespace {
36
37 using ::android::base::EndsWith;
38 using ::android::base::Error;
39 using ::android::base::ParseInt;
40 using ::android::base::ReadFileToString;
41 using ::android::base::Result;
42 using ::android::base::Split;
43
44 enum class ReadError : int {
45 ERR_INVALID_FILE,
46 ERR_FILE_OPEN_READ,
47 NUM_ERRORS,
48 };
49
50 /**
51 * Returns a map of CPU time in milliseconds spent by each UID since system boot up.
52 *
53 * /proc/uid_cputime/show_uid_stat file format:
54 * <uid>: <user_time_micro_seconds> <system_time_micro_seconds>
55 */
readUidCpuTimeFile(const std::string & path)56 Result<std::unordered_map<uid_t, int64_t>> readUidCpuTimeFile(const std::string& path) {
57 std::string buffer;
58 if (!ReadFileToString(path, &buffer)) {
59 return Error(static_cast<int>(ReadError::ERR_FILE_OPEN_READ))
60 << "ReadFileToString failed for " << path;
61 }
62 std::unordered_map<uid_t, int64_t> cpuTimeMillisByUid;
63 std::vector<std::string> lines = Split(buffer, "\n");
64 for (size_t i = 0; i < lines.size(); i++) {
65 if (lines[i].empty()) {
66 continue;
67 }
68 const std::string delimiter = " ";
69 std::vector<std::string> elements = Split(lines[i], delimiter);
70 if (elements.size() < 3) {
71 return Error(static_cast<int>(ReadError::ERR_INVALID_FILE))
72 << "Line \"" << lines[i] << "\" doesn't contain the delimiter \"" << delimiter
73 << "\" in file " << path;
74 }
75 if (EndsWith(elements[0], ":")) {
76 elements[0].pop_back();
77 }
78 int64_t uid = -1;
79 int64_t userCpuTimeUs = 0;
80 int64_t systemCpuTimeUs = 0;
81 if (!ParseInt(elements[0], &uid) || !ParseInt(elements[1], &userCpuTimeUs) ||
82 !ParseInt(elements[2], &systemCpuTimeUs)) {
83 return Error(static_cast<int>(ReadError::ERR_INVALID_FILE))
84 << "Failed to parse line from file: " << path << ", error: line " << lines[i]
85 << " has invalid format";
86 }
87 if (cpuTimeMillisByUid.find(uid) != cpuTimeMillisByUid.end()) {
88 return Error(static_cast<int>(ReadError::ERR_INVALID_FILE))
89 << "Duplicate " << uid << " line: \"" << lines[i] << "\" in file " << path;
90 }
91 // Store CPU time as milliseconds
92 cpuTimeMillisByUid[uid] = userCpuTimeUs / 1000 + systemCpuTimeUs / 1000;
93 }
94 if (cpuTimeMillisByUid.empty()) {
95 return Error(static_cast<int>(ReadError::ERR_INVALID_FILE)) << "Empty file: " << path;
96 }
97 return cpuTimeMillisByUid;
98 }
99
100 } // namespace
101
collect()102 Result<void> UidCpuStatsCollector::collect() {
103 Mutex::Autolock lock(mMutex);
104 if (!mEnabled) {
105 return Error() << "Can not access: " << mPath;
106 }
107 auto cpuTimeMillisByUid = readUidCpuTimeFile(mPath);
108 if (!cpuTimeMillisByUid.ok()) {
109 return Error(cpuTimeMillisByUid.error().code())
110 << "Failed to read top-level per UID CPU time file " << mPath << ": "
111 << cpuTimeMillisByUid.error().message();
112 }
113
114 mDeltaStats.clear();
115 for (const auto& [uid, cpuTime] : *cpuTimeMillisByUid) {
116 if (cpuTime == 0) {
117 continue;
118 }
119 int64_t deltaCpuTime = cpuTime;
120 if (const auto& it = mLatestStats.find(uid);
121 it != mLatestStats.end() && it->second <= deltaCpuTime) {
122 deltaCpuTime -= it->second;
123 if (deltaCpuTime == 0) {
124 continue;
125 }
126 }
127 mDeltaStats[uid] = deltaCpuTime;
128 }
129 mLatestStats = std::move(*cpuTimeMillisByUid);
130 return {};
131 }
132
133 } // namespace watchdog
134 } // namespace automotive
135 } // namespace android
136