1 /*
2 * Copyright (C) 2019 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 "WakeLockEntryList.h"
18
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21
22 #include <iomanip>
23
24 using android::base::ReadFdToString;
25
26 namespace android {
27 namespace system {
28 namespace suspend {
29 namespace V1_0 {
30
operator <<(std::ostream & out,const WakeLockInfo & entry)31 static std::ostream& operator<<(std::ostream& out, const WakeLockInfo& entry) {
32 const char* sep = " | ";
33 const char* notApplicable = "---";
34 bool kernelWakelock = entry.isKernelWakelock;
35
36 // clang-format off
37 out << sep
38 << std::left << std::setw(30) << entry.name << sep
39 << std::right << std::setw(6)
40 << ((kernelWakelock) ? notApplicable : std::to_string(entry.pid)) << sep
41 << std::left << std::setw(6) << ((kernelWakelock) ? "Kernel" : "Native") << sep
42 << std::left << std::setw(8) << ((entry.isActive) ? "Active" : "Inactive") << sep
43 << std::right << std::setw(12) << entry.activeCount << sep
44 << std::right << std::setw(12) << std::to_string(entry.totalTime) + "ms" << sep
45 << std::right << std::setw(12) << std::to_string(entry.maxTime) + "ms" << sep
46 << std::right << std::setw(12)
47 << ((kernelWakelock) ? std::to_string(entry.eventCount) : notApplicable) << sep
48 << std::right << std::setw(12)
49 << ((kernelWakelock) ? std::to_string(entry.wakeupCount) : notApplicable) << sep
50 << std::right << std::setw(12)
51 << ((kernelWakelock) ? std::to_string(entry.expireCount) : notApplicable) << sep
52 << std::right << std::setw(20)
53 << ((kernelWakelock) ? std::to_string(entry.preventSuspendTime) + "ms" : notApplicable)
54 << sep
55 << std::right << std::setw(16) << std::to_string(entry.lastChange) + "ms" << sep;
56 // clang-format on
57
58 return out;
59 }
60
operator <<(std::ostream & out,const WakeLockEntryList & list)61 std::ostream& operator<<(std::ostream& out, const WakeLockEntryList& list) {
62 std::vector<WakeLockInfo> wlStats;
63 list.getWakeLockStats(&wlStats);
64 int width = 194;
65 const char* sep = " | ";
66 std::stringstream ss;
67 ss << " " << std::setfill('-') << std::setw(width) << "\n";
68 std::string div = ss.str();
69
70 out << div;
71
72 std::stringstream header;
73 header << sep << std::right << std::setw(((width - 14) / 2) + 14) << "WAKELOCK STATS"
74 << std::right << std::setw((width - 14) / 2) << sep << "\n";
75 out << header.str();
76
77 out << div;
78
79 // Col names
80 // clang-format off
81 out << sep
82 << std::left << std::setw(30) << "NAME" << sep
83 << std::left << std::setw(6) << "PID" << sep
84 << std::left << std::setw(6) << "TYPE" << sep
85 << std::left << std::setw(8) << "STATUS" << sep
86 << std::left << std::setw(12) << "ACTIVE COUNT" << sep
87 << std::left << std::setw(12) << "TOTAL TIME" << sep
88 << std::left << std::setw(12) << "MAX TIME" << sep
89 << std::left << std::setw(12) << "EVENT COUNT" << sep
90 << std::left << std::setw(12) << "WAKEUP COUNT" << sep
91 << std::left << std::setw(12) << "EXPIRE COUNT" << sep
92 << std::left << std::setw(20) << "PREVENT SUSPEND TIME" << sep
93 << std::left << std::setw(16) << "LAST CHANGE" << sep
94 << "\n";
95 // clang-format on
96
97 out << div;
98
99 // Rows
100 for (const WakeLockInfo& entry : wlStats) {
101 out << entry << "\n";
102 }
103
104 out << div;
105 return out;
106 }
107
108 /**
109 * Returns the monotonic time in milliseconds.
110 */
getTimeNow()111 TimestampType getTimeNow() {
112 timespec monotime;
113 clock_gettime(CLOCK_MONOTONIC, &monotime);
114 return std::chrono::duration_cast<std::chrono::milliseconds>(
115 std::chrono::nanoseconds{monotime.tv_nsec})
116 .count() +
117 std::chrono::duration_cast<std::chrono::milliseconds>(
118 std::chrono::seconds{monotime.tv_sec})
119 .count();
120 }
121
WakeLockEntryList(size_t capacity,unique_fd kernelWakelockStatsFd)122 WakeLockEntryList::WakeLockEntryList(size_t capacity, unique_fd kernelWakelockStatsFd)
123 : mCapacity(capacity), mKernelWakelockStatsFd(std::move(kernelWakelockStatsFd)) {}
124
125 /**
126 * Evicts LRU from back of list if stats is at capacity.
127 */
evictIfFull()128 void WakeLockEntryList::evictIfFull() {
129 if (mStats.size() == mCapacity) {
130 auto evictIt = mStats.end();
131 std::advance(evictIt, -1);
132 auto evictKey = std::make_pair(evictIt->name, evictIt->pid);
133 mLookupTable.erase(evictKey);
134 mStats.erase(evictIt);
135 LOG(ERROR) << "WakeLock Stats: Stats capacity met, consider adjusting capacity to "
136 "avoid stats eviction.";
137 }
138 }
139
140 /**
141 * Inserts entry as MRU.
142 */
insertEntry(WakeLockInfo entry)143 void WakeLockEntryList::insertEntry(WakeLockInfo entry) {
144 auto key = std::make_pair(entry.name, entry.pid);
145 mStats.emplace_front(std::move(entry));
146 mLookupTable[key] = mStats.begin();
147 }
148
149 /**
150 * Removes entry from the stats list.
151 */
deleteEntry(std::list<WakeLockInfo>::iterator entry)152 void WakeLockEntryList::deleteEntry(std::list<WakeLockInfo>::iterator entry) {
153 auto key = std::make_pair(entry->name, entry->pid);
154 mLookupTable.erase(key);
155 mStats.erase(entry);
156 }
157
158 /**
159 * Creates and returns a native wakelock entry.
160 */
createNativeEntry(const std::string & name,int pid,TimestampType timeNow) const161 WakeLockInfo WakeLockEntryList::createNativeEntry(const std::string& name, int pid,
162 TimestampType timeNow) const {
163 WakeLockInfo info;
164
165 info.name = name;
166 // It only makes sense to create a new entry on initial activation of the lock.
167 info.activeCount = 1;
168 info.lastChange = timeNow;
169 info.maxTime = 0;
170 info.totalTime = 0;
171 info.isActive = true;
172 info.activeTime = 0;
173 info.isKernelWakelock = false;
174
175 info.pid = pid;
176
177 info.eventCount = 0;
178 info.expireCount = 0;
179 info.preventSuspendTime = 0;
180 info.wakeupCount = 0;
181
182 return info;
183 }
184
185 /*
186 * Checks whether a given directory entry is a stat file we're interested in.
187 */
isStatFile(const struct dirent * de)188 static bool isStatFile(const struct dirent* de) {
189 const char* statName = de->d_name;
190 if (!strcmp(statName, ".") || !strcmp(statName, "..") || !strcmp(statName, "device") ||
191 !strcmp(statName, "power") || !strcmp(statName, "subsystem") ||
192 !strcmp(statName, "uevent")) {
193 return false;
194 }
195 return true;
196 }
197
198 /*
199 * Creates and returns a kernel wakelock entry with data read from mKernelWakelockStatsFd
200 */
createKernelEntry(const std::string & kwlId) const201 WakeLockInfo WakeLockEntryList::createKernelEntry(const std::string& kwlId) const {
202 WakeLockInfo info;
203
204 info.activeCount = 0;
205 info.lastChange = 0;
206 info.maxTime = 0;
207 info.totalTime = 0;
208 info.isActive = false;
209 info.activeTime = 0;
210 info.isKernelWakelock = true;
211
212 info.pid = -1; // N/A
213
214 info.eventCount = 0;
215 info.expireCount = 0;
216 info.preventSuspendTime = 0;
217 info.wakeupCount = 0;
218
219 unique_fd wakelockFd{TEMP_FAILURE_RETRY(
220 openat(mKernelWakelockStatsFd, kwlId.c_str(), O_DIRECTORY | O_CLOEXEC | O_RDONLY))};
221 if (wakelockFd < 0) {
222 PLOG(ERROR) << "Error opening kernel wakelock stats for: " << kwlId;
223 }
224
225 std::unique_ptr<DIR, decltype(&closedir)> wakelockDp(fdopendir(dup(wakelockFd.get())),
226 &closedir);
227 if (wakelockDp) {
228 struct dirent* de;
229 while ((de = readdir(wakelockDp.get()))) {
230 if (!isStatFile(de)) {
231 continue;
232 }
233
234 std::string statName(de->d_name);
235 unique_fd statFd{
236 TEMP_FAILURE_RETRY(openat(wakelockFd, statName.c_str(), O_CLOEXEC | O_RDONLY))};
237 if (statFd < 0) {
238 PLOG(ERROR) << "Error opening " << statName << " for " << kwlId;
239 }
240
241 std::string valStr;
242 if (!ReadFdToString(statFd.get(), &valStr)) {
243 PLOG(ERROR) << "Error reading " << statName << " for " << kwlId;
244 continue;
245 }
246
247 // Trim newline
248 valStr.erase(std::remove(valStr.begin(), valStr.end(), '\n'), valStr.end());
249
250 if (statName == "name") {
251 info.name = valStr;
252 continue;
253 }
254
255 int64_t statVal = std::stoll(valStr);
256
257 if (statName == "active_count") {
258 info.activeCount = statVal;
259 } else if (statName == "active_time_ms") {
260 info.activeTime = statVal;
261 } else if (statName == "event_count") {
262 info.eventCount = statVal;
263 } else if (statName == "expire_count") {
264 info.expireCount = statVal;
265 } else if (statName == "last_change_ms") {
266 info.lastChange = statVal;
267 } else if (statName == "max_time_ms") {
268 info.maxTime = statVal;
269 } else if (statName == "prevent_suspend_time_ms") {
270 info.preventSuspendTime = statVal;
271 } else if (statName == "total_time_ms") {
272 info.totalTime = statVal;
273 } else if (statName == "wakeup_count") {
274 info.wakeupCount = statVal;
275 }
276 }
277 }
278
279 // Derived stats
280 info.isActive = info.activeTime > 0;
281
282 return info;
283 }
284
getKernelWakelockStats(std::vector<WakeLockInfo> * aidl_return) const285 void WakeLockEntryList::getKernelWakelockStats(std::vector<WakeLockInfo>* aidl_return) const {
286 std::unique_ptr<DIR, decltype(&closedir)> dp(fdopendir(dup(mKernelWakelockStatsFd.get())),
287 &closedir);
288 if (dp) {
289 // rewinddir, else subsequent calls will not get any kernel wakelocks.
290 rewinddir(dp.get());
291
292 struct dirent* de;
293 while ((de = readdir(dp.get()))) {
294 std::string kwlId(de->d_name);
295 if ((kwlId == ".") || (kwlId == "..")) {
296 continue;
297 }
298 WakeLockInfo entry = createKernelEntry(kwlId);
299 aidl_return->emplace_back(std::move(entry));
300 }
301 }
302 }
303
updateOnAcquire(const std::string & name,int pid,TimestampType timeNow)304 void WakeLockEntryList::updateOnAcquire(const std::string& name, int pid, TimestampType timeNow) {
305 std::lock_guard<std::mutex> lock(mStatsLock);
306
307 auto key = std::make_pair(name, pid);
308 auto it = mLookupTable.find(key);
309 if (it == mLookupTable.end()) {
310 evictIfFull();
311 WakeLockInfo newEntry = createNativeEntry(name, pid, timeNow);
312 insertEntry(newEntry);
313 } else {
314 auto staleEntry = it->second;
315 WakeLockInfo updatedEntry = *staleEntry;
316
317 // Update entry
318 updatedEntry.isActive = true;
319 updatedEntry.activeTime = 0;
320 updatedEntry.activeCount++;
321 updatedEntry.lastChange = timeNow;
322
323 deleteEntry(staleEntry);
324 insertEntry(std::move(updatedEntry));
325 }
326 }
327
updateOnRelease(const std::string & name,int pid,TimestampType timeNow)328 void WakeLockEntryList::updateOnRelease(const std::string& name, int pid, TimestampType timeNow) {
329 std::lock_guard<std::mutex> lock(mStatsLock);
330
331 auto key = std::make_pair(name, pid);
332 auto it = mLookupTable.find(key);
333 if (it == mLookupTable.end()) {
334 LOG(INFO) << "WakeLock Stats: A stats entry for, \"" << name
335 << "\" was not found. This is most likely due to it being evicted.";
336 } else {
337 auto staleEntry = it->second;
338 WakeLockInfo updatedEntry = *staleEntry;
339
340 // Update entry
341 TimestampType timeDelta = timeNow - updatedEntry.lastChange;
342 updatedEntry.isActive = false;
343 updatedEntry.activeTime += timeDelta;
344 updatedEntry.maxTime = std::max(updatedEntry.maxTime, updatedEntry.activeTime);
345 updatedEntry.activeTime = 0; // No longer active
346 updatedEntry.totalTime += timeDelta;
347 updatedEntry.lastChange = timeNow;
348
349 deleteEntry(staleEntry);
350 insertEntry(std::move(updatedEntry));
351 }
352 }
353 /**
354 * Updates the native wakelock stats based on the current time.
355 */
updateNow()356 void WakeLockEntryList::updateNow() {
357 std::lock_guard<std::mutex> lock(mStatsLock);
358
359 TimestampType timeNow = getTimeNow();
360
361 for (std::list<WakeLockInfo>::iterator it = mStats.begin(); it != mStats.end(); ++it) {
362 if (it->isActive) {
363 TimestampType timeDelta = timeNow - it->lastChange;
364 it->activeTime += timeDelta;
365 it->maxTime = std::max(it->maxTime, it->activeTime);
366 it->totalTime += timeDelta;
367 it->lastChange = timeNow;
368 }
369 }
370 }
371
getWakeLockStats(std::vector<WakeLockInfo> * aidl_return) const372 void WakeLockEntryList::getWakeLockStats(std::vector<WakeLockInfo>* aidl_return) const {
373 // Under no circumstances should the lock be held while getting kernel wakelock stats
374 {
375 std::lock_guard<std::mutex> lock(mStatsLock);
376 for (const WakeLockInfo& entry : mStats) {
377 aidl_return->emplace_back(entry);
378 }
379 }
380 getKernelWakelockStats(aidl_return);
381 }
382
383 } // namespace V1_0
384 } // namespace suspend
385 } // namespace system
386 } // namespace android
387