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 * On success, returns the updated stats from SystemSupend, else returns null. 157 */ getWakelockStatsFromSystemSuspend( final KernelWakelockStats staleStats)158 private KernelWakelockStats getWakelockStatsFromSystemSuspend( 159 final KernelWakelockStats staleStats) { 160 WakeLockInfo[] wlStats = null; 161 if (mSuspendControlService == null) { 162 try { 163 mSuspendControlService = ISuspendControlService.Stub.asInterface( 164 ServiceManager.getServiceOrThrow("suspend_control")); 165 } catch (ServiceNotFoundException e) { 166 Slog.wtf(TAG, "Required service suspend_control not available", e); 167 return null; 168 } 169 } 170 171 try { 172 wlStats = mSuspendControlService.getWakeLockStats(); 173 updateWakelockStats(wlStats, staleStats); 174 } catch (RemoteException e) { 175 Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e); 176 return null; 177 } 178 179 return staleStats; 180 } 181 182 /** 183 * Updates statleStats with stats from SystemSuspend. 184 * @param staleStats Existing object to update. 185 * @return the updated stats. 186 */ 187 @VisibleForTesting updateWakelockStats(WakeLockInfo[] wlStats, final KernelWakelockStats staleStats)188 public KernelWakelockStats updateWakelockStats(WakeLockInfo[] wlStats, 189 final KernelWakelockStats staleStats) { 190 for (WakeLockInfo info : wlStats) { 191 if (!staleStats.containsKey(info.name)) { 192 staleStats.put(info.name, new KernelWakelockStats.Entry((int) info.activeCount, 193 info.totalTime * 1000 /* ms to us */, sKernelWakelockUpdateVersion)); 194 } else { 195 KernelWakelockStats.Entry kwlStats = staleStats.get(info.name); 196 kwlStats.mCount = (int) info.activeCount; 197 // Convert milliseconds to microseconds 198 kwlStats.mTotalTime = info.totalTime * 1000; 199 kwlStats.mVersion = sKernelWakelockUpdateVersion; 200 } 201 } 202 203 return staleStats; 204 } 205 206 /** 207 * Reads the wakelocks and updates the staleStats with the new information. 208 */ 209 @VisibleForTesting parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources, final KernelWakelockStats staleStats)210 public KernelWakelockStats parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources, 211 final KernelWakelockStats staleStats) { 212 String name; 213 int count; 214 long totalTime; 215 int startIndex; 216 int endIndex; 217 218 // Advance past the first line. 219 int i; 220 for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++); 221 startIndex = endIndex = i + 1; 222 223 synchronized(this) { 224 while (endIndex < len) { 225 for (endIndex=startIndex; 226 endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0'; 227 endIndex++); 228 // Don't go over the end of the buffer, Process.parseProcLine might 229 // write to wlBuffer[endIndex] 230 if (endIndex > (len - 1) ) { 231 break; 232 } 233 234 String[] nameStringArray = mProcWakelocksName; 235 long[] wlData = mProcWakelocksData; 236 // Stomp out any bad characters since this is from a circular buffer 237 // A corruption is seen sometimes that results in the vm crashing 238 // This should prevent crashes and the line will probably fail to parse 239 for (int j = startIndex; j < endIndex; j++) { 240 if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?'; 241 } 242 boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex, 243 wakeup_sources ? WAKEUP_SOURCES_FORMAT : 244 PROC_WAKELOCKS_FORMAT, 245 nameStringArray, wlData, null); 246 247 name = nameStringArray[0].trim(); 248 count = (int) wlData[1]; 249 250 if (wakeup_sources) { 251 // convert milliseconds to microseconds 252 totalTime = wlData[2] * 1000; 253 } else { 254 // convert nanoseconds to microseconds with rounding. 255 totalTime = (wlData[2] + 500) / 1000; 256 } 257 258 if (parsed && name.length() > 0) { 259 if (!staleStats.containsKey(name)) { 260 staleStats.put(name, new KernelWakelockStats.Entry(count, totalTime, 261 sKernelWakelockUpdateVersion)); 262 } else { 263 KernelWakelockStats.Entry kwlStats = staleStats.get(name); 264 if (kwlStats.mVersion == sKernelWakelockUpdateVersion) { 265 kwlStats.mCount += count; 266 kwlStats.mTotalTime += totalTime; 267 } else { 268 kwlStats.mCount = count; 269 kwlStats.mTotalTime = totalTime; 270 kwlStats.mVersion = sKernelWakelockUpdateVersion; 271 } 272 } 273 } else if (!parsed) { 274 try { 275 Slog.wtf(TAG, "Failed to parse proc line: " + 276 new String(wlBuffer, startIndex, endIndex - startIndex)); 277 } catch (Exception e) { 278 Slog.wtf(TAG, "Failed to parse proc line!"); 279 } 280 } 281 startIndex = endIndex + 1; 282 } 283 284 return staleStats; 285 } 286 } 287 288 /** 289 * Increments sKernelWakelockUpdateVersion and updates the version in staleStats. 290 * @param staleStats Existing object to update. 291 * @return the updated stats. 292 */ 293 @VisibleForTesting updateVersion(KernelWakelockStats staleStats)294 public KernelWakelockStats updateVersion(KernelWakelockStats staleStats) { 295 sKernelWakelockUpdateVersion++; 296 staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion; 297 return staleStats; 298 } 299 300 /** 301 * Removes old stats from staleStats. 302 * @param staleStats Existing object to update. 303 * @return the updated stats. 304 */ 305 @VisibleForTesting removeOldStats(final KernelWakelockStats staleStats)306 public KernelWakelockStats removeOldStats(final KernelWakelockStats staleStats) { 307 Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator(); 308 while (itr.hasNext()) { 309 if (itr.next().mVersion != sKernelWakelockUpdateVersion) { 310 itr.remove(); 311 } 312 } 313 return staleStats; 314 } 315 } 316