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