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