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