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