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