1 /* 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 17 package com.android.internal.os; 18 19 import android.annotation.Nullable; 20 import android.util.Slog; 21 import android.util.SparseArray; 22 23 import com.android.internal.annotations.VisibleForTesting; 24 25 import java.nio.ByteBuffer; 26 import java.nio.IntBuffer; 27 import java.util.function.Consumer; 28 29 /** 30 * 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 * 44 * The file contains a monotonically increasing count of time for a single boot. This class 45 * 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 * 53 * 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. 55 */ 56 public class KernelUidCpuClusterTimeReader extends 57 KernelUidCpuTimeReaderBase<KernelUidCpuClusterTimeReader.Callback> { 58 private static final String TAG = KernelUidCpuClusterTimeReader.class.getSimpleName(); 59 60 private final KernelCpuProcReader mProcReader; 61 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. 69 private long[] mCurTimeRounded; // Reuse to avoid GC. 70 71 public interface Callback extends KernelUidCpuTimeReaderBase.Callback { 72 /** 73 * 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. 78 */ onUidCpuPolicyTime(int uid, long[] cpuClusterTimeMs)79 void onUidCpuPolicyTime(int uid, long[] cpuClusterTimeMs); 80 } 81 KernelUidCpuClusterTimeReader()82 public KernelUidCpuClusterTimeReader() { 83 mProcReader = KernelCpuProcReader.getClusterTimeReaderInstance(); 84 } 85 86 @VisibleForTesting KernelUidCpuClusterTimeReader(KernelCpuProcReader procReader)87 public KernelUidCpuClusterTimeReader(KernelCpuProcReader procReader) { 88 mProcReader = procReader; 89 } 90 91 @Override readDeltaImpl(@ullable Callback cb)92 protected void readDeltaImpl(@Nullable Callback cb) { 93 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 readAbsolute(Callback callback)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 sumClusterTime(IntBuffer buffer, double[] clusterTime)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 */ readImpl(Consumer<IntBuffer> processUid)158 private void readImpl(Consumer<IntBuffer> processUid) { 159 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++) { 200 processUid.accept(buf); 201 } 202 if (DEBUG) { 203 Slog.d(TAG, "Read uids: " + numUids); 204 } 205 } 206 } 207 208 // Returns if it has read valid info. readCoreInfo(IntBuffer buf, int numClusters)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]; 224 mCurTimeRounded = new long[numClusters]; 225 return true; 226 } 227 removeUid(int uid)228 public void removeUid(int uid) { 229 mLastUidPolicyTimeMs.delete(uid); 230 } 231 removeUidsInRange(int startUid, int endUid)232 public void removeUidsInRange(int startUid, int endUid) { 233 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 } 239 } 240