• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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