blob: e6d044f4722baf2310f2859d235c82ab163af8b1 [file] [log] [blame]
Mike Mae45b9b12018-11-13 17:49:43 -08001/*
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 */
16package com.android.internal.os;
17
18import static com.android.internal.os.KernelCpuProcStringReader.asLongs;
19import static com.android.internal.util.Preconditions.checkNotNull;
20
21import android.annotation.NonNull;
22import android.annotation.Nullable;
23import android.os.StrictMode;
24import android.os.SystemClock;
25import android.util.IntArray;
26import android.util.Slog;
27import android.util.SparseArray;
28
29import com.android.internal.annotations.VisibleForTesting;
30import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
31
32import java.io.BufferedReader;
33import java.io.FileWriter;
34import java.io.IOException;
35import java.nio.CharBuffer;
36import java.nio.file.Files;
37import java.nio.file.Path;
38import 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 */
52public 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 */
73 void onUidCpuTime(int uid, T time);
74 }
75
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 */
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 */
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
124 abstract void readDeltaImpl(@Nullable Callback<T> cb);
125
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 */
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 */
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 */
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.
Mike Ma7ab7fcd2019-01-16 15:00:40 -0800180 *
181 * The second parameter of the callback is a long[] with 2 elements, [user time in us, system
182 * time in us].
Mike Mae45b9b12018-11-13 17:49:43 -0800183 */
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
192 public KernelCpuUidUserSysTimeReader(boolean throttle) {
193 super(KernelCpuProcStringReader.getUserSysTimeReaderInstance(), throttle);
194 }
195
196 @VisibleForTesting
197 public KernelCpuUidUserSysTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
198 super(reader, throttle);
199 }
200
201 @Override
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
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
259 public void removeUid(int uid) {
260 super.removeUid(uid);
261 removeUidsFromKernelModule(uid, uid);
262 }
263
264 @Override
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 */
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
324 public KernelCpuUidFreqTimeReader(boolean throttle) {
325 this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
326 throttle);
327 }
328
329 @VisibleForTesting
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 */
339 public boolean perClusterTimesAvailable() {
340 return mPerClusterTimesAvailable;
341 }
342
343 /**
344 * @return Whether all-UID times are available.
345 */
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 */
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 */
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
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
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
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
483 private void copyToCurTimes() {
484 for (int i = 0; i < mFreqCount; i++) {
485 mCurTimes[i] = mBuffer[i + 1] * 10;
486 }
487 }
488
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 */
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
546 public KernelCpuUidActiveTimeReader(boolean throttle) {
547 super(KernelCpuProcStringReader.getActiveTimeReaderInstance(), throttle);
548 }
549
550 @VisibleForTesting
551 public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
552 super(reader, throttle);
553 }
554
555 @Override
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
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
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
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
666 public KernelCpuUidClusterTimeReader(boolean throttle) {
667 super(KernelCpuProcStringReader.getClusterTimeReaderInstance(), throttle);
668 }
669
670 @VisibleForTesting
671 public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
672 super(reader, throttle);
673 }
674
675 @Override
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
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
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
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}