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