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