1 /* 2 * Copyright (C) 2015 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 package com.android.server.power.stats; 17 18 import android.os.Process; 19 import android.os.RemoteException; 20 import android.os.ServiceManager; 21 import android.os.ServiceManager.ServiceNotFoundException; 22 import android.os.StrictMode; 23 import android.os.SystemClock; 24 import android.system.suspend.internal.ISuspendControlServiceInternal; 25 import android.system.suspend.internal.WakeLockInfo; 26 import android.util.Slog; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 30 import java.io.File; 31 import java.io.FileInputStream; 32 import java.util.Arrays; 33 import java.util.Iterator; 34 35 /** 36 * Reads and parses wakelock stats from the kernel (/proc/wakelocks). 37 */ 38 public class KernelWakelockReader { 39 private static final String TAG = "KernelWakelockReader"; 40 private static int sKernelWakelockUpdateVersion = 0; 41 private static final String sWakelockFile = "/proc/wakelocks"; 42 private static final String sWakeupSourceFile = "/d/wakeup_sources"; 43 private static final String sSysClassWakeupDir = "/sys/class/wakeup"; 44 45 private static final int[] PROC_WAKELOCKS_FORMAT = new int[]{ 46 Process.PROC_TAB_TERM | Process.PROC_OUT_STRING // 0: name 47 | Process.PROC_QUOTES, 48 Process.PROC_TAB_TERM | Process.PROC_OUT_LONG, // 1: count 49 Process.PROC_TAB_TERM, 50 Process.PROC_TAB_TERM | Process.PROC_OUT_LONG, // 3: activeSince 51 Process.PROC_TAB_TERM, 52 Process.PROC_TAB_TERM | Process.PROC_OUT_LONG, // 5: totalTime 53 }; 54 55 private static final int[] WAKEUP_SOURCES_FORMAT = new int[]{ 56 Process.PROC_TAB_TERM | Process.PROC_OUT_STRING, // 0: name 57 Process.PROC_TAB_TERM | Process.PROC_COMBINE 58 | Process.PROC_OUT_LONG, // 1: count 59 Process.PROC_TAB_TERM | Process.PROC_COMBINE, 60 Process.PROC_TAB_TERM | Process.PROC_COMBINE, 61 Process.PROC_TAB_TERM | Process.PROC_COMBINE, 62 Process.PROC_TAB_TERM | Process.PROC_COMBINE 63 | Process.PROC_OUT_LONG, // 5: activeSince 64 Process.PROC_TAB_TERM | Process.PROC_COMBINE 65 | Process.PROC_OUT_LONG, // 6: totalTime 66 }; 67 68 private final String[] mProcWakelocksName = new String[3]; 69 private final long[] mProcWakelocksData = new long[4]; 70 private ISuspendControlServiceInternal mSuspendControlService = null; 71 private byte[] mKernelWakelockBuffer = new byte[32 * 1024]; 72 73 /** 74 * Reads kernel wakelock stats and updates the staleStats with the new information. 75 * @param staleStats Existing object to update. 76 * @return the updated data. 77 */ readKernelWakelockStats(KernelWakelockStats staleStats)78 public KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) { 79 boolean useSystemSuspend = (new File(sSysClassWakeupDir)).exists(); 80 81 if (useSystemSuspend) { 82 // static read/write lock protection for sKernelWakelockUpdateVersion 83 synchronized (KernelWakelockReader.class) { 84 // Get both kernel and native wakelock stats from SystemSuspend 85 updateVersion(staleStats); 86 if (getWakelockStatsFromSystemSuspend(staleStats) == null) { 87 Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend"); 88 return null; 89 } 90 return removeOldStats(staleStats); 91 } 92 } else { 93 Arrays.fill(mKernelWakelockBuffer, (byte) 0); 94 int len = 0; 95 boolean wakeup_sources; 96 final long startTime = SystemClock.uptimeMillis(); 97 98 final int oldMask = StrictMode.allowThreadDiskReadsMask(); 99 try { 100 FileInputStream is; 101 try { 102 is = new FileInputStream(sWakelockFile); 103 wakeup_sources = false; 104 } catch (java.io.FileNotFoundException e) { 105 try { 106 is = new FileInputStream(sWakeupSourceFile); 107 wakeup_sources = true; 108 } catch (java.io.FileNotFoundException e2) { 109 Slog.wtf(TAG, "neither " + sWakelockFile + " nor " + 110 sWakeupSourceFile + " exists"); 111 return null; 112 } 113 } 114 115 int cnt; 116 while ((cnt = is.read(mKernelWakelockBuffer, len, 117 mKernelWakelockBuffer.length - len)) > 0) { 118 len += cnt; 119 } 120 121 is.close(); 122 } catch (java.io.IOException e) { 123 Slog.wtf(TAG, "failed to read kernel wakelocks", e); 124 return null; 125 } finally { 126 StrictMode.setThreadPolicyMask(oldMask); 127 } 128 129 final long readTime = SystemClock.uptimeMillis() - startTime; 130 if (readTime > 100) { 131 Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms"); 132 } 133 134 if (len > 0) { 135 if (len >= mKernelWakelockBuffer.length) { 136 Slog.wtf(TAG, "Kernel wake locks exceeded mKernelWakelockBuffer size " 137 + mKernelWakelockBuffer.length); 138 } 139 int i; 140 for (i=0; i<len; i++) { 141 if (mKernelWakelockBuffer[i] == '\0') { 142 len = i; 143 break; 144 } 145 } 146 } 147 148 // static read/write lock protection for sKernelWakelockUpdateVersion 149 synchronized (KernelWakelockReader.class) { 150 updateVersion(staleStats); 151 // Get native wakelock stats from SystemSuspend 152 if (getWakelockStatsFromSystemSuspend(staleStats) == null) { 153 Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend"); 154 } 155 // Get kernel wakelock stats 156 parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats); 157 return removeOldStats(staleStats); 158 } 159 } 160 } 161 162 /** 163 * Attempt to wait for suspend_control service if not immediately available. 164 */ waitForSuspendControlService()165 private ISuspendControlServiceInternal waitForSuspendControlService() 166 throws ServiceNotFoundException { 167 final String name = "suspend_control_internal"; 168 final int numRetries = 5; 169 for (int i = 0; i < numRetries; i++) { 170 mSuspendControlService = ISuspendControlServiceInternal.Stub.asInterface( 171 ServiceManager.getService(name)); 172 if (mSuspendControlService != null) { 173 return mSuspendControlService; 174 } 175 } 176 throw new ServiceNotFoundException(name); 177 } 178 179 /** 180 * On success, returns the updated stats from SystemSupend, else returns null. 181 */ getWakelockStatsFromSystemSuspend( final KernelWakelockStats staleStats)182 private KernelWakelockStats getWakelockStatsFromSystemSuspend( 183 final KernelWakelockStats staleStats) { 184 if (mSuspendControlService == null) { 185 try { 186 mSuspendControlService = waitForSuspendControlService(); 187 } catch (ServiceNotFoundException e) { 188 Slog.wtf(TAG, "Required service suspend_control not available", e); 189 return null; 190 } 191 } 192 193 WakeLockInfo[] wlStats; 194 try { 195 wlStats = mSuspendControlService.getWakeLockStats(); 196 updateWakelockStats(wlStats, staleStats); 197 } catch (RemoteException e) { 198 Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e); 199 return null; 200 } 201 202 return staleStats; 203 } 204 205 /** 206 * Updates statleStats with stats from SystemSuspend. 207 * @param staleStats Existing object to update. 208 * @return the updated stats. 209 */ 210 @VisibleForTesting updateWakelockStats(WakeLockInfo[] wlStats, final KernelWakelockStats staleStats)211 public KernelWakelockStats updateWakelockStats(WakeLockInfo[] wlStats, 212 final KernelWakelockStats staleStats) { 213 for (WakeLockInfo info : wlStats) { 214 if (!staleStats.containsKey(info.name)) { 215 staleStats.put(info.name, new KernelWakelockStats.Entry((int) info.activeCount, 216 info.totalTime * 1000 /* ms to us */, 217 info.isActive ? info.activeTime * 1000 : 0, 218 sKernelWakelockUpdateVersion)); 219 } else { 220 KernelWakelockStats.Entry kwlStats = staleStats.get(info.name); 221 kwlStats.count = (int) info.activeCount; 222 // Convert milliseconds to microseconds 223 kwlStats.totalTimeUs = info.totalTime * 1000; 224 kwlStats.activeTimeUs = info.isActive ? info.activeTime * 1000 : 0; 225 kwlStats.version = sKernelWakelockUpdateVersion; 226 } 227 } 228 229 return staleStats; 230 } 231 232 /** 233 * Reads the wakelocks and updates the staleStats with the new information. 234 */ 235 @VisibleForTesting parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources, final KernelWakelockStats staleStats)236 public KernelWakelockStats parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources, 237 final KernelWakelockStats staleStats) { 238 String name; 239 int count; 240 long totalTime; 241 long activeTime; 242 int startIndex; 243 int endIndex; 244 245 // Advance past the first line. 246 int i; 247 for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++); 248 startIndex = endIndex = i + 1; 249 250 synchronized(this) { 251 while (endIndex < len) { 252 for (endIndex=startIndex; 253 endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0'; 254 endIndex++); 255 // Don't go over the end of the buffer, Process.parseProcLine might 256 // write to wlBuffer[endIndex] 257 if (endIndex > (len - 1) ) { 258 break; 259 } 260 261 String[] nameStringArray = mProcWakelocksName; 262 long[] wlData = mProcWakelocksData; 263 // Stomp out any bad characters since this is from a circular buffer 264 // A corruption is seen sometimes that results in the vm crashing 265 // This should prevent crashes and the line will probably fail to parse 266 for (int j = startIndex; j < endIndex; j++) { 267 if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?'; 268 } 269 boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex, 270 wakeup_sources ? WAKEUP_SOURCES_FORMAT : 271 PROC_WAKELOCKS_FORMAT, 272 nameStringArray, wlData, null); 273 274 name = nameStringArray[0].trim(); 275 count = (int) wlData[1]; 276 277 if (wakeup_sources) { 278 // convert milliseconds to microseconds 279 activeTime = wlData[2] * 1000; 280 totalTime = wlData[3] * 1000; 281 } else { 282 // convert nanoseconds to microseconds with rounding. 283 activeTime = (wlData[2] + 500) / 1000; 284 totalTime = (wlData[3] + 500) / 1000; 285 } 286 287 if (parsed && name.length() > 0) { 288 if (!staleStats.containsKey(name)) { 289 staleStats.put(name, new KernelWakelockStats.Entry(count, totalTime, 290 activeTime, sKernelWakelockUpdateVersion)); 291 } else { 292 KernelWakelockStats.Entry kwlStats = staleStats.get(name); 293 if (kwlStats.version == sKernelWakelockUpdateVersion) { 294 kwlStats.count += count; 295 kwlStats.totalTimeUs += totalTime; 296 kwlStats.activeTimeUs = activeTime; 297 } else { 298 kwlStats.count = count; 299 kwlStats.totalTimeUs = totalTime; 300 kwlStats.activeTimeUs = activeTime; 301 kwlStats.version = sKernelWakelockUpdateVersion; 302 } 303 } 304 } else if (!parsed) { 305 try { 306 Slog.wtf(TAG, "Failed to parse proc line: " + 307 new String(wlBuffer, startIndex, endIndex - startIndex)); 308 } catch (Exception e) { 309 Slog.wtf(TAG, "Failed to parse proc line!"); 310 } 311 } 312 startIndex = endIndex + 1; 313 } 314 315 return staleStats; 316 } 317 } 318 319 /** 320 * Increments sKernelWakelockUpdateVersion and updates the version in staleStats. 321 * @param staleStats Existing object to update. 322 * @return the updated stats. 323 */ 324 @VisibleForTesting updateVersion(KernelWakelockStats staleStats)325 public KernelWakelockStats updateVersion(KernelWakelockStats staleStats) { 326 sKernelWakelockUpdateVersion++; 327 staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion; 328 return staleStats; 329 } 330 331 /** 332 * Removes old stats from staleStats. 333 * @param staleStats Existing object to update. 334 * @return the updated stats. 335 */ 336 @VisibleForTesting removeOldStats(final KernelWakelockStats staleStats)337 public KernelWakelockStats removeOldStats(final KernelWakelockStats staleStats) { 338 Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator(); 339 while (itr.hasNext()) { 340 if (itr.next().version != sKernelWakelockUpdateVersion) { 341 itr.remove(); 342 } 343 } 344 return staleStats; 345 } 346 } 347