blob: 46667d1ea688fec51ca5057dff48df0bdcc4928a [file] [log] [blame]
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -07001/*
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 */
16package com.android.internal.os;
17
18import android.os.Process;
Mike Maea357e52018-02-23 11:07:53 -080019import android.os.StrictMode;
Amith Yamasani3d5d9ae2017-07-05 13:34:46 -070020import android.os.SystemClock;
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -070021import android.util.Slog;
22
Adam Lesinskid84ad302016-05-17 18:31:02 -070023import com.android.internal.annotations.VisibleForTesting;
24
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -070025import java.io.FileInputStream;
26import java.util.Iterator;
27
28/**
29 * Reads and parses wakelock stats from the kernel (/proc/wakelocks).
30 */
31public 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 */
67 public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) {
68 byte[] buffer = new byte[32*1024];
69 int len;
70 boolean wakeup_sources;
Amith Yamasani3d5d9ae2017-07-05 13:34:46 -070071 final long startTime = SystemClock.uptimeMillis();
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -070072
Mike Maea357e52018-02-23 11:07:53 -080073 final int oldMask = StrictMode.allowThreadDiskReadsMask();
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -070074 try {
75 FileInputStream is;
76 try {
Adam Lesinski68569642015-07-21 12:08:56 -070077 is = new FileInputStream(sWakelockFile);
78 wakeup_sources = false;
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -070079 } catch (java.io.FileNotFoundException e) {
80 try {
Adam Lesinski68569642015-07-21 12:08:56 -070081 is = new FileInputStream(sWakeupSourceFile);
82 wakeup_sources = true;
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -070083 } catch (java.io.FileNotFoundException e2) {
Adam Lesinskifbabe7d2015-08-03 14:37:38 -070084 Slog.wtf(TAG, "neither " + sWakelockFile + " nor " +
85 sWakeupSourceFile + " exists");
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -070086 return null;
87 }
88 }
89
90 len = is.read(buffer);
91 is.close();
92 } catch (java.io.IOException e) {
Adam Lesinskifbabe7d2015-08-03 14:37:38 -070093 Slog.wtf(TAG, "failed to read kernel wakelocks", e);
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -070094 return null;
Mike Maea357e52018-02-23 11:07:53 -080095 } finally {
96 StrictMode.setThreadPolicyMask(oldMask);
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -070097 }
98
Amith Yamasani3d5d9ae2017-07-05 13:34:46 -070099 final long readTime = SystemClock.uptimeMillis() - startTime;
100 if (readTime > 100) {
101 Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms");
102 }
103
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -0700104 if (len > 0) {
105 if (len >= buffer.length) {
106 Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
107 }
108 int i;
109 for (i=0; i<len; i++) {
110 if (buffer[i] == '\0') {
111 len = i;
112 break;
113 }
114 }
115 }
116 return parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
117 }
118
119 /**
120 * Reads the wakelocks and updates the staleStats with the new information.
121 */
Adam Lesinskid84ad302016-05-17 18:31:02 -0700122 @VisibleForTesting
123 public KernelWakelockStats parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources,
124 final KernelWakelockStats staleStats) {
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -0700125 String name;
126 int count;
127 long totalTime;
128 int startIndex;
129 int endIndex;
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -0700130
131 // Advance past the first line.
132 int i;
133 for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++);
134 startIndex = endIndex = i + 1;
135
136 synchronized(this) {
137 sKernelWakelockUpdateVersion++;
138 while (endIndex < len) {
139 for (endIndex=startIndex;
140 endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0';
141 endIndex++);
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -0700142 // Don't go over the end of the buffer, Process.parseProcLine might
143 // write to wlBuffer[endIndex]
Adam Lesinskid84ad302016-05-17 18:31:02 -0700144 if (endIndex > (len - 1) ) {
145 break;
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -0700146 }
147
148 String[] nameStringArray = mProcWakelocksName;
149 long[] wlData = mProcWakelocksData;
150 // Stomp out any bad characters since this is from a circular buffer
151 // A corruption is seen sometimes that results in the vm crashing
152 // This should prevent crashes and the line will probably fail to parse
153 for (int j = startIndex; j < endIndex; j++) {
154 if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?';
155 }
156 boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex,
157 wakeup_sources ? WAKEUP_SOURCES_FORMAT :
158 PROC_WAKELOCKS_FORMAT,
159 nameStringArray, wlData, null);
160
161 name = nameStringArray[0];
162 count = (int) wlData[1];
163
164 if (wakeup_sources) {
165 // convert milliseconds to microseconds
166 totalTime = wlData[2] * 1000;
167 } else {
168 // convert nanoseconds to microseconds with rounding.
169 totalTime = (wlData[2] + 500) / 1000;
170 }
171
172 if (parsed && name.length() > 0) {
173 if (!staleStats.containsKey(name)) {
174 staleStats.put(name, new KernelWakelockStats.Entry(count, totalTime,
175 sKernelWakelockUpdateVersion));
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -0700176 } else {
177 KernelWakelockStats.Entry kwlStats = staleStats.get(name);
178 if (kwlStats.mVersion == sKernelWakelockUpdateVersion) {
179 kwlStats.mCount += count;
180 kwlStats.mTotalTime += totalTime;
181 } else {
182 kwlStats.mCount = count;
183 kwlStats.mTotalTime = totalTime;
184 kwlStats.mVersion = sKernelWakelockUpdateVersion;
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -0700185 }
186 }
Adam Lesinskifbabe7d2015-08-03 14:37:38 -0700187 } else if (!parsed) {
188 try {
189 Slog.wtf(TAG, "Failed to parse proc line: " +
190 new String(wlBuffer, startIndex, endIndex - startIndex));
191 } catch (Exception e) {
192 Slog.wtf(TAG, "Failed to parse proc line!");
193 }
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -0700194 }
Adam Lesinskid84ad302016-05-17 18:31:02 -0700195 startIndex = endIndex + 1;
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -0700196 }
197
Adam Lesinskid84ad302016-05-17 18:31:02 -0700198 // Don't report old data.
199 Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator();
200 while (itr.hasNext()) {
201 if (itr.next().mVersion != sKernelWakelockUpdateVersion) {
202 itr.remove();
Adam Lesinski4b6bd8d2015-03-19 14:35:45 -0700203 }
204 }
205
206 staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion;
207 return staleStats;
208 }
209 }
210}