system/hardware/interfaces
Revisión | 4e53a2961d1f92b8265f3455b9e8d3c22472c5d0 (tree) |
---|---|
Tiempo | 2018-12-08 08:17:52 |
Autor | Tri Vo <trong@goog...> |
Commiter | android-build-merger |
system suspend: more information about wake locks am: ba81fb29c4
am: 6cd53dfb0c
Change-Id: I450c0d4769d664fdef5ae383660db6f4497f3c84
@@ -27,6 +27,7 @@ | ||
27 | 27 | #include <sys/types.h> |
28 | 28 | |
29 | 29 | #include <fcntl.h> |
30 | +#include <ctime> | |
30 | 31 | #include <string> |
31 | 32 | #include <thread> |
32 | 33 |
@@ -57,7 +58,18 @@ static inline int getCallingPid() { | ||
57 | 58 | return IPCThreadState::self()->getCallingPid(); |
58 | 59 | } |
59 | 60 | |
60 | -WakeLock::WakeLock(SystemSuspend* systemSuspend) : mReleased(), mSystemSuspend(systemSuspend) { | |
61 | +static inline WakeLockIdType getWakeLockId(int pid, const string& name) { | |
62 | + // Doesn't guarantee unique ids, but for debuging purposes this is adequate. | |
63 | + return std::to_string(pid) + "/" + name; | |
64 | +} | |
65 | + | |
66 | +TimestampType getEpochTimeNow() { | |
67 | + auto timeSinceEpoch = std::chrono::system_clock::now().time_since_epoch(); | |
68 | + return std::chrono::duration_cast<std::chrono::microseconds>(timeSinceEpoch).count(); | |
69 | +} | |
70 | + | |
71 | +WakeLock::WakeLock(SystemSuspend* systemSuspend, const WakeLockIdType& id) | |
72 | + : mReleased(), mSystemSuspend(systemSuspend), mId(id) { | |
61 | 73 | mSystemSuspend->incSuspendCounter(); |
62 | 74 | } |
63 | 75 |
@@ -73,15 +85,16 @@ Return<void> WakeLock::release() { | ||
73 | 85 | void WakeLock::releaseOnce() { |
74 | 86 | std::call_once(mReleased, [this]() { |
75 | 87 | mSystemSuspend->decSuspendCounter(); |
76 | - mSystemSuspend->deleteWakeLockStatsEntry(reinterpret_cast<WakeLockIdType>(this)); | |
88 | + mSystemSuspend->deleteWakeLockStatsEntry(mId); | |
77 | 89 | }); |
78 | 90 | } |
79 | 91 | |
80 | -SystemSuspend::SystemSuspend(unique_fd wakeupCountFd, unique_fd stateFd, | |
92 | +SystemSuspend::SystemSuspend(unique_fd wakeupCountFd, unique_fd stateFd, size_t maxStatsEntries, | |
81 | 93 | std::chrono::milliseconds baseSleepTime) |
82 | 94 | : mSuspendCounter(0), |
83 | 95 | mWakeupCountFd(std::move(wakeupCountFd)), |
84 | 96 | mStateFd(std::move(stateFd)), |
97 | + mMaxStatsEntries(maxStatsEntries), | |
85 | 98 | mBaseSleepTime(baseSleepTime), |
86 | 99 | mSleepTime(baseSleepTime) {} |
87 | 100 |
@@ -99,14 +112,28 @@ Return<bool> SystemSuspend::enableAutosuspend() { | ||
99 | 112 | |
100 | 113 | Return<sp<IWakeLock>> SystemSuspend::acquireWakeLock(WakeLockType /* type */, |
101 | 114 | const hidl_string& name) { |
102 | - IWakeLock* wl = new WakeLock{this}; | |
115 | + auto pid = getCallingPid(); | |
116 | + auto wlId = getWakeLockId(pid, name); | |
117 | + IWakeLock* wl = new WakeLock{this, wlId}; | |
103 | 118 | { |
104 | 119 | auto l = std::lock_guard(mStatsLock); |
105 | - WakeLockStats wlStats{}; | |
106 | - wlStats.set_name(name); | |
107 | - wlStats.set_pid(getCallingPid()); | |
108 | - // Use WakeLock's address as a unique identifier. | |
109 | - (*mStats.mutable_wake_lock_stats())[reinterpret_cast<WakeLockIdType>(wl)] = wlStats; | |
120 | + | |
121 | + auto& wlStatsEntry = (*mStats.mutable_wl_stats())[wlId]; | |
122 | + auto lastUpdated = wlStatsEntry.last_updated(); | |
123 | + auto timeNow = getEpochTimeNow(); | |
124 | + mLruWakeLockId.erase(lastUpdated); | |
125 | + mLruWakeLockId[timeNow] = wlId; | |
126 | + | |
127 | + wlStatsEntry.set_name(name); | |
128 | + wlStatsEntry.set_pid(pid); | |
129 | + wlStatsEntry.set_active(true); | |
130 | + wlStatsEntry.set_last_updated(timeNow); | |
131 | + | |
132 | + if (mStats.wl_stats().size() > mMaxStatsEntries) { | |
133 | + auto lruWakeLockId = mLruWakeLockId.begin()->second; | |
134 | + mLruWakeLockId.erase(mLruWakeLockId.begin()); | |
135 | + mStats.mutable_wl_stats()->erase(lruWakeLockId); | |
136 | + } | |
110 | 137 | } |
111 | 138 | return wl; |
112 | 139 | } |
@@ -164,7 +191,16 @@ void SystemSuspend::decSuspendCounter() { | ||
164 | 191 | |
165 | 192 | void SystemSuspend::deleteWakeLockStatsEntry(WakeLockIdType id) { |
166 | 193 | auto l = std::lock_guard(mStatsLock); |
167 | - mStats.mutable_wake_lock_stats()->erase(id); | |
194 | + auto* wlStats = mStats.mutable_wl_stats(); | |
195 | + if (wlStats->find(id) != wlStats->end()) { | |
196 | + auto& wlStatsEntry = (*wlStats)[id]; | |
197 | + auto timeNow = getEpochTimeNow(); | |
198 | + auto lastUpdated = wlStatsEntry.last_updated(); | |
199 | + wlStatsEntry.set_active(false); | |
200 | + wlStatsEntry.set_last_updated(timeNow); | |
201 | + mLruWakeLockId.erase(lastUpdated); | |
202 | + mLruWakeLockId[timeNow] = id; | |
203 | + } | |
168 | 204 | } |
169 | 205 | |
170 | 206 | void SystemSuspend::initAutosuspend() { |
@@ -40,17 +40,19 @@ using ::android::hardware::hidl_vec; | ||
40 | 40 | using ::android::hardware::interfacesEqual; |
41 | 41 | using ::android::hardware::Return; |
42 | 42 | |
43 | -using WakeLockIdType = uint64_t; | |
43 | +using TimestampType = uint64_t; | |
44 | +using WakeLockIdType = std::string; | |
44 | 45 | |
45 | 46 | using namespace std::chrono_literals; |
46 | 47 | |
47 | 48 | class SystemSuspend; |
48 | 49 | |
49 | 50 | std::string readFd(int fd); |
51 | +TimestampType getEpochTimeNow(); | |
50 | 52 | |
51 | 53 | class WakeLock : public IWakeLock { |
52 | 54 | public: |
53 | - WakeLock(SystemSuspend* systemSuspend); | |
55 | + WakeLock(SystemSuspend* systemSuspend, const WakeLockIdType& id); | |
54 | 56 | ~WakeLock(); |
55 | 57 | |
56 | 58 | Return<void> release(); |
@@ -60,11 +62,12 @@ class WakeLock : public IWakeLock { | ||
60 | 62 | std::once_flag mReleased; |
61 | 63 | |
62 | 64 | SystemSuspend* mSystemSuspend; |
65 | + WakeLockIdType mId; | |
63 | 66 | }; |
64 | 67 | |
65 | 68 | class SystemSuspend : public ISystemSuspend, public hidl_death_recipient { |
66 | 69 | public: |
67 | - SystemSuspend(unique_fd wakeupCountFd, unique_fd stateFd, | |
70 | + SystemSuspend(unique_fd wakeupCountFd, unique_fd stateFd, size_t maxStatsEntries, | |
68 | 71 | std::chrono::milliseconds baseSleepTime); |
69 | 72 | Return<bool> enableAutosuspend() override; |
70 | 73 | Return<sp<IWakeLock>> acquireWakeLock(WakeLockType type, const hidl_string& name) override; |
@@ -88,6 +91,12 @@ class SystemSuspend : public ISystemSuspend, public hidl_death_recipient { | ||
88 | 91 | // protect these. However, since mStats is only for debugging we prioritize performance. |
89 | 92 | // Never hold both locks at the same time to avoid deadlock. |
90 | 93 | std::mutex mStatsLock; |
94 | + // We don't want mStats to grow unboundedly in memory. This constant limits amount of | |
95 | + // information mStats can collect on the device. | |
96 | + size_t mMaxStatsEntries; | |
97 | + // Used to evict the least recently used wake lock stats entry in case mMaxStatsEntries is | |
98 | + // reached. | |
99 | + std::map<TimestampType, WakeLockIdType> mLruWakeLockId; | |
91 | 100 | SystemSuspendStats mStats; |
92 | 101 | |
93 | 102 | using CbType = sp<ISystemSuspendCallback>; |
@@ -14,13 +14,15 @@ | ||
14 | 14 | |
15 | 15 | syntax = "proto3"; |
16 | 16 | |
17 | -// Collects information about individual WakeLock instances. | |
17 | +// Collects information about an individual live WakeLock instances. | |
18 | 18 | message WakeLockStats { |
19 | 19 | string name = 1; |
20 | 20 | int32 pid = 2; |
21 | + bool active = 3; | |
22 | + uint64 last_updated = 4; | |
21 | 23 | } |
22 | 24 | |
23 | 25 | message SystemSuspendStats { |
24 | 26 | // Maps a unique id of a WakeLock instance to the corresponding WakeLockStats message. |
25 | - map<uint64, WakeLockStats> wake_lock_stats = 1; | |
27 | + map<string, WakeLockStats> wl_stats = 1; | |
26 | 28 | } |
@@ -44,6 +44,7 @@ using android::hardware::configureRpcThreadpool; | ||
44 | 44 | using android::hardware::joinRpcThreadpool; |
45 | 45 | using android::hardware::Return; |
46 | 46 | using android::hardware::Void; |
47 | +using android::system::suspend::V1_0::getEpochTimeNow; | |
47 | 48 | using android::system::suspend::V1_0::ISystemSuspend; |
48 | 49 | using android::system::suspend::V1_0::ISystemSuspendCallback; |
49 | 50 | using android::system::suspend::V1_0::IWakeLock; |
@@ -80,8 +81,9 @@ class SystemSuspendTestEnvironment : public ::testing::Environment { | ||
80 | 81 | void registerTestService() { |
81 | 82 | std::thread testService([this] { |
82 | 83 | configureRpcThreadpool(1, true /* callerWillJoin */); |
83 | - sp<ISystemSuspend> suspend = new SystemSuspend( | |
84 | - std::move(wakeupCountFds[1]), std::move(stateFds[1]), 0ms /* baseSleepTime */); | |
84 | + sp<ISystemSuspend> suspend = | |
85 | + new SystemSuspend(std::move(wakeupCountFds[1]), std::move(stateFds[1]), | |
86 | + 1 /* maxStatsEntries */, 0ms /* baseSleepTime */); | |
85 | 87 | status_t status = suspend->registerAsService(kServiceName); |
86 | 88 | if (android::OK != status) { |
87 | 89 | LOG(FATAL) << "Unable to register service: " << status; |
@@ -155,7 +157,10 @@ class SystemSuspendTest : public ::testing::Test { | ||
155 | 157 | return stats; |
156 | 158 | } |
157 | 159 | |
158 | - size_t getWakeLockCount() { return getDebugDump().wake_lock_stats().size(); } | |
160 | + size_t getActiveWakeLockCount() { | |
161 | + const auto& wlStats = getDebugDump().wl_stats(); | |
162 | + return count_if(wlStats.begin(), wlStats.end(), [](auto x) { return x.second.active(); }); | |
163 | + } | |
159 | 164 | |
160 | 165 | void checkLoop(int numIter) { |
161 | 166 | for (int i = 0; i < numIter; i++) { |
@@ -248,15 +253,43 @@ TEST_F(SystemSuspendTest, CleanupOnAbort) { | ||
248 | 253 | ASSERT_FALSE(isSystemSuspendBlocked()); |
249 | 254 | } |
250 | 255 | |
251 | -// Test that debug dump has correct information about acquired WakeLocks. | |
252 | -TEST_F(SystemSuspendTest, DebugDump) { | |
256 | +// Test that debug dump has correct information about WakeLocks. | |
257 | +TEST_F(SystemSuspendTest, DebugDumpWakeLocks) { | |
258 | + uint64_t timeNow = getEpochTimeNow(); | |
253 | 259 | { |
254 | 260 | sp<IWakeLock> wl = acquireWakeLock(); |
255 | - SystemSuspendStats debugDump = getDebugDump(); | |
256 | - ASSERT_EQ(debugDump.wake_lock_stats().size(), 1); | |
257 | - ASSERT_EQ(debugDump.wake_lock_stats().begin()->second.name(), "TestLock"); | |
261 | + auto wlStats = getDebugDump().wl_stats(); | |
262 | + ASSERT_EQ(wlStats.size(), 1); | |
263 | + ASSERT_EQ(wlStats.begin()->second.name(), "TestLock"); | |
264 | + ASSERT_EQ(wlStats.begin()->second.pid(), getpid()); | |
265 | + ASSERT_EQ(wlStats.begin()->second.active(), true); | |
266 | + ASSERT_GT(wlStats.begin()->second.last_updated(), timeNow); | |
267 | + // We sleep so that the wake lock stats entry get updated with a different timestamp. | |
268 | + std::this_thread::sleep_for(1s); | |
258 | 269 | } |
259 | - ASSERT_EQ(getWakeLockCount(), 0); | |
270 | + auto wlStats = getDebugDump().wl_stats(); | |
271 | + ASSERT_EQ(wlStats.size(), 1); | |
272 | + ASSERT_EQ(wlStats.begin()->second.name(), "TestLock"); | |
273 | + ASSERT_EQ(wlStats.begin()->second.pid(), getpid()); | |
274 | + ASSERT_EQ(wlStats.begin()->second.active(), false); | |
275 | + // The updated timestamp is not deterministic. However, all SystemSuspend HAL calls run in the | |
276 | + // order of microseconds, so in practice the updated timestamp should be 1 second newer than the | |
277 | + // old one. | |
278 | + ASSERT_GT(wlStats.begin()->second.last_updated(), timeNow + 1000000); | |
279 | +} | |
280 | + | |
281 | +// Test that the least recently used wake stats entry is evicted after a given threshold. | |
282 | +TEST_F(SystemSuspendTest, LruWakeLockStatsEviction) { | |
283 | + suspendService->acquireWakeLock(WakeLockType::PARTIAL, "foo"); | |
284 | + suspendService->acquireWakeLock(WakeLockType::PARTIAL, "bar"); | |
285 | + suspendService->acquireWakeLock(WakeLockType::PARTIAL, "bar"); | |
286 | + suspendService->acquireWakeLock(WakeLockType::PARTIAL, "baz"); | |
287 | + | |
288 | + auto wlStats = getDebugDump().wl_stats(); | |
289 | + // Max number of stats entries was set to 1 in SystemSuspend constructor. | |
290 | + ASSERT_EQ(wlStats.size(), 1); | |
291 | + ASSERT_EQ(wlStats.begin()->second.name(), "baz"); | |
292 | + ASSERT_EQ(wlStats.begin()->second.active(), false); | |
260 | 293 | } |
261 | 294 | |
262 | 295 | // Stress test acquiring/releasing WakeLocks. |
@@ -268,7 +301,7 @@ TEST_F(SystemSuspendTest, WakeLockStressTest) { | ||
268 | 301 | |
269 | 302 | for (int i = 0; i < numThreads; i++) { |
270 | 303 | tds[i] = std::thread([this] { |
271 | - for (int i = 0; i < numLocks; i++) { | |
304 | + for (int j = 0; j < numLocks; j++) { | |
272 | 305 | sp<IWakeLock> wl1 = acquireWakeLock(); |
273 | 306 | sp<IWakeLock> wl2 = acquireWakeLock(); |
274 | 307 | wl2->release(); |
@@ -278,7 +311,7 @@ TEST_F(SystemSuspendTest, WakeLockStressTest) { | ||
278 | 311 | for (int i = 0; i < numThreads; i++) { |
279 | 312 | tds[i].join(); |
280 | 313 | } |
281 | - ASSERT_EQ(getWakeLockCount(), 0); | |
314 | + ASSERT_EQ(getActiveWakeLockCount(), 0); | |
282 | 315 | } |
283 | 316 | |
284 | 317 | // Callbacks are passed around as sp<>. However, mock expectations are verified when mock objects |
@@ -36,7 +36,8 @@ int main() { | ||
36 | 36 | |
37 | 37 | configureRpcThreadpool(1, true /* callerWillJoin */); |
38 | 38 | sp<ISystemSuspend> suspend = |
39 | - new SystemSuspend(std::move(wakeupCountFd), std::move(stateFd), 100ms /* baseSleepTime */); | |
39 | + new SystemSuspend(std::move(wakeupCountFd), std::move(stateFd), 100 /* maxStatsEntries */, | |
40 | + 100ms /* baseSleepTime */); | |
40 | 41 | status_t status = suspend->registerAsService(); |
41 | 42 | if (android::OK != status) { |
42 | 43 | LOG(FATAL) << "Unable to register service: " << status; |