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.StrictMode; 20 import android.os.SystemClock; 21 import android.util.Slog; 22 23 import com.android.internal.annotations.VisibleForTesting; 24 25 import java.io.FileInputStream; 26 import java.util.Iterator; 27 28 /** 29 * Reads and parses wakelock stats from the kernel (/proc/wakelocks). 30 */ 31 public class KernelWakelockReader { 32 private static final String TAG = "KernelWakelockReader"; 33 private static int sKernelWakelockUpdateVersion = 0; 34 private static final String sWakelockFile = "/proc/wakelocks"; 35 private static final String sWakeupSourceFile = "/d/wakeup_sources"; 36 37 private static final int[] PROC_WAKELOCKS_FORMAT = new int[] { 38 Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name 39 Process.PROC_QUOTES, 40 Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 1: count 41 Process.PROC_TAB_TERM, 42 Process.PROC_TAB_TERM, 43 Process.PROC_TAB_TERM, 44 Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 5: totalTime 45 }; 46 47 private static final int[] WAKEUP_SOURCES_FORMAT = new int[] { 48 Process.PROC_TAB_TERM|Process.PROC_OUT_STRING, // 0: name 49 Process.PROC_TAB_TERM|Process.PROC_COMBINE| 50 Process.PROC_OUT_LONG, // 1: count 51 Process.PROC_TAB_TERM|Process.PROC_COMBINE, 52 Process.PROC_TAB_TERM|Process.PROC_COMBINE, 53 Process.PROC_TAB_TERM|Process.PROC_COMBINE, 54 Process.PROC_TAB_TERM|Process.PROC_COMBINE, 55 Process.PROC_TAB_TERM|Process.PROC_COMBINE 56 |Process.PROC_OUT_LONG, // 6: totalTime 57 }; 58 59 private final String[] mProcWakelocksName = new String[3]; 60 private final long[] mProcWakelocksData = new long[3]; 61 62 /** 63 * Reads kernel wakelock stats and updates the staleStats with the new information. 64 * @param staleStats Existing object to update. 65 * @return the updated data. 66 */ readKernelWakelockStats(KernelWakelockStats staleStats)67 public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) { 68 byte[] buffer = new byte[32*1024]; 69 int len = 0; 70 boolean wakeup_sources; 71 final long startTime = SystemClock.uptimeMillis(); 72 73 final int oldMask = StrictMode.allowThreadDiskReadsMask(); 74 try { 75 FileInputStream is; 76 try { 77 is = new FileInputStream(sWakelockFile); 78 wakeup_sources = false; 79 } catch (java.io.FileNotFoundException e) { 80 try { 81 is = new FileInputStream(sWakeupSourceFile); 82 wakeup_sources = true; 83 } catch (java.io.FileNotFoundException e2) { 84 Slog.wtf(TAG, "neither " + sWakelockFile + " nor " + 85 sWakeupSourceFile + " exists"); 86 return null; 87 } 88 } 89 90 int cnt; 91 while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) { 92 len += cnt; 93 } 94 95 is.close(); 96 } catch (java.io.IOException e) { 97 Slog.wtf(TAG, "failed to read kernel wakelocks", e); 98 return null; 99 } finally { 100 StrictMode.setThreadPolicyMask(oldMask); 101 } 102 103 final long readTime = SystemClock.uptimeMillis() - startTime; 104 if (readTime > 100) { 105 Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms"); 106 } 107 108 if (len > 0) { 109 if (len >= buffer.length) { 110 Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length); 111 } 112 int i; 113 for (i=0; i<len; i++) { 114 if (buffer[i] == '\0') { 115 len = i; 116 break; 117 } 118 } 119 } 120 return parseProcWakelocks(buffer, len, wakeup_sources, staleStats); 121 } 122 123 /** 124 * Reads the wakelocks and updates the staleStats with the new information. 125 */ 126 @VisibleForTesting parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources, final KernelWakelockStats staleStats)127 public KernelWakelockStats parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources, 128 final KernelWakelockStats staleStats) { 129 String name; 130 int count; 131 long totalTime; 132 int startIndex; 133 int endIndex; 134 135 // Advance past the first line. 136 int i; 137 for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++); 138 startIndex = endIndex = i + 1; 139 140 synchronized(this) { 141 sKernelWakelockUpdateVersion++; 142 while (endIndex < len) { 143 for (endIndex=startIndex; 144 endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0'; 145 endIndex++); 146 // Don't go over the end of the buffer, Process.parseProcLine might 147 // write to wlBuffer[endIndex] 148 if (endIndex > (len - 1) ) { 149 break; 150 } 151 152 String[] nameStringArray = mProcWakelocksName; 153 long[] wlData = mProcWakelocksData; 154 // Stomp out any bad characters since this is from a circular buffer 155 // A corruption is seen sometimes that results in the vm crashing 156 // This should prevent crashes and the line will probably fail to parse 157 for (int j = startIndex; j < endIndex; j++) { 158 if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?'; 159 } 160 boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex, 161 wakeup_sources ? WAKEUP_SOURCES_FORMAT : 162 PROC_WAKELOCKS_FORMAT, 163 nameStringArray, wlData, null); 164 165 name = nameStringArray[0].trim(); 166 count = (int) wlData[1]; 167 168 if (wakeup_sources) { 169 // convert milliseconds to microseconds 170 totalTime = wlData[2] * 1000; 171 } else { 172 // convert nanoseconds to microseconds with rounding. 173 totalTime = (wlData[2] + 500) / 1000; 174 } 175 176 if (parsed && name.length() > 0) { 177 if (!staleStats.containsKey(name)) { 178 staleStats.put(name, new KernelWakelockStats.Entry(count, totalTime, 179 sKernelWakelockUpdateVersion)); 180 } else { 181 KernelWakelockStats.Entry kwlStats = staleStats.get(name); 182 if (kwlStats.mVersion == sKernelWakelockUpdateVersion) { 183 kwlStats.mCount += count; 184 kwlStats.mTotalTime += totalTime; 185 } else { 186 kwlStats.mCount = count; 187 kwlStats.mTotalTime = totalTime; 188 kwlStats.mVersion = sKernelWakelockUpdateVersion; 189 } 190 } 191 } else if (!parsed) { 192 try { 193 Slog.wtf(TAG, "Failed to parse proc line: " + 194 new String(wlBuffer, startIndex, endIndex - startIndex)); 195 } catch (Exception e) { 196 Slog.wtf(TAG, "Failed to parse proc line!"); 197 } 198 } 199 startIndex = endIndex + 1; 200 } 201 202 // Don't report old data. 203 Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator(); 204 while (itr.hasNext()) { 205 if (itr.next().mVersion != sKernelWakelockUpdateVersion) { 206 itr.remove(); 207 } 208 } 209 210 staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion; 211 return staleStats; 212 } 213 } 214 } 215