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