Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 723784a0 authored by Mat Bevilacqua's avatar Mat Bevilacqua
Browse files

Add state residency logging to power stats

Test: atest FrameworksServicesTests:PowerStatsServiceTest
Bug: 175724197

Change-Id: I4ba7af3b9a895ecdc9f8c16cd371e5582458d212
parent ff001a15
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -521,6 +521,11 @@ message IncidentProto {
        (section).args = "power_stats --proto model"
    ];

    optional com.android.server.powerstats.PowerStatsServiceResidencyProto powerstats_residency = 3056 [
        (section).type = SECTION_DUMPSYS,
        (section).args = "power_stats --proto residency"
    ];

    // Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
    optional android.util.TextDumpProto textdump_wifi = 4000 [
        (section).type = SECTION_TEXT_DUMPSYS,
+103 −0
Original line number Diff line number Diff line
@@ -40,6 +40,16 @@ message IncidentReportModelProto {
    optional PowerStatsServiceModelProto incident_report = 3055;
}

/**
 * IncidentReportResidencyProto is used only in the parsing tool located
 * in frameworks/base/tools which is used to parse this data out of
 * incident reports.
 */
message IncidentReportResidencyProto {
    /** Section number matches that in incident.proto */
    optional PowerStatsServiceResidencyProto incident_report = 3056;
}

/**
 * EnergyConsumer (model) data is exposed by the PowerStats HAL.  This data
 * represents modeled energy consumption estimates and is provided per
@@ -62,6 +72,99 @@ message PowerStatsServiceMeterProto {
    repeated EnergyMeasurementProto energy_measurement = 2;
}

/**
 * A PowerEntity is defined as a platform subsystem, peripheral, or power domain
 * that impacts the total device power consumption.  PowerEntityInfo is
 * information related to each power entity.  Each PowerEntity may reside in one
 * of multiple states. It may also transition from one state to another.
 * StateResidency is defined as an accumulation of time that a PowerEntity
 * resided in each of its possible states, the number of times that each state
 * was entered, and a timestamp corresponding to the last time that state was
 * entered.
 */
message PowerStatsServiceResidencyProto {
    repeated PowerEntityInfoProto power_entity_info = 1;
    repeated StateResidencyResultProto state_residency_result = 2;
}

/**
 * Information about the possible states for a particular PowerEntity.
 */
message StateInfoProto {
    /**
     * Unique (for a given PowerEntityInfo) ID of this StateInfo
     */
    optional int32 state_id = 1;
    /**
     * Unique (for a given PowerEntityInfo) name of the state. Vendor/device specific.
     * Opaque to framework
     */
    optional string state_name = 2;
}

/**
 * A PowerEntity is defined as a platform subsystem, peripheral, or power domain
 * that impacts the total device power consumption.  PowerEntityInfo is
 * information about a PowerEntity.  It includes an array of information about
 * each possible state of the PowerEntity.
 */
message PowerEntityInfoProto {
    /**
     * Unique ID of this PowerEntityInfo
     */
    optional int32 power_entity_id = 1;
    /**
     * Unique name of the PowerEntity. Vendor/device specific. Opaque to framework
     */
    optional string power_entity_name = 2;
    /**
     * List of states that the PowerEntity may reside in
     */
    repeated StateInfoProto states = 3;
}

/**
 * StateResidency is defined as an accumulation of time that a PowerEntity
 * resided in each of its possible states, the number of times that each state
 * was entered, and a timestamp corresponding to the last time that state was
 * entered.  Data is accumulated starting at device boot.
 */
message StateResidencyProto {
    /**
     * ID of the state associated with this residency
     */
    optional int32 state_id = 1;
    /**
     * Total time in milliseconds that the corresponding PowerEntity resided
     * in this state since boot
     */
    optional int64 total_time_in_state_ms = 2;
    /**
     * Total number of times that the state was entered since boot
     */
    optional int64 total_state_entry_count = 3;
    /**
     * Last time this state was entered. Time in milliseconds since boot
     */
    optional int64 last_entry_timestamp_ms = 4;
}

/**
 * A StateResidencyResult is an array of StateResidencies for a particular
 * PowerEntity.  The StateResidencyResult can be matched to its corresponding
 * PowerEntityInfo through the power_entity_id field.
 */
message StateResidencyResultProto {
    /**
     * ID of the PowerEntity associated with this result
     */
    optional int32 power_entity_id = 1;
    /**
     * Residency for each state in the PowerEntity's state space
     */
    repeated StateResidencyProto state_residency_data = 2;
}

/**
 * Energy consumer ID:
 * A list of default subsystems for which energy consumption estimates
+1 −1
Original line number Diff line number Diff line
@@ -218,7 +218,7 @@ public class PowerStatsDataStorage {
     *             array and written to on-device storage.
     */
    public void write(byte[] data) {
        if (data.length > 0) {
        if (data != null && data.length > 0) {
            mLock.lock();

            long currentTimeMillis = System.currentTimeMillis();
+59 −3
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.content.Context;
import android.hardware.power.stats.ChannelInfo;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyMeasurement;
import android.hardware.power.stats.PowerEntityInfo;
import android.hardware.power.stats.StateResidencyResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -32,6 +34,8 @@ import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils;
import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils;
import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
import com.android.server.powerstats.ProtoStreamUtils.EnergyMeasurementUtils;
import com.android.server.powerstats.ProtoStreamUtils.PowerEntityInfoUtils;
import com.android.server.powerstats.ProtoStreamUtils.StateResidencyResultUtils;

import java.io.ByteArrayInputStream;
import java.io.File;
@@ -42,8 +46,8 @@ import java.io.IOException;
 * PowerStatsLogger is responsible for logging model and meter energy data to on-device storage.
 * Messages are sent to its message handler to request that energy data be logged, at which time it
 * queries the PowerStats HAL and logs the data to on-device storage.  The on-device storage is
 * dumped to file by calling writeModelDataToFile or writeMeterDataToFile with a file descriptor
 * that points to the output file.
 * dumped to file by calling writeModelDataToFile, writeMeterDataToFile, or writeResidencyDataToFile
 * with a file descriptor that points to the output file.
 */
public final class PowerStatsLogger extends Handler {
    private static final String TAG = PowerStatsLogger.class.getSimpleName();
@@ -52,6 +56,7 @@ public final class PowerStatsLogger extends Handler {

    private final PowerStatsDataStorage mPowerStatsMeterStorage;
    private final PowerStatsDataStorage mPowerStatsModelStorage;
    private final PowerStatsDataStorage mPowerStatsResidencyStorage;
    private final IPowerStatsHALWrapper mPowerStatsHALWrapper;

    @Override
@@ -73,6 +78,13 @@ public final class PowerStatsLogger extends Handler {
                mPowerStatsModelStorage.write(
                        EnergyConsumerResultUtils.getProtoBytes(energyConsumerResults));
                if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResults);

                // Log state residency data.
                StateResidencyResult[] stateResidencyResults =
                    mPowerStatsHALWrapper.getStateResidency(new int[0]);
                mPowerStatsResidencyStorage.write(
                        StateResidencyResultUtils.getProtoBytes(stateResidencyResults));
                if (DEBUG) StateResidencyResultUtils.print(stateResidencyResults);
                break;
        }
    }
@@ -159,13 +171,57 @@ public final class PowerStatsLogger extends Handler {
        pos.flush();
    }

    /**
     * Writes residency data stored in PowerStatsDataStorage to a file descriptor.
     *
     * @param fd FileDescriptor where residency data stored in PowerStatsDataStorage is written.
     *           Data is written in protobuf format as defined by powerstatsservice.proto.
     */
    public void writeResidencyDataToFile(FileDescriptor fd) {
        if (DEBUG) Slog.d(TAG, "Writing residency data to file");

        final ProtoOutputStream pos = new ProtoOutputStream(fd);

        try {
            PowerEntityInfo[] powerEntityInfo = mPowerStatsHALWrapper.getPowerEntityInfo();
            PowerEntityInfoUtils.packProtoMessage(powerEntityInfo, pos);
            if (DEBUG) PowerEntityInfoUtils.print(powerEntityInfo);

            mPowerStatsResidencyStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
                @Override
                public void onReadDataElement(byte[] data) {
                    try {
                        final ProtoInputStream pis =
                                new ProtoInputStream(new ByteArrayInputStream(data));
                        // TODO(b/166535853): ProtoOutputStream doesn't provide a method to write
                        // a byte array that already contains a serialized proto, so I have to
                        // deserialize, then re-serialize.  This is computationally inefficient.
                        StateResidencyResult[] stateResidencyResult =
                            StateResidencyResultUtils.unpackProtoMessage(data);
                        StateResidencyResultUtils.packProtoMessage(stateResidencyResult, pos);
                        if (DEBUG) StateResidencyResultUtils.print(stateResidencyResult);
                    } catch (IOException e) {
                        Slog.e(TAG, "Failed to write residency data to incident report.");
                    }
                }
            });
        } catch (IOException e) {
            Slog.e(TAG, "Failed to write residency data to incident report.");
        }

        pos.flush();
    }

    public PowerStatsLogger(Context context, File dataStoragePath, String meterFilename,
            String modelFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
            String modelFilename, String residencyFilename,
            IPowerStatsHALWrapper powerStatsHALWrapper) {
        super(Looper.getMainLooper());
        mPowerStatsHALWrapper = powerStatsHALWrapper;
        mPowerStatsMeterStorage = new PowerStatsDataStorage(context, dataStoragePath,
            meterFilename);
        mPowerStatsModelStorage = new PowerStatsDataStorage(context, dataStoragePath,
            modelFilename);
        mPowerStatsResidencyStorage = new PowerStatsDataStorage(context, dataStoragePath,
            residencyFilename);
    }
}
+12 −3
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ public class PowerStatsService extends SystemService {
    private static final int DATA_STORAGE_VERSION = 0;
    private static final String METER_FILENAME = "log.powerstats.meter." + DATA_STORAGE_VERSION;
    private static final String MODEL_FILENAME = "log.powerstats.model." + DATA_STORAGE_VERSION;
    private static final String RESIDENCY_FILENAME =
            "log.powerstats.residency." + DATA_STORAGE_VERSION;

    private final Injector mInjector;

@@ -76,15 +78,19 @@ public class PowerStatsService extends SystemService {
            return MODEL_FILENAME;
        }

        String createResidencyFilename() {
            return RESIDENCY_FILENAME;
        }

        IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
            return PowerStatsHALWrapper.getPowerStatsHalImpl();
        }

        PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
                String meterFilename, String modelFilename,
                String meterFilename, String modelFilename, String residencyFilename,
                IPowerStatsHALWrapper powerStatsHALWrapper) {
            return new PowerStatsLogger(context, dataStoragePath, meterFilename,
                modelFilename, powerStatsHALWrapper);
                modelFilename, residencyFilename, powerStatsHALWrapper);
        }

        BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
@@ -109,6 +115,8 @@ public class PowerStatsService extends SystemService {
                        mPowerStatsLogger.writeModelDataToFile(fd);
                    } else if ("meter".equals(args[1])) {
                        mPowerStatsLogger.writeMeterDataToFile(fd);
                    } else if ("residency".equals(args[1])) {
                        mPowerStatsLogger.writeResidencyDataToFile(fd);
                    }
                } else if (args.length == 0) {
                    pw.println("PowerStatsService dumpsys: available PowerEntityInfos");
@@ -148,7 +156,8 @@ public class PowerStatsService extends SystemService {
            // Only start logger and triggers if initialization is successful.
            mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext,
                mInjector.createDataStoragePath(), mInjector.createMeterFilename(),
                mInjector.createModelFilename(), mPowerStatsHALWrapper);
                mInjector.createModelFilename(), mInjector.createResidencyFilename(),
                mPowerStatsHALWrapper);
            mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
            mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
        } else {
Loading