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; 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 len = is.read(buffer); 91 is.close(); 92 } catch (java.io.IOException e) { 93 Slog.wtf(TAG, "failed to read kernel wakelocks", e); 94 return null; 95 } finally { 96 StrictMode.setThreadPolicyMask(oldMask); 97 } 98 99 final long readTime = SystemClock.uptimeMillis() - startTime; 100 if (readTime > 100) { 101 Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms"); 102 } 103 104 if (len > 0) { 105 if (len >= buffer.length) { 106 Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length); 107 } 108 int i; 109 for (i=0; i<len; i++) { 110 if (buffer[i] == '\0') { 111 len = i; 112 break; 113 } 114 } 115 } 116 return parseProcWakelocks(buffer, len, wakeup_sources, staleStats); 117 } 118 119 /** 120 * Reads the wakelocks and updates the staleStats with the new information. 121 */ 122 @VisibleForTesting parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources, final KernelWakelockStats staleStats)123 public KernelWakelockStats parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources, 124 final KernelWakelockStats staleStats) { 125 String name; 126 int count; 127 long totalTime; 128 int startIndex; 129 int endIndex; 130 131 // Advance past the first line. 132 int i; 133 for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++); 134 startIndex = endIndex = i + 1; 135 136 synchronized(this) { 137 sKernelWakelockUpdateVersion++; 138 while (endIndex < len) { 139 for (endIndex=startIndex; 140 endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0'; 141 endIndex++); 142 // Don't go over the end of the buffer, Process.parseProcLine might 143 // write to wlBuffer[endIndex] 144 if (endIndex > (len - 1) ) { 145 break; 146 } 147 148 String[] nameStringArray = mProcWakelocksName; 149 long[] wlData = mProcWakelocksData; 150 // Stomp out any bad characters since this is from a circular buffer 151 // A corruption is seen sometimes that results in the vm crashing 152 // This should prevent crashes and the line will probably fail to parse 153 for (int j = startIndex; j < endIndex; j++) { 154 if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?'; 155 } 156 boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex, 157 wakeup_sources ? WAKEUP_SOURCES_FORMAT : 158 PROC_WAKELOCKS_FORMAT, 159 nameStringArray, wlData, null); 160 161 name = nameStringArray[0]; 162 count = (int) wlData[1]; 163 164 if (wakeup_sources) { 165 // convert milliseconds to microseconds 166 totalTime = wlData[2] * 1000; 167 } else { 168 // convert nanoseconds to microseconds with rounding. 169 totalTime = (wlData[2] + 500) / 1000; 170 } 171 172 if (parsed && name.length() > 0) { 173 if (!staleStats.containsKey(name)) { 174 staleStats.put(name, new KernelWakelockStats.Entry(count, totalTime, 175 sKernelWakelockUpdateVersion)); 176 } else { 177 KernelWakelockStats.Entry kwlStats = staleStats.get(name); 178 if (kwlStats.mVersion == sKernelWakelockUpdateVersion) { 179 kwlStats.mCount += count; 180 kwlStats.mTotalTime += totalTime; 181 } else { 182 kwlStats.mCount = count; 183 kwlStats.mTotalTime = totalTime; 184 kwlStats.mVersion = sKernelWakelockUpdateVersion; 185 } 186 } 187 } else if (!parsed) { 188 try { 189 Slog.wtf(TAG, "Failed to parse proc line: " + 190 new String(wlBuffer, startIndex, endIndex - startIndex)); 191 } catch (Exception e) { 192 Slog.wtf(TAG, "Failed to parse proc line!"); 193 } 194 } 195 startIndex = endIndex + 1; 196 } 197 198 // Don't report old data. 199 Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator(); 200 while (itr.hasNext()) { 201 if (itr.next().mVersion != sKernelWakelockUpdateVersion) { 202 itr.remove(); 203 } 204 } 205 206 staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion; 207 return staleStats; 208 } 209 } 210 } 211