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