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