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