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