1 /*
2  * Copyright (C) 2018 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 package com.android.internal.os;
17 
18 import static com.android.internal.os.KernelCpuProcStringReader.asLongs;
19 import static com.android.internal.util.Preconditions.checkNotNull;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.os.StrictMode;
24 import android.util.IntArray;
25 import android.util.Slog;
26 import android.util.SparseArray;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
30 import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;
31 
32 import java.io.BufferedReader;
33 import java.io.FileWriter;
34 import java.io.IOException;
35 import java.nio.CharBuffer;
36 import java.nio.file.Files;
37 import java.nio.file.Path;
38 import java.nio.file.Paths;
39 
40 /**
41  * Reads per-UID CPU time proc files. Concrete implementations are all nested inside.
42  *
43  * This class uses a throttler to reject any {@link #readDelta} or {@link #readAbsolute} call
44  * within {@link #mMinTimeBetweenRead}. The throttler can be enable / disabled via a param in
45  * the constructor.
46  *
47  * This class and its subclasses are NOT thread-safe and NOT designed to be accessed by more than
48  * one caller since each caller has its own view of delta.
49  *
50  * @param <T> The type of CPU time for the callback.
51  */
52 public abstract class KernelCpuUidTimeReader<T> {
53     protected static final boolean DEBUG = false;
54     private static final long DEFAULT_MIN_TIME_BETWEEN_READ = 1000L; // In milliseconds
55 
56     final String mTag = this.getClass().getSimpleName();
57     final SparseArray<T> mLastTimes = new SparseArray<>();
58     final KernelCpuProcStringReader mReader;
59     final boolean mThrottle;
60     protected boolean mBpfTimesAvailable;
61     final KernelCpuUidBpfMapReader mBpfReader;
62     private final Clock mClock;
63     private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
64     private long mLastReadTimeMs = 0;
65 
66     /**
67      * Callback interface for processing each line of the proc file.
68      *
69      * @param <T> The type of CPU time for the callback function.
70      */
71     public interface Callback<T> {
72         /**
73          * @param uid  UID of the app
74          * @param time Time spent. The exact data structure depends on subclass implementation.
75          */
onUidCpuTime(int uid, T time)76         void onUidCpuTime(int uid, T time);
77     }
78 
KernelCpuUidTimeReader(KernelCpuProcStringReader reader, @Nullable KernelCpuUidBpfMapReader bpfReader, boolean throttle, Clock clock)79     KernelCpuUidTimeReader(KernelCpuProcStringReader reader,
80             @Nullable KernelCpuUidBpfMapReader bpfReader, boolean throttle, Clock clock) {
81         mReader = reader;
82         mThrottle = throttle;
83         mBpfReader = bpfReader;
84         mClock = clock;
85         mBpfTimesAvailable = (mBpfReader != null);
86     }
87 
KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle, Clock clock)88     KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle, Clock clock) {
89         this(reader, null, throttle, clock);
90     }
91 
92     /**
93      * Reads the proc file, calling into the callback with a delta of time for each UID.
94      *
95      * @param cb The callback to invoke for each line of the proc file. If null,the data is
96      */
readDelta(@ullable Callback<T> cb)97     public void readDelta(@Nullable Callback<T> cb) {
98         readDelta(false, cb);
99     }
100 
101     /**
102      * Reads the proc file, calling into the callback with a delta of time for each UID.
103      *
104      * @param force Ignore the throttling and force read the delta.
105      * @param cb The callback to invoke for each line of the proc file. If null,the data is
106      */
readDelta(boolean force, @Nullable Callback<T> cb)107     public void readDelta(boolean force, @Nullable Callback<T> cb) {
108         if (!mThrottle) {
109             readDeltaImpl(cb, force);
110             return;
111         }
112         final long currTimeMs = mClock.elapsedRealtime();
113         if (!force && currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
114             if (DEBUG) {
115                 Slog.d(mTag, "Throttle readDelta");
116             }
117             return;
118         }
119         readDeltaImpl(cb, force);
120         mLastReadTimeMs = currTimeMs;
121     }
122 
123     /**
124      * Reads the proc file, calling into the callback with cumulative time for each UID.
125      *
126      * @param cb The callback to invoke for each line of the proc file. It cannot be null.
127      */
readAbsolute(Callback<T> cb)128     public void readAbsolute(Callback<T> cb) {
129         if (!mThrottle) {
130             readAbsoluteImpl(cb);
131             return;
132         }
133         final long currTimeMs = mClock.elapsedRealtime();
134         if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
135             if (DEBUG) {
136                 Slog.d(mTag, "Throttle readAbsolute");
137             }
138             return;
139         }
140         readAbsoluteImpl(cb);
141         mLastReadTimeMs = currTimeMs;
142     }
143 
readDeltaImpl(@ullable Callback<T> cb, boolean forceRead)144     abstract void readDeltaImpl(@Nullable Callback<T> cb, boolean forceRead);
145 
readAbsoluteImpl(Callback<T> callback)146     abstract void readAbsoluteImpl(Callback<T> callback);
147 
148     /**
149      * Removes the UID from internal accounting data. This method, overridden in
150      * {@link KernelCpuUidUserSysTimeReader}, also removes the UID from the kernel module.
151      *
152      * @param uid The UID to remove.
153      * @see KernelCpuUidUserSysTimeReader#removeUid(int)
154      */
removeUid(int uid)155     public void removeUid(int uid) {
156         mLastTimes.delete(uid);
157 
158         if (mBpfTimesAvailable) {
159             mBpfReader.removeUidsInRange(uid, uid);
160         }
161     }
162 
163     /**
164      * Removes UIDs in a given range from internal accounting data. This method, overridden in
165      * {@link KernelCpuUidUserSysTimeReader}, also removes the UIDs from the kernel module.
166      *
167      * @param startUid the first uid to remove.
168      * @param endUid   the last uid to remove.
169      * @see KernelCpuUidUserSysTimeReader#removeUidsInRange(int, int)
170      */
removeUidsInRange(int startUid, int endUid)171     public void removeUidsInRange(int startUid, int endUid) {
172         if (endUid < startUid) {
173             Slog.e(mTag, "start UID " + startUid + " > end UID " + endUid);
174             return;
175         }
176         mLastTimes.put(startUid, null);
177         mLastTimes.put(endUid, null);
178         int firstIndex = mLastTimes.indexOfKey(startUid);
179         int lastIndex = mLastTimes.indexOfKey(endUid);
180         mLastTimes.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
181 
182         if (mBpfTimesAvailable) {
183             mBpfReader.removeUidsInRange(startUid, endUid);
184         }
185     }
186 
187     /**
188      * Set the minimum time in milliseconds between reads. If throttle is not enabled, this method
189      * has no effect.
190      *
191      * @param minTimeBetweenRead The minimum time in milliseconds.
192      */
setThrottle(long minTimeBetweenRead)193     public void setThrottle(long minTimeBetweenRead) {
194         if (mThrottle && minTimeBetweenRead >= 0) {
195             mMinTimeBetweenRead = minTimeBetweenRead;
196         }
197     }
198 
199     /**
200      * Reads /proc/uid_cputime/show_uid_stat which has the line format:
201      *
202      * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
203      *
204      * This provides the time a UID's processes spent executing in user-space and kernel-space.
205      * The file contains a monotonically increasing count of time for a single boot. This class
206      * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
207      * delta.
208      *
209      * The second parameter of the callback is a long[] with 2 elements, [user time in us, system
210      * time in us].
211      */
212     public static class KernelCpuUidUserSysTimeReader extends KernelCpuUidTimeReader<long[]> {
213         private static final String REMOVE_UID_PROC_FILE = "/proc/uid_cputime/remove_uid_range";
214 
215         // [uid, user_time, system_time, (maybe) power_in_milli-amp-micro_seconds]
216         private final long[] mBuffer = new long[4];
217         // A reusable array to hold [user_time, system_time] for the callback.
218         private final long[] mUsrSysTime = new long[2];
219 
KernelCpuUidUserSysTimeReader(boolean throttle)220         public KernelCpuUidUserSysTimeReader(boolean throttle) {
221             this(throttle, Clock.SYSTEM_CLOCK);
222         }
223 
KernelCpuUidUserSysTimeReader(boolean throttle, Clock clock)224         public KernelCpuUidUserSysTimeReader(boolean throttle, Clock clock) {
225             super(KernelCpuProcStringReader.getUserSysTimeReaderInstance(), throttle, clock);
226         }
227 
228         @VisibleForTesting
KernelCpuUidUserSysTimeReader(KernelCpuProcStringReader reader, boolean throttle, Clock clock)229         public KernelCpuUidUserSysTimeReader(KernelCpuProcStringReader reader, boolean throttle,
230                 Clock clock) {
231             super(reader, throttle, clock);
232         }
233 
234         @Override
readDeltaImpl(@ullable Callback<long[]> cb, boolean forceRead)235         void readDeltaImpl(@Nullable Callback<long[]> cb, boolean forceRead) {
236             try (ProcFileIterator iter = mReader.open(!mThrottle || forceRead)) {
237                 if (iter == null) {
238                     return;
239                 }
240                 CharBuffer buf;
241                 while ((buf = iter.nextLine()) != null) {
242                     if (asLongs(buf, mBuffer) < 3) {
243                         Slog.wtf(mTag, "Invalid line: " + buf.toString());
244                         continue;
245                     }
246                     final int uid = (int) mBuffer[0];
247                     long[] lastTimes = mLastTimes.get(uid);
248                     if (lastTimes == null) {
249                         lastTimes = new long[2];
250                         mLastTimes.put(uid, lastTimes);
251                     }
252                     final long currUsrTimeUs = mBuffer[1];
253                     final long currSysTimeUs = mBuffer[2];
254                     mUsrSysTime[0] = currUsrTimeUs - lastTimes[0];
255                     mUsrSysTime[1] = currSysTimeUs - lastTimes[1];
256 
257                     if (mUsrSysTime[0] < 0 || mUsrSysTime[1] < 0) {
258                         Slog.e(mTag, "Negative user/sys time delta for UID=" + uid
259                                 + "\nPrev times: u=" + lastTimes[0] + " s=" + lastTimes[1]
260                                 + " Curr times: u=" + currUsrTimeUs + " s=" + currSysTimeUs);
261                     } else if (mUsrSysTime[0] > 0 || mUsrSysTime[1] > 0) {
262                         if (cb != null) {
263                             cb.onUidCpuTime(uid, mUsrSysTime);
264                         }
265                     }
266                     lastTimes[0] = currUsrTimeUs;
267                     lastTimes[1] = currSysTimeUs;
268                 }
269             }
270         }
271 
272         @Override
readAbsoluteImpl(Callback<long[]> cb)273         void readAbsoluteImpl(Callback<long[]> cb) {
274             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
275                 if (iter == null) {
276                     return;
277                 }
278                 CharBuffer buf;
279                 while ((buf = iter.nextLine()) != null) {
280                     if (asLongs(buf, mBuffer) < 3) {
281                         Slog.wtf(mTag, "Invalid line: " + buf.toString());
282                         continue;
283                     }
284                     mUsrSysTime[0] = mBuffer[1]; // User time in microseconds
285                     mUsrSysTime[1] = mBuffer[2]; // System time in microseconds
286                     cb.onUidCpuTime((int) mBuffer[0], mUsrSysTime);
287                 }
288             }
289         }
290 
291         @Override
removeUid(int uid)292         public void removeUid(int uid) {
293             super.removeUid(uid);
294             removeUidsFromKernelModule(uid, uid);
295         }
296 
297         @Override
removeUidsInRange(int startUid, int endUid)298         public void removeUidsInRange(int startUid, int endUid) {
299             super.removeUidsInRange(startUid, endUid);
300             removeUidsFromKernelModule(startUid, endUid);
301         }
302 
303         /**
304          * Removes UIDs in a given range from the kernel module and internal accounting data. Only
305          * {@link BatteryStatsImpl} and its child processes should call this, as the change on
306          * Kernel is
307          * visible system wide.
308          *
309          * @param startUid the first uid to remove
310          * @param endUid   the last uid to remove
311          */
removeUidsFromKernelModule(int startUid, int endUid)312         private void removeUidsFromKernelModule(int startUid, int endUid) {
313             Slog.d(mTag, "Removing uids " + startUid + "-" + endUid);
314             final int oldMask = StrictMode.allowThreadDiskWritesMask();
315             try (FileWriter writer = new FileWriter(REMOVE_UID_PROC_FILE)) {
316                 writer.write(startUid + "-" + endUid);
317                 writer.flush();
318             } catch (IOException e) {
319                 Slog.e(mTag, "failed to remove uids " + startUid + " - " + endUid
320                         + " from uid_cputime module", e);
321             } finally {
322                 StrictMode.setThreadPolicyMask(oldMask);
323             }
324         }
325     }
326 
327     /**
328      * Reads /proc/uid_time_in_state which has the format:
329      *
330      * uid: [freq1] [freq2] [freq3] ...
331      * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
332      * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
333      * ...
334      *
335      * This provides the times a UID's processes spent executing at each different cpu frequency.
336      * The file contains a monotonically increasing count of time for a single boot. This class
337      * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
338      * delta.
339      */
340     public static class KernelCpuUidFreqTimeReader extends KernelCpuUidTimeReader<long[]> {
341         private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
342         // We check the existence of proc file a few times (just in case it is not ready yet when we
343         // start reading) and if it is not available, we simply ignore further read requests.
344         private static final int MAX_ERROR_COUNT = 5;
345 
346         private final Path mProcFilePath;
347         private long[] mBuffer;
348         private long[] mCurTimes;
349         private long[] mDeltaTimes;
350         private long[] mCpuFreqs;
351 
352         private int mFreqCount = 0;
353         private int mErrors = 0;
354         private boolean mPerClusterTimesAvailable;
355         private boolean mAllUidTimesAvailable = true;
356 
KernelCpuUidFreqTimeReader(boolean throttle)357         public KernelCpuUidFreqTimeReader(boolean throttle) {
358             this(throttle, Clock.SYSTEM_CLOCK);
359         }
360 
KernelCpuUidFreqTimeReader(boolean throttle, Clock clock)361         public KernelCpuUidFreqTimeReader(boolean throttle, Clock clock) {
362             this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
363                     KernelCpuUidBpfMapReader.getFreqTimeReaderInstance(), throttle, clock);
364         }
365 
366         @VisibleForTesting
KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader, KernelCpuUidBpfMapReader bpfReader, boolean throttle)367         public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
368                 KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
369             this(procFile, reader, bpfReader, throttle, Clock.SYSTEM_CLOCK);
370         }
371 
KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader, KernelCpuUidBpfMapReader bpfReader, boolean throttle, Clock clock)372         private KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
373                 KernelCpuUidBpfMapReader bpfReader, boolean throttle, Clock clock) {
374             super(reader, bpfReader, throttle, clock);
375             mProcFilePath = Paths.get(procFile);
376         }
377 
378         /**
379          * @return Whether per-cluster times are available.
380          */
perClusterTimesAvailable()381         public boolean perClusterTimesAvailable() {
382             return mPerClusterTimesAvailable;
383         }
384 
385         /**
386          * @return Whether all-UID times are available.
387          */
allUidTimesAvailable()388         public boolean allUidTimesAvailable() {
389             return mAllUidTimesAvailable;
390         }
391 
392         /**
393          * @return A map of all UIDs to their CPU time-in-state array in milliseconds.
394          */
getAllUidCpuFreqTimeMs()395         public SparseArray<long[]> getAllUidCpuFreqTimeMs() {
396             return mLastTimes;
397         }
398 
399         /**
400          * Reads a list of CPU frequencies from /proc/uid_time_in_state. Uses a given PowerProfile
401          * to determine if per-cluster times are available.
402          *
403          * @param powerProfile The PowerProfile to compare against.
404          * @return A long[] of CPU frequencies in Hz.
405          */
readFreqs(@onNull PowerProfile powerProfile)406         public long[] readFreqs(@NonNull PowerProfile powerProfile) {
407             checkNotNull(powerProfile);
408             if (mCpuFreqs != null) {
409                 // No need to read cpu freqs more than once.
410                 return mCpuFreqs;
411             }
412             if (!mAllUidTimesAvailable) {
413                 return null;
414             }
415             if (mBpfTimesAvailable) {
416                 readFreqsThroughBpf();
417             }
418             if (mCpuFreqs == null) {
419                 final int oldMask = StrictMode.allowThreadDiskReadsMask();
420                 try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
421                     if (readFreqs(reader.readLine()) == null) {
422                         return null;
423                     }
424                 } catch (IOException e) {
425                     if (++mErrors >= MAX_ERROR_COUNT) {
426                         mAllUidTimesAvailable = false;
427                     }
428                     Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
429                     return null;
430                 } finally {
431                     StrictMode.setThreadPolicyMask(oldMask);
432                 }
433             }
434             // Check if the freqs in the proc file correspond to per-cluster freqs.
435             final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
436             final int numClusters = powerProfile.getNumCpuClusters();
437             if (numClusterFreqs.size() == numClusters) {
438                 mPerClusterTimesAvailable = true;
439                 for (int i = 0; i < numClusters; ++i) {
440                     if (numClusterFreqs.get(i) != powerProfile.getNumSpeedStepsInCpuCluster(i)) {
441                         mPerClusterTimesAvailable = false;
442                         break;
443                     }
444                 }
445             } else {
446                 mPerClusterTimesAvailable = false;
447             }
448             Slog.i(mTag, "mPerClusterTimesAvailable=" + mPerClusterTimesAvailable);
449             return mCpuFreqs;
450         }
451 
readFreqsThroughBpf()452         private long[] readFreqsThroughBpf() {
453             if (!mBpfTimesAvailable || mBpfReader == null) {
454                 return null;
455             }
456             mCpuFreqs = mBpfReader.getDataDimensions();
457             if (mCpuFreqs == null) {
458                 return null;
459             }
460             mFreqCount = mCpuFreqs.length;
461             mCurTimes = new long[mFreqCount];
462             mDeltaTimes = new long[mFreqCount];
463             mBuffer = new long[mFreqCount + 1];
464             return mCpuFreqs;
465         }
466 
readFreqs(String line)467         private long[] readFreqs(String line) {
468             if (line == null || line.trim().isEmpty()) {
469                 return null;
470             }
471             final String[] lineArray = line.split(" ");
472             if (lineArray.length <= 1) {
473                 Slog.wtf(mTag, "Malformed freq line: " + line);
474                 return null;
475             }
476             mFreqCount = lineArray.length - 1;
477             mCpuFreqs = new long[mFreqCount];
478             mCurTimes = new long[mFreqCount];
479             mDeltaTimes = new long[mFreqCount];
480             mBuffer = new long[mFreqCount + 1];
481             for (int i = 0; i < mFreqCount; ++i) {
482                 mCpuFreqs[i] = Long.parseLong(lineArray[i + 1], 10);
483             }
484             return mCpuFreqs;
485         }
486 
processUidDelta(@ullable Callback<long[]> cb)487         private void processUidDelta(@Nullable Callback<long[]> cb) {
488             final int uid = (int) mBuffer[0];
489             long[] lastTimes = mLastTimes.get(uid);
490             if (lastTimes == null) {
491                 lastTimes = new long[mFreqCount];
492                 mLastTimes.put(uid, lastTimes);
493             }
494             copyToCurTimes();
495             boolean notify = false;
496             for (int i = 0; i < mFreqCount; i++) {
497                 // Unit is 10ms.
498                 mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
499                 if (mDeltaTimes[i] < 0) {
500                     Slog.e(mTag, "Negative delta from freq time for uid: " + uid
501                             + ", delta: " + mDeltaTimes[i]);
502                     return;
503                 }
504                 notify |= mDeltaTimes[i] > 0;
505             }
506             if (notify) {
507                 System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
508                 if (cb != null) {
509                     cb.onUidCpuTime(uid, mDeltaTimes);
510                 }
511             }
512         }
513 
514         @Override
readDeltaImpl(@ullable Callback<long[]> cb, boolean forceRead)515         void readDeltaImpl(@Nullable Callback<long[]> cb, boolean forceRead) {
516             if (mBpfTimesAvailable) {
517                 try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
518                     if (checkPrecondition(iter)) {
519                         while (iter.getNextUid(mBuffer)) {
520                             processUidDelta(cb);
521                         }
522                         return;
523                     }
524                 }
525             }
526             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
527                 if (!checkPrecondition(iter)) {
528                     return;
529                 }
530                 CharBuffer buf;
531                 while ((buf = iter.nextLine()) != null) {
532                     if (asLongs(buf, mBuffer) != mBuffer.length) {
533                         Slog.wtf(mTag, "Invalid line: " + buf.toString());
534                         continue;
535                     }
536                     processUidDelta(cb);
537                 }
538             }
539         }
540 
541         @Override
readAbsoluteImpl(Callback<long[]> cb)542         void readAbsoluteImpl(Callback<long[]> cb) {
543             if (mBpfTimesAvailable) {
544                 try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
545                     if (checkPrecondition(iter)) {
546                         while (iter.getNextUid(mBuffer)) {
547                             copyToCurTimes();
548                             cb.onUidCpuTime((int) mBuffer[0], mCurTimes);
549                         }
550                         return;
551                     }
552                 }
553             }
554             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
555                 if (!checkPrecondition(iter)) {
556                     return;
557                 }
558                 CharBuffer buf;
559                 while ((buf = iter.nextLine()) != null) {
560                     if (asLongs(buf, mBuffer) != mBuffer.length) {
561                         Slog.wtf(mTag, "Invalid line: " + buf.toString());
562                         continue;
563                     }
564                     copyToCurTimes();
565                     cb.onUidCpuTime((int) mBuffer[0], mCurTimes);
566                 }
567             }
568         }
569 
copyToCurTimes()570         private void copyToCurTimes() {
571             long factor = mBpfTimesAvailable ? 1 : 10;
572             for (int i = 0; i < mFreqCount; i++) {
573                 mCurTimes[i] = mBuffer[i + 1] * factor;
574             }
575         }
576 
checkPrecondition(BpfMapIterator iter)577         private boolean checkPrecondition(BpfMapIterator iter) {
578             if (iter == null) {
579                 mBpfTimesAvailable = false;
580                 return false;
581             }
582             if (mCpuFreqs != null) {
583                 return true;
584             }
585             mBpfTimesAvailable = (readFreqsThroughBpf() != null);
586             return mBpfTimesAvailable;
587         }
588 
checkPrecondition(ProcFileIterator iter)589         private boolean checkPrecondition(ProcFileIterator iter) {
590             if (iter == null || !iter.hasNextLine()) {
591                 // Error logged in KernelCpuProcStringReader.
592                 return false;
593             }
594             CharBuffer line = iter.nextLine();
595             if (mCpuFreqs != null) {
596                 return true;
597             }
598             return readFreqs(line.toString()) != null;
599         }
600 
601         /**
602          * Extracts no. of cpu clusters and no. of freqs in each of these clusters from the freqs
603          * read from the proc file.
604          *
605          * We need to assume that freqs in each cluster are strictly increasing.
606          * For e.g. if the freqs read from proc file are: 12, 34, 15, 45, 12, 15, 52. Then it means
607          * there are 3 clusters: (12, 34), (15, 45), (12, 15, 52)
608          *
609          * @return an IntArray filled with no. of freqs in each cluster.
610          */
extractClusterInfoFromProcFileFreqs()611         private IntArray extractClusterInfoFromProcFileFreqs() {
612             final IntArray numClusterFreqs = new IntArray();
613             int freqsFound = 0;
614             for (int i = 0; i < mFreqCount; ++i) {
615                 freqsFound++;
616                 if (i + 1 == mFreqCount || mCpuFreqs[i + 1] <= mCpuFreqs[i]) {
617                     numClusterFreqs.add(freqsFound);
618                     freqsFound = 0;
619                 }
620             }
621             return numClusterFreqs;
622         }
623 
isFastCpuTimesReader()624         public boolean isFastCpuTimesReader() {
625             return mBpfTimesAvailable;
626         }
627     }
628 
629     /**
630      * Reads /proc/uid_concurrent_active_time and reports CPU active time to BatteryStats to
631      * compute {@link PowerProfile#POWER_CPU_ACTIVE}.
632      *
633      * /proc/uid_concurrent_active_time has the following format:
634      * cpus: n
635      * uid0: time0a, time0b, ..., time0n,
636      * uid1: time1a, time1b, ..., time1n,
637      * uid2: time2a, time2b, ..., time2n,
638      * ...
639      * where n is the total number of cpus (num_possible_cpus)
640      * timeXn means the CPU time that a UID X spent running concurrently with n other processes.
641      *
642      * The file contains a monotonically increasing count of time for a single boot. This class
643      * maintains the previous results of a call to {@link #readDelta} in order to provide a
644      * proper delta.
645      */
646     public static class KernelCpuUidActiveTimeReader extends KernelCpuUidTimeReader<Long> {
647         private int mCores = 0;
648         private long[] mBuffer;
649 
KernelCpuUidActiveTimeReader(boolean throttle)650         public KernelCpuUidActiveTimeReader(boolean throttle) {
651             this(throttle, Clock.SYSTEM_CLOCK);
652         }
653 
KernelCpuUidActiveTimeReader(boolean throttle, Clock clock)654         public KernelCpuUidActiveTimeReader(boolean throttle, Clock clock) {
655             super(KernelCpuProcStringReader.getActiveTimeReaderInstance(),
656                     KernelCpuUidBpfMapReader.getActiveTimeReaderInstance(), throttle, clock);
657         }
658 
659         @VisibleForTesting
KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, KernelCpuUidBpfMapReader bpfReader, boolean throttle)660         public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader,
661                 KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
662             super(reader, bpfReader, throttle, Clock.SYSTEM_CLOCK);
663         }
664 
processUidDelta(@ullable Callback<Long> cb)665         private void processUidDelta(@Nullable Callback<Long> cb) {
666             int uid = (int) mBuffer[0];
667             long cpuActiveTime = sumActiveTime(mBuffer, mBpfTimesAvailable ? 1 : 10);
668             if (cpuActiveTime > 0) {
669                 long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
670                 if (delta > 0) {
671                     mLastTimes.put(uid, cpuActiveTime);
672                     if (cb != null) {
673                         cb.onUidCpuTime(uid, delta);
674                     }
675                 } else if (delta < 0) {
676                     Slog.e(mTag, "Negative delta from active time for uid: " + uid
677                             + ", delta: " + delta);
678                 }
679             }
680         }
681 
682         @Override
readDeltaImpl(@ullable Callback<Long> cb, boolean forceRead)683         void readDeltaImpl(@Nullable Callback<Long> cb, boolean forceRead) {
684             if (mBpfTimesAvailable) {
685                 try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
686                     if (checkPrecondition(iter)) {
687                         while (iter.getNextUid(mBuffer)) {
688                             processUidDelta(cb);
689                         }
690                         return;
691                     }
692                 }
693             }
694             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
695                 if (!checkPrecondition(iter)) {
696                     return;
697                 }
698                 CharBuffer buf;
699                 while ((buf = iter.nextLine()) != null) {
700                     if (asLongs(buf, mBuffer) != mBuffer.length) {
701                         Slog.wtf(mTag, "Invalid line: " + buf.toString());
702                         continue;
703                     }
704                     processUidDelta(cb);
705                 }
706             }
707         }
708 
processUidAbsolute(@ullable Callback<Long> cb)709         private void processUidAbsolute(@Nullable Callback<Long> cb) {
710             long cpuActiveTime = sumActiveTime(mBuffer, mBpfTimesAvailable ? 1 : 10);
711             if (cpuActiveTime > 0) {
712                 cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
713             }
714         }
715 
716         @Override
readAbsoluteImpl(Callback<Long> cb)717         void readAbsoluteImpl(Callback<Long> cb) {
718             if (mBpfTimesAvailable) {
719                 try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
720                     if (checkPrecondition(iter)) {
721                         while (iter.getNextUid(mBuffer)) {
722                             processUidAbsolute(cb);
723                         }
724                         return;
725                     }
726                 }
727             }
728             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
729                 if (!checkPrecondition(iter)) {
730                     return;
731                 }
732                 CharBuffer buf;
733                 while ((buf = iter.nextLine()) != null) {
734                     if (asLongs(buf, mBuffer) != mBuffer.length) {
735                         Slog.wtf(mTag, "Invalid line: " + buf.toString());
736                         continue;
737                     }
738                     processUidAbsolute(cb);
739                 }
740             }
741         }
742 
sumActiveTime(long[] times, double factor)743         private static long sumActiveTime(long[] times, double factor) {
744             // UID is stored at times[0].
745             double sum = 0;
746             for (int i = 1; i < times.length; i++) {
747                 sum += (double) times[i] * factor / i; // Unit is 10ms.
748             }
749             return (long) sum;
750         }
751 
checkPrecondition(BpfMapIterator iter)752         private boolean checkPrecondition(BpfMapIterator iter) {
753             if (iter == null) {
754                 mBpfTimesAvailable = false;
755                 return false;
756             }
757             if (mCores > 0) {
758                 return true;
759             }
760             long[] cores = mBpfReader.getDataDimensions();
761             if (cores == null || cores.length < 1) {
762                 mBpfTimesAvailable = false;
763                 return false;
764             }
765             mCores = (int) cores[0];
766             mBuffer = new long[mCores + 1];
767             return true;
768         }
769 
checkPrecondition(ProcFileIterator iter)770         private boolean checkPrecondition(ProcFileIterator iter) {
771             if (iter == null || !iter.hasNextLine()) {
772                 // Error logged in KernelCpuProcStringReader.
773                 return false;
774             }
775             CharBuffer line = iter.nextLine();
776             if (mCores > 0) {
777                 return true;
778             }
779 
780             String str = line.toString().trim();
781             if (str.isEmpty()) {
782                 Slog.w(mTag, "Empty uid_concurrent_active_time");
783                 return false;
784             }
785             if (!str.startsWith("cpus:")) {
786                 Slog.wtf(mTag, "Malformed uid_concurrent_active_time line: " + str);
787                 return false;
788             }
789             int cores = Integer.parseInt(str.substring(5).trim(), 10);
790             if (cores <= 0) {
791                 Slog.wtf(mTag, "Malformed uid_concurrent_active_time line: " + str);
792                 return false;
793             }
794             mCores = cores;
795             mBuffer = new long[mCores + 1]; // UID is stored at mBuffer[0].
796             return true;
797         }
798     }
799 
800 
801     /**
802      * Reads /proc/uid_concurrent_policy_time and reports CPU cluster times to BatteryStats to
803      * compute cluster power. See {@link PowerProfile#getAveragePowerForCpuCluster(int)}.
804      *
805      * /proc/uid_concurrent_policy_time has the following format:
806      * policyX: x policyY: y policyZ: z...
807      * uid1, time1a, time1b, ..., time1n,
808      * uid2, time2a, time2b, ..., time2n,
809      * ...
810      * The first line lists all policies (i.e. clusters) followed by # cores in each policy.
811      * Each uid is followed by x time entries corresponding to the time it spent on clusterX
812      * running concurrently with 0, 1, 2, ..., x - 1 other processes, then followed by y, z, ...
813      * time entries.
814      *
815      * The file contains a monotonically increasing count of time for a single boot. This class
816      * maintains the previous results of a call to {@link #readDelta} in order to provide a
817      * proper delta.
818      */
819     public static class KernelCpuUidClusterTimeReader extends KernelCpuUidTimeReader<long[]> {
820         private int mNumClusters;
821         private int mNumCores;
822         private int[] mCoresOnClusters; // # cores on each cluster.
823         private long[] mBuffer; // To store data returned from ProcFileIterator.
824         private long[] mCurTime;
825         private long[] mDeltaTime;
826 
KernelCpuUidClusterTimeReader(boolean throttle)827         public KernelCpuUidClusterTimeReader(boolean throttle) {
828             this(throttle, Clock.SYSTEM_CLOCK);
829         }
830 
KernelCpuUidClusterTimeReader(boolean throttle, Clock clock)831         public KernelCpuUidClusterTimeReader(boolean throttle, Clock clock) {
832             super(KernelCpuProcStringReader.getClusterTimeReaderInstance(),
833                     KernelCpuUidBpfMapReader.getClusterTimeReaderInstance(), throttle, clock);
834         }
835 
836         @VisibleForTesting
KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader, KernelCpuUidBpfMapReader bpfReader, boolean throttle)837         public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader,
838                 KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
839             super(reader, bpfReader, throttle, Clock.SYSTEM_CLOCK);
840         }
841 
processUidDelta(@ullable Callback<long[]> cb)842         void processUidDelta(@Nullable Callback<long[]> cb) {
843             int uid = (int) mBuffer[0];
844             long[] lastTimes = mLastTimes.get(uid);
845             if (lastTimes == null) {
846                 lastTimes = new long[mNumClusters];
847                 mLastTimes.put(uid, lastTimes);
848             }
849             sumClusterTime();
850             boolean valid = true;
851             boolean notify = false;
852             for (int i = 0; i < mNumClusters; i++) {
853                 mDeltaTime[i] = mCurTime[i] - lastTimes[i];
854                 if (mDeltaTime[i] < 0) {
855                     Slog.e(mTag, "Negative delta from cluster time for uid: " + uid
856                             + ", delta: " + mDeltaTime[i]);
857                     return;
858                 }
859                 notify |= mDeltaTime[i] > 0;
860             }
861             if (notify) {
862                 System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
863                 if (cb != null) {
864                     cb.onUidCpuTime(uid, mDeltaTime);
865                 }
866             }
867         }
868 
869         @Override
readDeltaImpl(@ullable Callback<long[]> cb, boolean forceRead)870         void readDeltaImpl(@Nullable Callback<long[]> cb, boolean forceRead) {
871             if (mBpfTimesAvailable) {
872                 try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
873                     if (checkPrecondition(iter)) {
874                         while (iter.getNextUid(mBuffer)) {
875                             processUidDelta(cb);
876                         }
877                         return;
878                     }
879                 }
880             }
881             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
882                 if (!checkPrecondition(iter)) {
883                     return;
884                 }
885                 CharBuffer buf;
886                 while ((buf = iter.nextLine()) != null) {
887                     if (asLongs(buf, mBuffer) != mBuffer.length) {
888                         Slog.wtf(mTag, "Invalid line: " + buf.toString());
889                         continue;
890                     }
891                     processUidDelta(cb);
892                 }
893             }
894         }
895 
896         @Override
readAbsoluteImpl(Callback<long[]> cb)897         void readAbsoluteImpl(Callback<long[]> cb) {
898             if (mBpfTimesAvailable) {
899                 try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
900                     if (checkPrecondition(iter)) {
901                         while (iter.getNextUid(mBuffer)) {
902                             sumClusterTime();
903                             cb.onUidCpuTime((int) mBuffer[0], mCurTime);
904                         }
905                         return;
906                     }
907                 }
908             }
909             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
910                 if (!checkPrecondition(iter)) {
911                     return;
912                 }
913                 CharBuffer buf;
914                 while ((buf = iter.nextLine()) != null) {
915                     if (asLongs(buf, mBuffer) != mBuffer.length) {
916                         Slog.wtf(mTag, "Invalid line: " + buf.toString());
917                         continue;
918                     }
919                     sumClusterTime();
920                     cb.onUidCpuTime((int) mBuffer[0], mCurTime);
921                 }
922             }
923         }
924 
sumClusterTime()925         private void sumClusterTime() {
926             double factor = mBpfTimesAvailable ? 1 : 10;
927             // UID is stored at mBuffer[0].
928             int core = 1;
929             for (int i = 0; i < mNumClusters; i++) {
930                 double sum = 0;
931                 for (int j = 1; j <= mCoresOnClusters[i]; j++) {
932                     sum += (double) mBuffer[core++] * factor / j; // Unit is 10ms.
933                 }
934                 mCurTime[i] = (long) sum;
935             }
936         }
937 
checkPrecondition(BpfMapIterator iter)938         private boolean checkPrecondition(BpfMapIterator iter) {
939             if (iter == null) {
940                 mBpfTimesAvailable = false;
941                 return false;
942             }
943             if (mNumClusters > 0) {
944                 return true;
945             }
946             long[] coresOnClusters = mBpfReader.getDataDimensions();
947             if (coresOnClusters == null || coresOnClusters.length < 1) {
948                 mBpfTimesAvailable = false;
949                 return false;
950             }
951             mNumClusters = coresOnClusters.length;
952             mCoresOnClusters = new int[mNumClusters];
953             int cores = 0;
954             for (int i = 0; i < mNumClusters; i++) {
955                 mCoresOnClusters[i] = (int) coresOnClusters[i];
956                 cores += mCoresOnClusters[i];
957             }
958             mNumCores = cores;
959             mBuffer = new long[cores + 1];
960             mCurTime = new long[mNumClusters];
961             mDeltaTime = new long[mNumClusters];
962             return true;
963         }
964 
checkPrecondition(ProcFileIterator iter)965         private boolean checkPrecondition(ProcFileIterator iter) {
966             if (iter == null || !iter.hasNextLine()) {
967                 // Error logged in KernelCpuProcStringReader.
968                 return false;
969             }
970             CharBuffer line = iter.nextLine();
971             if (mNumClusters > 0) {
972                 return true;
973             }
974             String lineStr = line.toString().trim();
975             if (lineStr.isEmpty()) {
976                 Slog.w(mTag, "Empty uid_concurrent_policy_time");
977                 return false;
978             }
979             // Parse # cores in clusters.
980             String[] lineArray = lineStr.split(" ");
981             if (lineArray.length % 2 != 0) {
982                 Slog.wtf(mTag, "Malformed uid_concurrent_policy_time line: " + lineStr);
983                 return false;
984             }
985             int[] clusters = new int[lineArray.length / 2];
986             int cores = 0;
987             for (int i = 0; i < clusters.length; i++) {
988                 if (!lineArray[i * 2].startsWith("policy")) {
989                     Slog.wtf(mTag, "Malformed uid_concurrent_policy_time line: " + lineStr);
990                     return false;
991                 }
992                 clusters[i] = Integer.parseInt(lineArray[i * 2 + 1], 10);
993                 cores += clusters[i];
994             }
995             mNumClusters = clusters.length;
996             mNumCores = cores;
997             mCoresOnClusters = clusters;
998             mBuffer = new long[cores + 1];
999             mCurTime = new long[mNumClusters];
1000             mDeltaTime = new long[mNumClusters];
1001             return true;
1002         }
1003     }
1004 
1005 }
1006