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