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