diff options
author | Mat Bevilacqua <matbev@google.com> | 2021-01-19 17:48:50 -0800 |
---|---|---|
committer | Mat Bevilacqua <matbev@google.com> | 2021-02-03 19:48:58 -0800 |
commit | 3bf7191228a1c862e16bd7ffa8b3974b7cbad8ed (patch) | |
tree | c7115d05cb176a0705883b23506f3ceb0a67dd61 | |
parent | 6c3b1b0b76c3c8a4c46435a01eda6ba5ee7cac88 (diff) |
Log energy consumer result attribution data
Bug: 175723658
Test: atest FrameworksServicesTests:PowerStatsServiceTest
Change-Id: I4d0dccd440b02a35e086b08e31b1bf1f087f61e3
6 files changed, 155 insertions, 26 deletions
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto index 0c5a36049a29..4b1ee02a6c30 100644 --- a/core/proto/android/server/powerstatsservice.proto +++ b/core/proto/android/server/powerstatsservice.proto @@ -191,6 +191,14 @@ message EnergyConsumerProto { optional string name = 4; } +message EnergyConsumerAttributionProto { + /** Android ID / Linux UID, the accumulated energy should be attributed to. */ + optional int32 uid = 1; + + /** Accumulated energy since boot in microwatt-seconds (uWs) for this AID. */ + optional int64 energy_uws = 2; +} + /** * Energy consumer result: * An estimate of energy consumption since boot for the subsystem identified @@ -205,6 +213,9 @@ message EnergyConsumerResultProto { /** Accumulated energy since device boot in microwatt-seconds (uWs) */ optional int64 energy_uws = 3; + + /** Optional attribution per UID for this EnergyConsumer. */ + repeated EnergyConsumerAttributionProto attribution = 4; } /** diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java index 78a227ee170e..88090b32e64d 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java @@ -53,8 +53,9 @@ import java.io.IOException; public final class PowerStatsLogger extends Handler { private static final String TAG = PowerStatsLogger.class.getSimpleName(); private static final boolean DEBUG = false; - protected static final int MSG_LOG_TO_DATA_STORAGE_TIMER = 0; - protected static final int MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP = 1; + protected static final int MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP = 0; + protected static final int MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY = 1; + protected static final int MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY = 2; private final PowerStatsDataStorage mPowerStatsMeterStorage; private final PowerStatsDataStorage mPowerStatsModelStorage; @@ -64,8 +65,8 @@ public final class PowerStatsLogger extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_LOG_TO_DATA_STORAGE_TIMER: - if (DEBUG) Slog.d(TAG, "Logging to data storage on timer"); + case MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY: + if (DEBUG) Slog.d(TAG, "Logging to data storage on high frequency timer"); // Log power meter data. EnergyMeasurement[] energyMeasurements = @@ -74,12 +75,23 @@ public final class PowerStatsLogger extends Handler { EnergyMeasurementUtils.getProtoBytes(energyMeasurements)); if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements); - // Log power model data. - EnergyConsumerResult[] energyConsumerResults = + // Log power model data without attribution data. + EnergyConsumerResult[] ecrNoAttribution = mPowerStatsHALWrapper.getEnergyConsumed(new int[0]); mPowerStatsModelStorage.write( - EnergyConsumerResultUtils.getProtoBytes(energyConsumerResults)); - if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResults); + EnergyConsumerResultUtils.getProtoBytes(ecrNoAttribution, false)); + if (DEBUG) EnergyConsumerResultUtils.print(ecrNoAttribution); + break; + + case MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY: + if (DEBUG) Slog.d(TAG, "Logging to data storage on low frequency timer"); + + // Log power model data with attribution data. + EnergyConsumerResult[] ecrAttribution = + mPowerStatsHALWrapper.getEnergyConsumed(new int[0]); + mPowerStatsModelStorage.write( + EnergyConsumerResultUtils.getProtoBytes(ecrAttribution, true)); + if (DEBUG) EnergyConsumerResultUtils.print(ecrAttribution); break; case MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP: @@ -163,7 +175,7 @@ public final class PowerStatsLogger extends Handler { // deserialize, then re-serialize. This is computationally inefficient. EnergyConsumerResult[] energyConsumerResult = EnergyConsumerResultUtils.unpackProtoMessage(data); - EnergyConsumerResultUtils.packProtoMessage(energyConsumerResult, pos); + EnergyConsumerResultUtils.packProtoMessage(energyConsumerResult, pos, true); if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResult); } catch (IOException e) { Slog.e(TAG, "Failed to write energy model data to incident report."); diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java index 766cf9c1b678..bd003d3ac2dd 100644 --- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java +++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java @@ -18,6 +18,7 @@ package com.android.server.powerstats; import android.hardware.power.stats.Channel; import android.hardware.power.stats.EnergyConsumer; +import android.hardware.power.stats.EnergyConsumerAttribution; import android.hardware.power.stats.EnergyConsumerResult; import android.hardware.power.stats.EnergyMeasurement; import android.hardware.power.stats.PowerEntity; @@ -433,23 +434,40 @@ public class ProtoStreamUtils { } static class EnergyConsumerResultUtils { - public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult) { + public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult, + boolean includeAttribution) { ProtoOutputStream pos = new ProtoOutputStream(); - packProtoMessage(energyConsumerResult, pos); + packProtoMessage(energyConsumerResult, pos, includeAttribution); return pos.getBytes(); } public static void packProtoMessage(EnergyConsumerResult[] energyConsumerResult, - ProtoOutputStream pos) { + ProtoOutputStream pos, boolean includeAttribution) { if (energyConsumerResult == null) return; for (int i = 0; i < energyConsumerResult.length; i++) { - long token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT); + long ecrToken = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT); pos.write(EnergyConsumerResultProto.ID, energyConsumerResult[i].id); pos.write(EnergyConsumerResultProto.TIMESTAMP_MS, energyConsumerResult[i].timestampMs); pos.write(EnergyConsumerResultProto.ENERGY_UWS, energyConsumerResult[i].energyUWs); - pos.end(token); + + if (includeAttribution) { + final int attributionLength = energyConsumerResult[i].attribution.length; + + for (int j = 0; j < attributionLength; j++) { + final EnergyConsumerAttribution energyConsumerAttribution = + energyConsumerResult[i].attribution[j]; + final long ecaToken = pos.start(EnergyConsumerResultProto.ATTRIBUTION); + pos.write(EnergyConsumerAttributionProto.UID, + energyConsumerAttribution.uid); + pos.write(EnergyConsumerAttributionProto.ENERGY_UWS, + energyConsumerAttribution.energyUWs); + pos.end(ecaToken); + } + } + + pos.end(ecrToken); } } @@ -480,9 +498,45 @@ public class ProtoStreamUtils { } } + private static EnergyConsumerAttribution unpackEnergyConsumerAttributionProto( + ProtoInputStream pis) throws IOException { + final EnergyConsumerAttribution energyConsumerAttribution = + new EnergyConsumerAttribution(); + + while (true) { + try { + switch (pis.nextField()) { + case (int) EnergyConsumerAttributionProto.UID: + energyConsumerAttribution.uid = + pis.readInt(EnergyConsumerAttributionProto.UID); + break; + + case (int) EnergyConsumerAttributionProto.ENERGY_UWS: + energyConsumerAttribution.energyUWs = + pis.readLong(EnergyConsumerAttributionProto.ENERGY_UWS); + break; + + case ProtoInputStream.NO_MORE_FIELDS: + return energyConsumerAttribution; + + default: + Slog.e(TAG, "Unhandled field in EnergyConsumerAttributionProto: " + + ProtoUtils.currentFieldToString(pis)); + break; + + } + } catch (WireTypeMismatchException wtme) { + Slog.e(TAG, "Wire Type mismatch in EnergyConsumerAttributionProto: " + + ProtoUtils.currentFieldToString(pis)); + } + } + } + private static EnergyConsumerResult unpackEnergyConsumerResultProto(ProtoInputStream pis) throws IOException { EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult(); + final List<EnergyConsumerAttribution> energyConsumerAttributionList = + new ArrayList<EnergyConsumerAttribution>(); while (true) { try { @@ -501,7 +555,18 @@ public class ProtoStreamUtils { pis.readLong(EnergyConsumerResultProto.ENERGY_UWS); break; + case (int) EnergyConsumerResultProto.ATTRIBUTION: + final long token = pis.start(EnergyConsumerResultProto.ATTRIBUTION); + energyConsumerAttributionList.add( + unpackEnergyConsumerAttributionProto(pis)); + pis.end(token); + break; + case ProtoInputStream.NO_MORE_FIELDS: + energyConsumerResult.attribution = + energyConsumerAttributionList.toArray( + new EnergyConsumerAttribution[ + energyConsumerAttributionList.size()]); return energyConsumerResult; default: @@ -520,9 +585,16 @@ public class ProtoStreamUtils { if (energyConsumerResult == null) return; for (int i = 0; i < energyConsumerResult.length; i++) { - Slog.d(TAG, "EnergyConsumerId: " + energyConsumerResult[i].id - + ", Timestamp (ms): " + energyConsumerResult[i].timestampMs - + ", Energy (uWs): " + energyConsumerResult[i].energyUWs); + final EnergyConsumerResult result = energyConsumerResult[i]; + Slog.d(TAG, "EnergyConsumerId: " + result.id + + ", Timestamp (ms): " + result.timestampMs + + ", Energy (uWs): " + result.energyUWs); + final int attributionLength = result.attribution.length; + for (int j = 0; j < attributionLength; j++) { + final EnergyConsumerAttribution attribution = result.attribution[j]; + Slog.d(TAG, " UID: " + attribution.uid + + " Energy (uWs): " + attribution.energyUWs); + } } } } diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java index 7cba00f9a669..f8a4135b9d66 100644 --- a/services/core/java/com/android/server/powerstats/TimerTrigger.java +++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java @@ -29,18 +29,30 @@ public final class TimerTrigger extends PowerStatsLogTrigger { private static final String TAG = TimerTrigger.class.getSimpleName(); private static final boolean DEBUG = false; // TODO(b/166689029): Make configurable through global settings. - private static final long LOG_PERIOD_MS = 120 * 1000; + private static final long LOG_PERIOD_MS_LOW_FREQUENCY = 60 * 60 * 1000; // 1 hour + private static final long LOG_PERIOD_MS_HIGH_FREQUENCY = 2 * 60 * 1000; // 2 minutes private final Handler mHandler; - private Runnable mLogData = new Runnable() { + private Runnable mLogDataLowFrequency = new Runnable() { @Override public void run() { // Do not wake the device for these messages. Opportunistically log rail data every - // LOG_PERIOD_MS. - mHandler.postDelayed(mLogData, LOG_PERIOD_MS); - if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data"); - logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER); + // LOG_PERIOD_MS_LOW_FREQUENCY. + mHandler.postDelayed(mLogDataLowFrequency, LOG_PERIOD_MS_LOW_FREQUENCY); + if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data low frequency"); + logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY); + } + }; + + private Runnable mLogDataHighFrequency = new Runnable() { + @Override + public void run() { + // Do not wake the device for these messages. Opportunistically log rail data every + // LOG_PERIOD_MS_HIGH_FREQUENCY. + mHandler.postDelayed(mLogDataHighFrequency, LOG_PERIOD_MS_HIGH_FREQUENCY); + if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data high frequency"); + logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY); } }; @@ -50,7 +62,8 @@ public final class TimerTrigger extends PowerStatsLogTrigger { mHandler = mContext.getMainThreadHandler(); if (triggerEnabled) { - mLogData.run(); + mLogDataLowFrequency.run(); + mLogDataHighFrequency.run(); } } } diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index 84b690f01b02..49ee5656664e 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.fail; import android.content.Context; import android.hardware.power.stats.Channel; import android.hardware.power.stats.EnergyConsumer; +import android.hardware.power.stats.EnergyConsumerAttribution; import android.hardware.power.stats.EnergyConsumerResult; import android.hardware.power.stats.EnergyMeasurement; import android.hardware.power.stats.PowerEntity; @@ -73,6 +74,7 @@ public class PowerStatsServiceTest { private static final String ENERGY_CONSUMER_NAME = "energyconsumer"; private static final int ENERGY_METER_COUNT = 8; private static final int ENERGY_CONSUMER_COUNT = 2; + private static final int ENERGY_CONSUMER_ATTRIBUTION_COUNT = 5; private static final int POWER_ENTITY_COUNT = 3; private static final int STATE_INFO_COUNT = 5; private static final int STATE_RESIDENCY_COUNT = 4; @@ -204,6 +206,13 @@ public class PowerStatsServiceTest { energyConsumedList[i].id = i; energyConsumedList[i].timestampMs = i; energyConsumedList[i].energyUWs = i; + energyConsumedList[i].attribution = + new EnergyConsumerAttribution[ENERGY_CONSUMER_ATTRIBUTION_COUNT]; + for (int j = 0; j < energyConsumedList[i].attribution.length; j++) { + energyConsumedList[i].attribution[j] = new EnergyConsumerAttribution(); + energyConsumedList[i].attribution[j].uid = j; + energyConsumedList[i].attribution[j].energyUWs = j; + } } return energyConsumedList; } @@ -250,7 +259,7 @@ public class PowerStatsServiceTest { mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); // Write data to on-device storage. - mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER); + mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY); // The above call puts a message on a handler. Wait for // it to be processed. @@ -293,7 +302,7 @@ public class PowerStatsServiceTest { mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); // Write data to on-device storage. - mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER); + mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY); // The above call puts a message on a handler. Wait for // it to be processed. @@ -324,6 +333,12 @@ public class PowerStatsServiceTest { assertTrue(pssProto.energyConsumerResult[i].id == i); assertTrue(pssProto.energyConsumerResult[i].timestampMs == i); assertTrue(pssProto.energyConsumerResult[i].energyUws == i); + assertTrue(pssProto.energyConsumerResult[i].attribution.length + == ENERGY_CONSUMER_ATTRIBUTION_COUNT); + for (int j = 0; j < pssProto.energyConsumerResult[i].attribution.length; j++) { + assertTrue(pssProto.energyConsumerResult[i].attribution[j].uid == j); + assertTrue(pssProto.energyConsumerResult[i].attribution[j].energyUws == j); + } } } diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java index 21409542714d..04f9bf26b43c 100644 --- a/tools/powerstats/PowerStatsServiceProtoParser.java +++ b/tools/powerstats/PowerStatsServiceProtoParser.java @@ -86,6 +86,12 @@ public class PowerStatsServiceProtoParser { csvRow += energyConsumerResult.getId() + "," + energyConsumerResult.getTimestampMs() + "," + energyConsumerResult.getEnergyUws() + ","; + for (int k = 0; k < energyConsumerResult.getAttributionCount(); k++) { + final EnergyConsumerAttributionProto energyConsumerAttribution = + energyConsumerResult.getAttribution(k); + csvRow += energyConsumerAttribution.getUid() + "," + + energyConsumerAttribution.getEnergyUws() + ","; + } } System.out.println(csvRow); } |