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