• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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