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

Commit fa4bda70 authored by Sudheer Shanka's avatar Sudheer Shanka Committed by Android (Google) Code Review
Browse files

Merge "Update BatteryStatsImpl to track per-procstate cpu times."

parents 0be794a1 b2f83c16
Loading
Loading
Loading
Loading
+106 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.os;

import android.app.ActivityManager;
import android.app.job.JobParameters;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -33,6 +34,7 @@ import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.Display;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;

@@ -327,7 +329,8 @@ public abstract class BatteryStats implements Parcelable {
     *
     * Other types might include times spent in foreground, background etc.
     */
    private final String UID_TIMES_TYPE_ALL = "A";
    @VisibleForTesting
    public static final String UID_TIMES_TYPE_ALL = "A";

    /**
     * State for keeping track of counting information.
@@ -506,6 +509,31 @@ public abstract class BatteryStats implements Parcelable {
        public abstract void logState(Printer pw, String prefix);
    }

    /**
     * Maps the ActivityManager procstate into corresponding BatteryStats procstate.
     */
    public static int mapToInternalProcessState(int procState) {
        if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
            return ActivityManager.PROCESS_STATE_NONEXISTENT;
        } else if (procState == ActivityManager.PROCESS_STATE_TOP) {
            return Uid.PROCESS_STATE_TOP;
        } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
            // Persistent and other foreground states go here.
            return Uid.PROCESS_STATE_FOREGROUND_SERVICE;
        } else if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
            // Persistent and other foreground states go here.
            return Uid.PROCESS_STATE_FOREGROUND;
        } else if (procState <= ActivityManager.PROCESS_STATE_RECEIVER) {
            return Uid.PROCESS_STATE_BACKGROUND;
        } else if (procState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
            return Uid.PROCESS_STATE_TOP_SLEEPING;
        } else if (procState <= ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
            return Uid.PROCESS_STATE_HEAVY_WEIGHT;
        } else {
            return Uid.PROCESS_STATE_CACHED;
        }
    }

    /**
     * The statistics associated with a particular uid.
     */
@@ -645,6 +673,15 @@ public abstract class BatteryStats implements Parcelable {
        public abstract long[] getCpuFreqTimes(int which);
        public abstract long[] getScreenOffCpuFreqTimes(int which);

        /**
         * Returns cpu times of an uid at a particular process state.
         */
        public abstract long[] getCpuFreqTimes(int which, int procState);
        /**
         * Returns cpu times of an uid while the screen if off at a particular process state.
         */
        public abstract long[] getScreenOffCpuFreqTimes(int which, int procState);

        // Note: the following times are disjoint.  They can be added together to find the
        // total time a uid has had any processes running at all.

@@ -689,11 +726,32 @@ public abstract class BatteryStats implements Parcelable {
         */
        public static final int NUM_PROCESS_STATE = 7;

        // Used in dump
        static final String[] PROCESS_STATE_NAMES = {
                "Top", "Fg Service", "Foreground", "Background", "Top Sleeping", "Heavy Weight",
                "Cached"
        };

        // Used in checkin dump
        @VisibleForTesting
        public static final String[] UID_PROCESS_TYPES = {
                "T",  // TOP
                "FS", // FOREGROUND_SERVICE
                "F",  // FOREGROUND
                "B",  // BACKGROUND
                "TS", // TOP_SLEEPING
                "HW",  // HEAVY_WEIGHT
                "C"   // CACHED
        };

        /**
         * When the process exits one of these states, we need to make sure cpu time in this state
         * is not attributed to any non-critical process states.
         */
        public static final int[] CRITICAL_PROC_STATES = {
            PROCESS_STATE_TOP, PROCESS_STATE_FOREGROUND_SERVICE, PROCESS_STATE_FOREGROUND
        };

        public abstract long getProcessStateTime(int state, long elapsedRealtimeUs, int which);
        public abstract Timer getProcessStateTimer(int state);

@@ -4002,6 +4060,29 @@ public abstract class BatteryStats implements Parcelable {
                    dumpLine(pw, uid, category, CPU_TIMES_AT_FREQ_DATA, UID_TIMES_TYPE_ALL,
                            cpuFreqTimeMs.length, sb.toString());
                }

                for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
                    final long[] timesMs = u.getCpuFreqTimes(which, procState);
                    if (timesMs != null && timesMs.length == cpuFreqs.length) {
                        sb.setLength(0);
                        for (int i = 0; i < timesMs.length; ++i) {
                            sb.append((i == 0 ? "" : ",") + timesMs[i]);
                        }
                        final long[] screenOffTimesMs = u.getScreenOffCpuFreqTimes(
                                which, procState);
                        if (screenOffTimesMs != null) {
                            for (int i = 0; i < screenOffTimesMs.length; ++i) {
                                sb.append("," + screenOffTimesMs[i]);
                            }
                        } else {
                            for (int i = 0; i < timesMs.length; ++i) {
                                sb.append(",0");
                            }
                        }
                        dumpLine(pw, uid, category, CPU_TIMES_AT_FREQ_DATA,
                                Uid.UID_PROCESS_TYPES[procState], timesMs.length, sb.toString());
                    }
                }
            }

            final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats
