blob: 3cbfaead477966761a5f75236c5b8524192d1c09 [file] [log] [blame]
Mike Ma3d422c32017-10-25 11:08:57 -07001/*
2 * Copyright (C) 2017 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
17package com.android.internal.os;
18
19import android.annotation.Nullable;
Mike Ma3d422c32017-10-25 11:08:57 -070020import android.util.Slog;
21import android.util.SparseArray;
Mike Ma3d422c32017-10-25 11:08:57 -070022
23import com.android.internal.annotations.VisibleForTesting;
24
Mike Ma2ab01442018-02-13 14:22:47 -080025import java.nio.ByteBuffer;
26import java.nio.IntBuffer;
Mike Ma234d1822018-03-13 18:53:21 -070027import java.util.function.Consumer;
Mike Ma3d422c32017-10-25 11:08:57 -070028
29/**
Mike Ma2ab01442018-02-13 14:22:47 -080030 * Reads binary proc file /proc/uid_cpupower/concurrent_policy_time and reports CPU cluster times
31 * to BatteryStats to compute cluster power. See
32 * {@link PowerProfile#getAveragePowerForCpuCluster(int)}.
33 *
34 * concurrent_policy_time is an array of u32's in the following format:
35 * [n, x0, ..., xn, uid0, time0a, time0b, ..., time0n,
36 * uid1, time1a, time1b, ..., time1n,
37 * uid2, time2a, time2b, ..., time2n, etc.]
38 * where n is the number of policies
39 * xi is the number cpus on a particular policy
40 * Each uidX is followed by x0 time entries corresponding to the time UID X spent on cluster0
41 * running concurrently with 0, 1, 2, ..., x0 - 1 other processes, then followed by x1, ..., xn
42 * time entries.
43 *
Mike Ma3d422c32017-10-25 11:08:57 -070044 * The file contains a monotonically increasing count of time for a single boot. This class
Mike Ma2ab01442018-02-13 14:22:47 -080045 * maintains the previous results of a call to {@link #readDelta} in order to provide a
46 * proper delta.
47 *
48 * This class uses a throttler to reject any {@link #readDelta} call within
49 * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader},
50 * which has a shorter throttle interval and returns cached result from last read when the request
51 * is throttled.
52 *
Mike Ma69d8b3e2018-02-23 14:51:26 -080053 * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each
54 * caller has its own view of delta.
Mike Ma3d422c32017-10-25 11:08:57 -070055 */
Mike Ma69d8b3e2018-02-23 14:51:26 -080056public class KernelUidCpuClusterTimeReader extends
57 KernelUidCpuTimeReaderBase<KernelUidCpuClusterTimeReader.Callback> {
58 private static final String TAG = KernelUidCpuClusterTimeReader.class.getSimpleName();
Mike Ma3d422c32017-10-25 11:08:57 -070059
Mike Ma2ab01442018-02-13 14:22:47 -080060 private final KernelCpuProcReader mProcReader;
Mike Ma2ab01442018-02-13 14:22:47 -080061 private SparseArray<double[]> mLastUidPolicyTimeMs = new SparseArray<>();
62
63 private int mNumClusters = -1;
64 private int mNumCores;
65 private int[] mNumCoresOnCluster;
66
67 private double[] mCurTime; // Reuse to avoid GC.
68 private long[] mDeltaTime; // Reuse to avoid GC.
Chenjie Yuec676612018-03-07 09:19:17 -080069 private long[] mCurTimeRounded; // Reuse to avoid GC.
Mike Ma3d422c32017-10-25 11:08:57 -070070
Mike Ma69d8b3e2018-02-23 14:51:26 -080071 public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
Mike Ma3d422c32017-10-25 11:08:57 -070072 /**
Mike Ma2ab01442018-02-13 14:22:47 -080073 * Notifies when new data is available.
74 *
75 * @param uid uid int
76 * @param cpuClusterTimeMs an array of times spent by this uid on corresponding clusters.
77 * The array index is the cluster index.
Mike Ma3d422c32017-10-25 11:08:57 -070078 */
Mike Ma2ab01442018-02-13 14:22:47 -080079 void onUidCpuPolicyTime(int uid, long[] cpuClusterTimeMs);
80 }
81
82 public KernelUidCpuClusterTimeReader() {
83 mProcReader = KernelCpuProcReader.getClusterTimeReaderInstance();
84 }
85
86 @VisibleForTesting
87 public KernelUidCpuClusterTimeReader(KernelCpuProcReader procReader) {
88 mProcReader = procReader;
89 }
90
Mike Ma69d8b3e2018-02-23 14:51:26 -080091 @Override
92 protected void readDeltaImpl(@Nullable Callback cb) {
Mike Ma234d1822018-03-13 18:53:21 -070093 readImpl((buf) -> {
94 int uid = buf.get();
95 double[] lastTimes = mLastUidPolicyTimeMs.get(uid);
96 if (lastTimes == null) {
97 lastTimes = new double[mNumClusters];
98 mLastUidPolicyTimeMs.put(uid, lastTimes);
99 }
100 if (!sumClusterTime(buf, mCurTime)) {
101 return;
102 }
103 boolean valid = true;
104 boolean notify = false;
105 for (int i = 0; i < mNumClusters; i++) {
106 mDeltaTime[i] = (long) (mCurTime[i] - lastTimes[i]);
107 if (mDeltaTime[i] < 0) {
108 Slog.e(TAG, "Negative delta from cluster time proc: " + mDeltaTime[i]);
109 valid = false;
110 }
111 notify |= mDeltaTime[i] > 0;
112 }
113 if (notify && valid) {
114 System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
115 if (cb != null) {
116 cb.onUidCpuPolicyTime(uid, mDeltaTime);
117 }
118 }
119 });
120 }
121
122 public void readAbsolute(Callback callback) {
123 readImpl((buf) -> {
124 int uid = buf.get();
125 if (sumClusterTime(buf, mCurTime)) {
126 for (int i = 0; i < mNumClusters; i++) {
127 mCurTimeRounded[i] = (long) mCurTime[i];
128 }
129 callback.onUidCpuPolicyTime(uid, mCurTimeRounded);
130 }
131 });
132 }
133
134 private boolean sumClusterTime(IntBuffer buffer, double[] clusterTime) {
135 boolean valid = true;
136 for (int i = 0; i < mNumClusters; i++) {
137 clusterTime[i] = 0;
138 for (int j = 1; j <= mNumCoresOnCluster[i]; j++) {
139 int time = buffer.get();
140 if (time < 0) {
141 Slog.e(TAG, "Negative time from cluster time proc: " + time);
142 valid = false;
143 }
144 clusterTime[i] += (double) time * 10 / j; // Unit is 10ms.
145 }
146 }
147 return valid;
148 }
149
150 /**
151 * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
152 * seen results while processing the buffer, while readAbsolute returns the absolute value read
153 * from the buffer without storing. So readImpl contains the common logic of the two, leaving
154 * the difference to a processUid function.
155 *
156 * @param processUid the callback function to process the uid entry in the buffer.
157 */
158 private void readImpl(Consumer<IntBuffer> processUid) {
Mike Ma2ab01442018-02-13 14:22:47 -0800159 synchronized (mProcReader) {
160 ByteBuffer bytes = mProcReader.readBytes();
161 if (bytes == null || bytes.remaining() <= 4) {
162 // Error already logged in mProcReader.
163 return;
164 }
165 if ((bytes.remaining() & 3) != 0) {
166 Slog.wtf(TAG,
167 "Cannot parse cluster time proc bytes to int: " + bytes.remaining());
168 return;
169 }
170 IntBuffer buf = bytes.asIntBuffer();
171 final int numClusters = buf.get();
172 if (numClusters <= 0) {
173 Slog.wtf(TAG, "Cluster time format error: " + numClusters);
174 return;
175 }
176 if (mNumClusters == -1) {
177 mNumClusters = numClusters;
178 }
179 if (buf.remaining() < numClusters) {
180 Slog.wtf(TAG, "Too few data left in the buffer: " + buf.remaining());
181 return;
182 }
183 if (mNumCores <= 0) {
184 if (!readCoreInfo(buf, numClusters)) {
185 return;
186 }
187 } else {
188 buf.position(buf.position() + numClusters);
189 }
190
191 if (buf.remaining() % (mNumCores + 1) != 0) {
192 Slog.wtf(TAG,
193 "Cluster time format error: " + buf.remaining() + " / " + (mNumCores
194 + 1));
195 return;
196 }
197 int numUids = buf.remaining() / (mNumCores + 1);
198
199 for (int i = 0; i < numUids; i++) {
Mike Ma234d1822018-03-13 18:53:21 -0700200 processUid.accept(buf);
Mike Ma2ab01442018-02-13 14:22:47 -0800201 }
Mike Ma69d8b3e2018-02-23 14:51:26 -0800202 if (DEBUG) {
203 Slog.d(TAG, "Read uids: " + numUids);
204 }
Mike Ma2ab01442018-02-13 14:22:47 -0800205 }
Mike Ma2ab01442018-02-13 14:22:47 -0800206 }
207
Mike Ma2ab01442018-02-13 14:22:47 -0800208 // Returns if it has read valid info.
209 private boolean readCoreInfo(IntBuffer buf, int numClusters) {
210 int numCores = 0;
211 int[] numCoresOnCluster = new int[numClusters];
212 for (int i = 0; i < numClusters; i++) {
213 numCoresOnCluster[i] = buf.get();
214 numCores += numCoresOnCluster[i];
215 }
216 if (numCores <= 0) {
217 Slog.e(TAG, "Invalid # cores from cluster time proc file: " + numCores);
218 return false;
219 }
220 mNumCores = numCores;
221 mNumCoresOnCluster = numCoresOnCluster;
222 mCurTime = new double[numClusters];
223 mDeltaTime = new long[numClusters];
Chenjie Yuec676612018-03-07 09:19:17 -0800224 mCurTimeRounded = new long[numClusters];
Mike Ma2ab01442018-02-13 14:22:47 -0800225 return true;
Mike Ma3d422c32017-10-25 11:08:57 -0700226 }
227
228 public void removeUid(int uid) {
229 mLastUidPolicyTimeMs.delete(uid);
230 }
231
232 public void removeUidsInRange(int startUid, int endUid) {
Mike Ma3d422c32017-10-25 11:08:57 -0700233 mLastUidPolicyTimeMs.put(startUid, null);
234 mLastUidPolicyTimeMs.put(endUid, null);
235 final int firstIndex = mLastUidPolicyTimeMs.indexOfKey(startUid);
236 final int lastIndex = mLastUidPolicyTimeMs.indexOfKey(endUid);
237 mLastUidPolicyTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
238 }
Mike Ma3d422c32017-10-25 11:08:57 -0700239}