• Home
  • History
  • Annotate
  • 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.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