@@ -5612,6 +5693,30 @@ public abstract class BatteryStats implements Parcelable {
                pw.println(sb.toString());
            }

            for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
                final long[] cpuTimes = u.getCpuFreqTimes(which, procState);
                if (cpuTimes != null) {
                    sb.setLength(0);
                    sb.append("    Cpu times per freq at state "
                            + Uid.PROCESS_STATE_NAMES[procState] + ":");
                    for (int i = 0; i < cpuTimes.length; ++i) {
                        sb.append(" " + cpuTimes[i]);
                    }
                    pw.println(sb.toString());
                }

                final long[] screenOffCpuTimes = u.getScreenOffCpuFreqTimes(which, procState);
                if (screenOffCpuTimes != null) {
                    sb.setLength(0);
                    sb.append("   Screen-off cpu times per freq at state "
                            + Uid.PROCESS_STATE_NAMES[procState] + ":");
                    for (int i = 0; i < screenOffCpuTimes.length; ++i) {
                        sb.append(" " + screenOffCpuTimes[i]);
                    }
                    pw.println(sb.toString());
                }
            }

            final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats
                    = u.getProcessStats();
            for (int ipr=processStats.size()-1; ipr>=0; ipr--) {
+387 −43

File changed.

Preview size limit exceeded, changes collapsed.

+204 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.internal.os;

import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;

import android.annotation.NonNull;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;

@VisibleForTesting(visibility = PACKAGE)
public class KernelSingleUidTimeReader {
    private final String TAG = KernelUidCpuFreqTimeReader.class.getName();
    private final boolean DBG = false;

    private final String PROC_FILE_DIR = "/proc/uid/";
    private final String PROC_FILE_NAME = "/time_in_state";

    @VisibleForTesting
    public static final int TOTAL_READ_ERROR_COUNT = 5;

    @GuardedBy("this")
    private final int mCpuFreqsCount;

    @GuardedBy("this")
    private final SparseArray<long[]> mLastUidCpuTimeMs = new SparseArray<>();

    @GuardedBy("this")
    private int mReadErrorCounter;
    @GuardedBy("this")
    private boolean mSingleUidCpuTimesAvailable = true;

    private final Injector mInjector;

    KernelSingleUidTimeReader(int cpuFreqsCount) {
        this(cpuFreqsCount, new Injector());
    }

    public KernelSingleUidTimeReader(int cpuFreqsCount, Injector injector) {
        mInjector = injector;
        mCpuFreqsCount = cpuFreqsCount;
        if (mCpuFreqsCount == 0) {
            mSingleUidCpuTimesAvailable = false;
        }
    }

    public boolean singleUidCpuTimesAvailable() {
        return mSingleUidCpuTimesAvailable;
    }

    public long[] readDeltaMs(int uid) {
        synchronized (this) {
            if (!mSingleUidCpuTimesAvailable) {
                return null;
            }
            // Read total cpu times from the proc file.
            final String procFile = new StringBuilder(PROC_FILE_DIR)
                    .append(uid)
                    .append(PROC_FILE_NAME).toString();
            final long[] cpuTimesMs = new long[mCpuFreqsCount];
            try {
                final byte[] data = mInjector.readData(procFile);
                final ByteBuffer buffer = ByteBuffer.wrap(data);
                buffer.order(ByteOrder.nativeOrder());
                for (int i = 0; i < mCpuFreqsCount; ++i) {
                    // Times read will be in units of 10ms
                    cpuTimesMs[i] = buffer.getLong() * 10;
                }
            } catch (Exception e) {
                if (++mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) {
                    mSingleUidCpuTimesAvailable = false;
                }
                if (DBG) Slog.e(TAG, "Some error occured while reading " + procFile, e);
                return null;
            }

            return computeDelta(uid, cpuTimesMs);
        }
    }

    /**
     * Compute and return cpu times delta of an uid using previously read cpu times and
     * {@param latestCpuTimesMs}.
     *
     * @return delta of cpu times if at least one of the cpu time at a freq is +ve, otherwise null.
     */
    public long[] computeDelta(int uid, @NonNull long[] latestCpuTimesMs) {
        synchronized (this) {
            if (!mSingleUidCpuTimesAvailable) {
                return null;
            }
            // Subtract the last read cpu times to get deltas.
            final long[] lastCpuTimesMs = mLastUidCpuTimeMs.get(uid);
            final long[] deltaTimesMs = getDeltaLocked(lastCpuTimesMs, latestCpuTimesMs);
            if (deltaTimesMs == null) {
                if (DBG) Slog.e(TAG, "Malformed data read for uid=" + uid
                        + "; last=" + Arrays.toString(lastCpuTimesMs)
                        + "; latest=" + Arrays.toString(latestCpuTimesMs));
                return null;
            }
            // If all elements are zero, return null to avoid unnecessary work on the caller side.
            boolean hasNonZero = false;
            for (int i = deltaTimesMs.length - 1; i >= 0; --i) {
                if (deltaTimesMs[i] > 0) {
                    hasNonZero = true;
                    break;
                }
            }
            if (hasNonZero) {
                mLastUidCpuTimeMs.put(uid, latestCpuTimesMs);
                return deltaTimesMs;
            } else {
                return null;
            }
        }
    }

    /**
     * Returns null if the latest cpu times are not valid**, otherwise delta of
     * {@param latestCpuTimesMs} and {@param lastCpuTimesMs}.
     *
     * **latest cpu times are considered valid if all the cpu times are +ve and
     * greater than or equal to previously read cpu times.
     */
    @GuardedBy("this")
    @VisibleForTesting(visibility = PACKAGE)
    public long[] getDeltaLocked(long[] lastCpuTimesMs, @NonNull long[] latestCpuTimesMs) {
        for (int i = latestCpuTimesMs.length - 1; i >= 0; --i) {
            if (latestCpuTimesMs[i] < 0) {
                return null;
            }
        }
        if (lastCpuTimesMs == null) {
            return latestCpuTimesMs;
        }
        final long[] deltaTimesMs = new long[latestCpuTimesMs.length];
        for (int i = latestCpuTimesMs.length - 1; i >= 0; --i) {
            deltaTimesMs[i] = latestCpuTimesMs[i] - lastCpuTimesMs[i];
            if (deltaTimesMs[i] < 0) {
                return null;
            }
        }
        return deltaTimesMs;
    }

    public void removeUid(int uid) {
        synchronized (this) {
            mLastUidCpuTimeMs.delete(uid);
        }
    }

    public void removeUidsInRange(int startUid, int endUid) {
        if (endUid < startUid) {
            return;
        }
        synchronized (this) {
            mLastUidCpuTimeMs.put(startUid, null);
            mLastUidCpuTimeMs.put(endUid, null);
            final int startIdx = mLastUidCpuTimeMs.indexOfKey(startUid);
            final int endIdx = mLastUidCpuTimeMs.indexOfKey(endUid);
            mLastUidCpuTimeMs.removeAtRange(startIdx, endIdx - startIdx + 1);
        }
    }

    @VisibleForTesting
    public static class Injector {
        public byte[] readData(String procFile) throws IOException {
            return Files.readAllBytes(Paths.get(procFile));
        }
    }

    @VisibleForTesting
    public SparseArray<long[]> getLastUidCpuTimeMs() {
        return mLastUidCpuTimeMs;
    }

    @VisibleForTesting
    public void setSingleUidCpuTimesAvailable(boolean singleUidCpuTimesAvailable) {
        mSingleUidCpuTimesAvailable = singleUidCpuTimesAvailable;
    }
}
 No newline at end of file
+14 −5
Original line number Diff line number Diff line
@@ -66,13 +66,21 @@ public class KernelUidCpuFreqTimeReader {
    // start reading) and if it is not available, we simply ignore further read requests.
    private static final int TOTAL_READ_ERROR_COUNT = 5;
    private int mReadErrorCounter;
    private boolean mProcFileAvailable;
    private boolean mPerClusterTimesAvailable;
    private boolean mAllUidTimesAvailable = true;

    public boolean perClusterTimesAvailable() {
        return mPerClusterTimesAvailable;
    }

    public boolean allUidTimesAvailable() {
        return mAllUidTimesAvailable;
    }

    public SparseArray<long[]> getAllUidCpuFreqTimeMs() {
        return mLastUidCpuFreqTimeMs;
    }

    public long[] readFreqs(@NonNull PowerProfile powerProfile) {
        checkNotNull(powerProfile);

@@ -80,15 +88,16 @@ public class KernelUidCpuFreqTimeReader {
            // No need to read cpu freqs more than once.
            return mCpuFreqs;
        }
        if (!mProcFileAvailable && mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) {
        if (!mAllUidTimesAvailable) {
            return null;
        }
        final int oldMask = StrictMode.allowThreadDiskReadsMask();
        try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
            mProcFileAvailable = true;
            return readFreqs(reader, powerProfile);
        } catch (IOException e) {
            mReadErrorCounter++;
            if (++mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) {
                mAllUidTimesAvailable = false;
            }
            Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
            return null;
        } finally {
@@ -107,7 +116,7 @@ public class KernelUidCpuFreqTimeReader {
    }

    public void readDelta(@Nullable Callback callback) {
        if (!mProcFileAvailable) {
        if (mCpuFreqs == null) {
            return;
        }
        final int oldMask = StrictMode.allowThreadDiskReadsMask();
+6 −0
Original line number Diff line number Diff line
@@ -17,9 +17,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.coretests.apps.bstatstestapp">

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25"/>

    <application>
        <activity android:name=".TestActivity"
                  android:exported="true" />
        <service android:name=".TestService"
                  android:exported="true" />
        <service android:name=".IsolatedTestService"
                 android:exported="true"
                 android:isolatedProcess="true" />
Loading