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