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