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_active_time and reports CPU active time to 31 * BatteryStats to compute {@link PowerProfile#POWER_CPU_ACTIVE}. 32 * 33 * concurrent_active_time is an array of u32's in the following format: 34 * [n, uid0, time0a, time0b, ..., time0n, 35 * uid1, time1a, time1b, ..., time1n, 36 * uid2, time2a, time2b, ..., time2n, etc.] 37 * where n is the total number of cpus (num_possible_cpus) 38 * ... 39 * timeXn means the CPU time that a UID X spent running concurrently with n other processes. 40 * The file contains a monotonically increasing count of time for a single boot. This class 41 * maintains the previous results of a call to {@link #readDelta} in order to provide a 42 * proper delta. 43 * 44 * This class uses a throttler to reject any {@link #readDelta} call within 45 * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader}, 46 * which has a shorter throttle interval and returns cached result from last read when the request 47 * is throttled. 48 * 49 * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each 50 * caller has its own view of delta. 51 */ 52 public class KernelUidCpuActiveTimeReader extends 53 KernelUidCpuTimeReaderBase<KernelUidCpuActiveTimeReader.Callback> { 54 private static final String TAG = KernelUidCpuActiveTimeReader.class.getSimpleName(); 55 56 private final KernelCpuProcReader mProcReader; 57 private SparseArray<Double> mLastUidCpuActiveTimeMs = new SparseArray<>(); 58 private int mCores; 59 60 public interface Callback extends KernelUidCpuTimeReaderBase.Callback { 61 /** 62 * Notifies when new data is available. 63 * 64 * @param uid uid int 65 * @param cpuActiveTimeMs cpu active time spent by this uid in milliseconds 66 */ onUidCpuActiveTime(int uid, long cpuActiveTimeMs)67 void onUidCpuActiveTime(int uid, long cpuActiveTimeMs); 68 } 69 KernelUidCpuActiveTimeReader()70 public KernelUidCpuActiveTimeReader() { 71 mProcReader = KernelCpuProcReader.getActiveTimeReaderInstance(); 72 } 73 74 @VisibleForTesting KernelUidCpuActiveTimeReader(KernelCpuProcReader procReader)75 public KernelUidCpuActiveTimeReader(KernelCpuProcReader procReader) { 76 mProcReader = procReader; 77 } 78 79 @Override readDeltaImpl(@ullable Callback callback)80 protected void readDeltaImpl(@Nullable Callback callback) { 81 readImpl((buf) -> { 82 int uid = buf.get(); 83 double activeTime = sumActiveTime(buf); 84 if (activeTime > 0) { 85 double delta = activeTime - mLastUidCpuActiveTimeMs.get(uid, 0.0); 86 if (delta > 0) { 87 mLastUidCpuActiveTimeMs.put(uid, activeTime); 88 if (callback != null) { 89 callback.onUidCpuActiveTime(uid, (long) delta); 90 } 91 } else if (delta < 0) { 92 Slog.e(TAG, "Negative delta from active time proc: " + delta); 93 } 94 } 95 }); 96 } 97 readAbsolute(Callback callback)98 public void readAbsolute(Callback callback) { 99 readImpl((buf) -> { 100 int uid = buf.get(); 101 double activeTime = sumActiveTime(buf); 102 if (activeTime > 0) { 103 callback.onUidCpuActiveTime(uid, (long) activeTime); 104 } 105 }); 106 } 107 sumActiveTime(IntBuffer buffer)108 private double sumActiveTime(IntBuffer buffer) { 109 double sum = 0; 110 boolean corrupted = false; 111 for (int j = 1; j <= mCores; j++) { 112 int time = buffer.get(); 113 if (time < 0) { 114 // Even if error happens, we still need to continue reading. 115 // Buffer cannot be skipped. 116 Slog.e(TAG, "Negative time from active time proc: " + time); 117 corrupted = true; 118 } else { 119 sum += (double) time * 10 / j; // Unit is 10ms. 120 } 121 } 122 return corrupted ? -1 : sum; 123 } 124 125 /** 126 * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last 127 * seen results while processing the buffer, while readAbsolute returns the absolute value read 128 * from the buffer without storing. So readImpl contains the common logic of the two, leaving 129 * the difference to a processUid function. 130 * 131 * @param processUid the callback function to process the uid entry in the buffer. 132 */ readImpl(Consumer<IntBuffer> processUid)133 private void readImpl(Consumer<IntBuffer> processUid) { 134 synchronized (mProcReader) { 135 final ByteBuffer bytes = mProcReader.readBytes(); 136 if (bytes == null || bytes.remaining() <= 4) { 137 // Error already logged in mProcReader. 138 return; 139 } 140 if ((bytes.remaining() & 3) != 0) { 141 Slog.wtf(TAG, 142 "Cannot parse active time proc bytes to int: " + bytes.remaining()); 143 return; 144 } 145 final IntBuffer buf = bytes.asIntBuffer(); 146 final int cores = buf.get(); 147 if (mCores != 0 && cores != mCores) { 148 Slog.wtf(TAG, "Cpu active time wrong # cores: " + cores); 149 return; 150 } 151 mCores = cores; 152 if (cores <= 0 || buf.remaining() % (cores + 1) != 0) { 153 Slog.wtf(TAG, 154 "Cpu active time format error: " + buf.remaining() + " / " + (cores 155 + 1)); 156 return; 157 } 158 int numUids = buf.remaining() / (cores + 1); 159 for (int i = 0; i < numUids; i++) { 160 processUid.accept(buf); 161 } 162 if (DEBUG) { 163 Slog.d(TAG, "Read uids: " + numUids); 164 } 165 } 166 } 167 removeUid(int uid)168 public void removeUid(int uid) { 169 mLastUidCpuActiveTimeMs.delete(uid); 170 } 171 removeUidsInRange(int startUid, int endUid)172 public void removeUidsInRange(int startUid, int endUid) { 173 mLastUidCpuActiveTimeMs.put(startUid, null); 174 mLastUidCpuActiveTimeMs.put(endUid, null); 175 final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid); 176 final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid); 177 mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); 178 } 179 } 180