1 /* 2 * Copyright (C) 2023 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 17 package com.android.server.power.stats; 18 19 import android.hardware.power.stats.EnergyConsumerType; 20 import android.os.BatteryConsumer; 21 import android.os.Handler; 22 import android.os.PersistableBundle; 23 import android.os.Process; 24 import android.util.Slog; 25 import android.util.SparseArray; 26 27 import com.android.internal.annotations.Keep; 28 import com.android.internal.annotations.VisibleForNative; 29 import com.android.internal.annotations.VisibleForTesting; 30 import com.android.internal.os.Clock; 31 import com.android.internal.os.CpuScalingPolicies; 32 import com.android.internal.os.PowerProfile; 33 import com.android.internal.os.PowerStats; 34 35 import java.io.PrintWriter; 36 import java.util.Arrays; 37 import java.util.Locale; 38 import java.util.function.IntSupplier; 39 40 /** 41 * Collects snapshots of power-related system statistics. 42 * <p> 43 * The class is intended to be used in a serialized fashion using the handler supplied in the 44 * constructor. Thus the object is not thread-safe except where noted. 45 */ 46 public class CpuPowerStatsCollector extends PowerStatsCollector { 47 private static final String TAG = "CpuPowerStatsCollector"; 48 private static final long NANOS_PER_MILLIS = 1000000; 49 private static final long ENERGY_UNSPECIFIED = -1; 50 private static final int DEFAULT_CPU_POWER_BRACKETS = 3; 51 private static final int DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER = 2; 52 53 interface Injector { getHandler()54 Handler getHandler(); getClock()55 Clock getClock(); getUidResolver()56 PowerStatsUidResolver getUidResolver(); getCpuScalingPolicies()57 CpuScalingPolicies getCpuScalingPolicies(); getPowerProfile()58 PowerProfile getPowerProfile(); getKernelCpuStatsReader()59 KernelCpuStatsReader getKernelCpuStatsReader(); getConsumedEnergyRetriever()60 ConsumedEnergyRetriever getConsumedEnergyRetriever(); getVoltageSupplier()61 IntSupplier getVoltageSupplier(); getPowerStatsCollectionThrottlePeriod(String powerComponentName)62 long getPowerStatsCollectionThrottlePeriod(String powerComponentName); 63 getDefaultCpuPowerBrackets()64 default int getDefaultCpuPowerBrackets() { 65 return DEFAULT_CPU_POWER_BRACKETS; 66 } 67 getDefaultCpuPowerBracketsPerEnergyConsumer()68 default int getDefaultCpuPowerBracketsPerEnergyConsumer() { 69 return DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER; 70 } 71 } 72 73 private final Injector mInjector; 74 75 private boolean mIsInitialized; 76 private CpuScalingPolicies mCpuScalingPolicies; 77 private PowerProfile mPowerProfile; 78 private KernelCpuStatsReader mKernelCpuStatsReader; 79 private ConsumedEnergyRetriever mConsumedEnergyRetriever; 80 private IntSupplier mVoltageSupplier; 81 private int mDefaultCpuPowerBrackets; 82 private int mDefaultCpuPowerBracketsPerEnergyConsumer; 83 private long[] mCpuTimeByScalingStep; 84 private long[] mTempCpuTimeByScalingStep; 85 private long[] mTempUidStats; 86 private final SparseArray<UidStats> mUidStats = new SparseArray<>(); 87 private boolean mIsPerUidTimeInStateSupported; 88 private int[] mCpuEnergyConsumerIds = new int[0]; 89 private PowerStats.Descriptor mPowerStatsDescriptor; 90 // Reusable instance 91 private PowerStats mCpuPowerStats; 92 private CpuPowerStatsLayout mLayout; 93 private long mLastUpdateTimestampNanos; 94 private long mLastUpdateUptimeMillis; 95 private int mLastVoltageMv; 96 private long[] mLastConsumedEnergyUws; 97 CpuPowerStatsCollector(Injector injector)98 CpuPowerStatsCollector(Injector injector) { 99 super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod( 100 BatteryConsumer.powerComponentIdToString( 101 BatteryConsumer.POWER_COMPONENT_CPU)), 102 injector.getUidResolver(), injector.getClock()); 103 mInjector = injector; 104 } 105 ensureInitialized()106 private boolean ensureInitialized() { 107 if (mIsInitialized) { 108 return true; 109 } 110 111 if (!isEnabled()) { 112 return false; 113 } 114 115 mCpuScalingPolicies = mInjector.getCpuScalingPolicies(); 116 mPowerProfile = mInjector.getPowerProfile(); 117 mKernelCpuStatsReader = mInjector.getKernelCpuStatsReader(); 118 mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever(); 119 mVoltageSupplier = mInjector.getVoltageSupplier(); 120 mDefaultCpuPowerBrackets = mInjector.getDefaultCpuPowerBrackets(); 121 mDefaultCpuPowerBracketsPerEnergyConsumer = 122 mInjector.getDefaultCpuPowerBracketsPerEnergyConsumer(); 123 124 mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.isSupportedFeature(); 125 mCpuEnergyConsumerIds = 126 mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER); 127 mLastConsumedEnergyUws = new long[mCpuEnergyConsumerIds.length]; 128 Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED); 129 130 int cpuScalingStepCount = mCpuScalingPolicies.getScalingStepCount(); 131 mCpuTimeByScalingStep = new long[cpuScalingStepCount]; 132 mTempCpuTimeByScalingStep = new long[cpuScalingStepCount]; 133 int[] scalingStepToPowerBracketMap = initPowerBrackets(); 134 135 mLayout = new CpuPowerStatsLayout(); 136 mLayout.addDeviceSectionCpuTimeByScalingStep(cpuScalingStepCount); 137 mLayout.addDeviceSectionCpuTimeByCluster(mCpuScalingPolicies.getPolicies().length); 138 mLayout.addDeviceSectionUsageDuration(); 139 mLayout.addDeviceSectionEnergyConsumers(mCpuEnergyConsumerIds.length); 140 mLayout.addDeviceSectionPowerEstimate(); 141 mLayout.addUidSectionCpuTimeByPowerBracket(scalingStepToPowerBracketMap); 142 mLayout.addUidSectionPowerEstimate(); 143 144 PersistableBundle extras = new PersistableBundle(); 145 mLayout.toExtras(extras); 146 147 mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 148 mLayout.getDeviceStatsArrayLength(), /* stateLabels */null, 149 /* stateStatsArrayLength */ 0, mLayout.getUidStatsArrayLength(), extras); 150 mCpuPowerStats = new PowerStats(mPowerStatsDescriptor); 151 152 mTempUidStats = new long[mLayout.getCpuPowerBracketCount()]; 153 154 mIsInitialized = true; 155 return true; 156 } 157 initPowerBrackets()158 private int[] initPowerBrackets() { 159 if (mPowerProfile.getCpuPowerBracketCount() != PowerProfile.POWER_BRACKETS_UNSPECIFIED) { 160 return initPowerBracketsFromPowerProfile(); 161 } else if (mCpuEnergyConsumerIds.length == 0 || mCpuEnergyConsumerIds.length == 1) { 162 return initDefaultPowerBrackets(mDefaultCpuPowerBrackets); 163 } else if (mCpuScalingPolicies.getPolicies().length == mCpuEnergyConsumerIds.length) { 164 return initPowerBracketsByCluster(mDefaultCpuPowerBracketsPerEnergyConsumer); 165 } else { 166 Slog.i(TAG, "Assigning a single power brackets to each CPU_CLUSTER energy consumer." 167 + " Number of CPU clusters (" 168 + mCpuScalingPolicies.getPolicies().length 169 + ") does not match the number of energy consumers (" 170 + mCpuEnergyConsumerIds.length + "). " 171 + " Using default power bucket assignment."); 172 return initDefaultPowerBrackets(mDefaultCpuPowerBrackets); 173 } 174 } 175 initPowerBracketsFromPowerProfile()176 private int[] initPowerBracketsFromPowerProfile() { 177 int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()]; 178 int index = 0; 179 for (int policy : mCpuScalingPolicies.getPolicies()) { 180 int[] frequencies = mCpuScalingPolicies.getFrequencies(policy); 181 for (int step = 0; step < frequencies.length; step++) { 182 int bracket = mPowerProfile.getCpuPowerBracketForScalingStep(policy, step); 183 stepToBracketMap[index++] = bracket; 184 } 185 } 186 return stepToBracketMap; 187 } 188 189 initPowerBracketsByCluster(int defaultBracketCountPerCluster)190 private int[] initPowerBracketsByCluster(int defaultBracketCountPerCluster) { 191 int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()]; 192 int index = 0; 193 int bracketBase = 0; 194 int[] policies = mCpuScalingPolicies.getPolicies(); 195 for (int policy : policies) { 196 int[] frequencies = mCpuScalingPolicies.getFrequencies(policy); 197 double[] powerByStep = new double[frequencies.length]; 198 for (int step = 0; step < frequencies.length; step++) { 199 powerByStep[step] = mPowerProfile.getAveragePowerForCpuScalingStep(policy, step); 200 } 201 202 int[] policyStepToBracketMap = new int[frequencies.length]; 203 mapScalingStepsToDefaultBrackets(policyStepToBracketMap, powerByStep, 204 defaultBracketCountPerCluster); 205 int maxBracket = 0; 206 for (int step = 0; step < frequencies.length; step++) { 207 int bracket = bracketBase + policyStepToBracketMap[step]; 208 stepToBracketMap[index++] = bracket; 209 if (bracket > maxBracket) { 210 maxBracket = bracket; 211 } 212 } 213 bracketBase = maxBracket + 1; 214 } 215 return stepToBracketMap; 216 } 217 initDefaultPowerBrackets(int defaultCpuPowerBracketCount)218 private int[] initDefaultPowerBrackets(int defaultCpuPowerBracketCount) { 219 int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()]; 220 double[] powerByStep = new double[mCpuScalingPolicies.getScalingStepCount()]; 221 int index = 0; 222 int[] policies = mCpuScalingPolicies.getPolicies(); 223 for (int policy : policies) { 224 int[] frequencies = mCpuScalingPolicies.getFrequencies(policy); 225 for (int step = 0; step < frequencies.length; step++) { 226 powerByStep[index++] = mPowerProfile.getAveragePowerForCpuScalingStep(policy, step); 227 } 228 } 229 mapScalingStepsToDefaultBrackets(stepToBracketMap, powerByStep, 230 defaultCpuPowerBracketCount); 231 return stepToBracketMap; 232 } 233 mapScalingStepsToDefaultBrackets(int[] stepToBracketMap, double[] powerByStep, int defaultCpuPowerBracketCount)234 private static void mapScalingStepsToDefaultBrackets(int[] stepToBracketMap, 235 double[] powerByStep, int defaultCpuPowerBracketCount) { 236 double minPower = Double.MAX_VALUE; 237 double maxPower = Double.MIN_VALUE; 238 for (final double power : powerByStep) { 239 if (power < minPower) { 240 minPower = power; 241 } 242 if (power > maxPower) { 243 maxPower = power; 244 } 245 } 246 if (powerByStep.length <= defaultCpuPowerBracketCount) { 247 for (int index = 0; index < stepToBracketMap.length; index++) { 248 stepToBracketMap[index] = index; 249 } 250 } else { 251 final double minLogPower = Math.log(minPower); 252 final double logBracket = (Math.log(maxPower) - minLogPower) 253 / defaultCpuPowerBracketCount; 254 255 for (int step = 0; step < powerByStep.length; step++) { 256 int bracket = (int) ((Math.log(powerByStep[step]) - minLogPower) / logBracket); 257 if (bracket >= defaultCpuPowerBracketCount) { 258 bracket = defaultCpuPowerBracketCount - 1; 259 } 260 stepToBracketMap[step] = bracket; 261 } 262 } 263 } 264 265 /** 266 * Prints the definitions of power brackets. 267 */ dumpCpuPowerBracketsLocked(PrintWriter pw)268 public void dumpCpuPowerBracketsLocked(PrintWriter pw) { 269 if (!ensureInitialized()) { 270 return; 271 } 272 273 if (mLayout == null) { 274 return; 275 } 276 277 pw.println("CPU power brackets; cluster/freq in MHz(avg current in mA):"); 278 for (int bracket = 0; bracket < mLayout.getCpuPowerBracketCount(); bracket++) { 279 pw.print(" "); 280 pw.print(bracket); 281 pw.print(": "); 282 pw.println(getCpuPowerBracketDescription(bracket)); 283 } 284 } 285 286 /** 287 * Description of a CPU power bracket: which cluster/frequency combinations are included. 288 */ 289 @VisibleForTesting getCpuPowerBracketDescription(int powerBracket)290 public String getCpuPowerBracketDescription(int powerBracket) { 291 if (!ensureInitialized()) { 292 return ""; 293 } 294 295 int[] stepToPowerBracketMap = mLayout.getScalingStepToPowerBracketMap(); 296 StringBuilder sb = new StringBuilder(); 297 int index = 0; 298 int[] policies = mCpuScalingPolicies.getPolicies(); 299 for (int policy : policies) { 300 int[] freqs = mCpuScalingPolicies.getFrequencies(policy); 301 for (int step = 0; step < freqs.length; step++) { 302 if (stepToPowerBracketMap[index] != powerBracket) { 303 index++; 304 continue; 305 } 306 307 if (sb.length() != 0) { 308 sb.append(", "); 309 } 310 if (policies.length > 1) { 311 sb.append(policy).append('/'); 312 } 313 sb.append(freqs[step] / 1000); 314 sb.append('('); 315 sb.append(String.format(Locale.US, "%.1f", 316 mPowerProfile.getAveragePowerForCpuScalingStep(policy, step))); 317 sb.append(')'); 318 319 index++; 320 } 321 } 322 return sb.toString(); 323 } 324 325 /** 326 * Returns the descriptor of PowerStats produced by this collector. 327 */ 328 @VisibleForTesting getPowerStatsDescriptor()329 public PowerStats.Descriptor getPowerStatsDescriptor() { 330 if (!ensureInitialized()) { 331 return null; 332 } 333 334 return mPowerStatsDescriptor; 335 } 336 337 @Override collectStats()338 protected PowerStats collectStats() { 339 if (!ensureInitialized()) { 340 return null; 341 } 342 343 if (!mIsPerUidTimeInStateSupported) { 344 return null; 345 } 346 347 mCpuPowerStats.uidStats.clear(); 348 // TODO(b/305120724): additionally retrieve time-in-cluster for each CPU cluster 349 long newTimestampNanos = mKernelCpuStatsReader.readCpuStats(this::processUidStats, 350 mLayout.getScalingStepToPowerBracketMap(), mLastUpdateTimestampNanos, 351 mTempCpuTimeByScalingStep, mTempUidStats); 352 for (int step = mLayout.getCpuScalingStepCount() - 1; step >= 0; step--) { 353 mLayout.setTimeByScalingStep(mCpuPowerStats.stats, step, 354 mTempCpuTimeByScalingStep[step] - mCpuTimeByScalingStep[step]); 355 mCpuTimeByScalingStep[step] = mTempCpuTimeByScalingStep[step]; 356 } 357 358 mCpuPowerStats.durationMs = 359 (newTimestampNanos - mLastUpdateTimestampNanos) / NANOS_PER_MILLIS; 360 mLastUpdateTimestampNanos = newTimestampNanos; 361 362 long uptimeMillis = mClock.uptimeMillis(); 363 long uptimeDelta = uptimeMillis - mLastUpdateUptimeMillis; 364 mLastUpdateUptimeMillis = uptimeMillis; 365 366 if (uptimeDelta > mCpuPowerStats.durationMs) { 367 uptimeDelta = mCpuPowerStats.durationMs; 368 } 369 mLayout.setUsageDuration(mCpuPowerStats.stats, uptimeDelta); 370 371 if (mCpuEnergyConsumerIds.length != 0) { 372 collectEnergyConsumers(); 373 } 374 375 return mCpuPowerStats; 376 } 377 collectEnergyConsumers()378 private void collectEnergyConsumers() { 379 int voltageMv = mVoltageSupplier.getAsInt(); 380 if (voltageMv <= 0) { 381 Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv 382 + " mV) when querying energy consumers"); 383 return; 384 } 385 386 int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv; 387 mLastVoltageMv = voltageMv; 388 389 long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mCpuEnergyConsumerIds); 390 if (energyUws == null) { 391 return; 392 } 393 394 for (int i = energyUws.length - 1; i >= 0; i--) { 395 long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED 396 ? energyUws[i] - mLastConsumedEnergyUws[i] : 0; 397 if (energyDelta < 0) { 398 // Likely, restart of powerstats HAL 399 energyDelta = 0; 400 } 401 mLayout.setConsumedEnergy(mCpuPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage)); 402 mLastConsumedEnergyUws[i] = energyUws[i]; 403 } 404 } 405 406 @VisibleForNative 407 interface KernelCpuStatsCallback { 408 @Keep // Called from native processUidStats(int uid, long[] timeByPowerBracket)409 void processUidStats(int uid, long[] timeByPowerBracket); 410 } 411 processUidStats(int uid, long[] timeByPowerBracket)412 private void processUidStats(int uid, long[] timeByPowerBracket) { 413 int powerBracketCount = mLayout.getCpuPowerBracketCount(); 414 415 UidStats uidStats = mUidStats.get(uid); 416 if (uidStats == null) { 417 uidStats = new UidStats(); 418 uidStats.timeByPowerBracket = new long[powerBracketCount]; 419 uidStats.stats = new long[mLayout.getUidStatsArrayLength()]; 420 mUidStats.put(uid, uidStats); 421 } 422 423 boolean nonzero = false; 424 for (int bracket = powerBracketCount - 1; bracket >= 0; bracket--) { 425 long delta = Math.max(0, 426 timeByPowerBracket[bracket] - uidStats.timeByPowerBracket[bracket]); 427 if (delta != 0) { 428 nonzero = true; 429 } 430 mLayout.setUidTimeByPowerBracket(uidStats.stats, bracket, delta); 431 uidStats.timeByPowerBracket[bracket] = timeByPowerBracket[bracket]; 432 } 433 if (nonzero) { 434 int ownerUid; 435 if (Process.isSdkSandboxUid(uid)) { 436 ownerUid = Process.getAppUidForSdkSandboxUid(uid); 437 } else { 438 ownerUid = mUidResolver.mapUid(uid); 439 } 440 441 long[] ownerStats = mCpuPowerStats.uidStats.get(ownerUid); 442 if (ownerStats == null) { 443 mCpuPowerStats.uidStats.put(ownerUid, uidStats.stats); 444 } else { 445 for (int i = 0; i < ownerStats.length; i++) { 446 ownerStats[i] += uidStats.stats[i]; 447 } 448 } 449 } 450 } 451 452 @Override onUidRemoved(int uid)453 protected void onUidRemoved(int uid) { 454 super.onUidRemoved(uid); 455 mUidStats.remove(uid); 456 } 457 458 /** 459 * Native class that retrieves CPU stats from the kernel. 460 */ 461 public static class KernelCpuStatsReader { isSupportedFeature()462 protected boolean isSupportedFeature() { 463 return nativeIsSupportedFeature(); 464 } 465 readCpuStats(KernelCpuStatsCallback callback, int[] scalingStepToPowerBracketMap, long lastUpdateTimestampNanos, long[] outCpuTimeByScalingStep, long[] tempForUidStats)466 protected long readCpuStats(KernelCpuStatsCallback callback, 467 int[] scalingStepToPowerBracketMap, long lastUpdateTimestampNanos, 468 long[] outCpuTimeByScalingStep, long[] tempForUidStats) { 469 return nativeReadCpuStats(callback, scalingStepToPowerBracketMap, 470 lastUpdateTimestampNanos, outCpuTimeByScalingStep, tempForUidStats); 471 } 472 nativeIsSupportedFeature()473 protected native boolean nativeIsSupportedFeature(); 474 nativeReadCpuStats(KernelCpuStatsCallback callback, int[] scalingStepToPowerBracketMap, long lastUpdateTimestampNanos, long[] outCpuTimeByScalingStep, long[] tempForUidStats)475 protected native long nativeReadCpuStats(KernelCpuStatsCallback callback, 476 int[] scalingStepToPowerBracketMap, long lastUpdateTimestampNanos, 477 long[] outCpuTimeByScalingStep, long[] tempForUidStats); 478 } 479 480 private static class UidStats { 481 public long[] stats; 482 public long[] timeByPowerBracket; 483 } 484 } 485