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