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

Commit 94326cb5 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Add reporting of how jobs complete.

In both battery stats and job scheduler statistics, keep track of
the reason jobs are ending.  This can help to identify apps that
are having bad behavior with jobs (in particular, regularly timing
out their job instead of finishing it when done).

Test: manual
Change-Id: Icc12d5e0dc0df1906716e7d995c56d50a9fa148a
parent 8ad1d780
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -45,6 +45,18 @@ public class JobParameters implements Parcelable {
    /** @hide */
    public static final int REASON_DEVICE_IDLE = 4;

    /** @hide */
    public static String getReasonName(int reason) {
        switch (reason) {
            case REASON_CANCELED: return "canceled";
            case REASON_CONSTRAINTS_NOT_SATISFIED: return "constraints";
            case REASON_PREEMPT: return "preempt";
            case REASON_TIMEOUT: return "timeout";
            case REASON_DEVICE_IDLE: return "device_idle";
            default: return "unknown:" + reason;
        }
    }

    private final int jobId;
    private final PersistableBundle extras;
    private final Bundle transientExtras;
+42 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.job.JobParameters;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.telephony.SignalStrength;
@@ -238,6 +239,7 @@ public abstract class BatteryStats implements Parcelable {
    private static final String AGGREGATED_WAKELOCK_DATA = "awl";
    private static final String SYNC_DATA = "sy";
    private static final String JOB_DATA = "jb";
    private static final String JOB_COMPLETION_DATA = "jbc";
    private static final String KERNEL_WAKELOCK_DATA = "kwl";
    private static final String WAKEUP_REASON_DATA = "wr";
    private static final String NETWORK_DATA = "nt";
@@ -497,6 +499,13 @@ public abstract class BatteryStats implements Parcelable {
         */
        public abstract ArrayMap<String, ? extends Timer> getJobStats();

        /**
         * Returns statistics about how jobs have completed.
         *
         * @return A Map of String job names to completion type -> count mapping.
         */
        public abstract ArrayMap<String, SparseIntArray> getJobCompletionStats();

        /**
         * The statistics associated with a particular wake lock.
         */
@@ -3557,6 +3566,20 @@ public abstract class BatteryStats implements Parcelable {
                }
            }

            final ArrayMap<String, SparseIntArray> completions = u.getJobCompletionStats();
            for (int ic=completions.size()-1; ic>=0; ic--) {
                SparseIntArray types = completions.valueAt(ic);
                if (types != null) {
                    dumpLine(pw, uid, category, JOB_COMPLETION_DATA,
                            "\"" + completions.keyAt(ic) + "\"",
                            types.get(JobParameters.REASON_CANCELED, 0),
                            types.get(JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, 0),
                            types.get(JobParameters.REASON_PREEMPT, 0),
                            types.get(JobParameters.REASON_TIMEOUT, 0),
                            types.get(JobParameters.REASON_DEVICE_IDLE, 0));
                }
            }

            dumpTimer(pw, uid, category, FLASHLIGHT_DATA, u.getFlashlightTurnedOnTimer(),
                    rawRealtime, which);
            dumpTimer(pw, uid, category, CAMERA_DATA, u.getCameraTurnedOnTimer(),
@@ -4979,6 +5002,25 @@ public abstract class BatteryStats implements Parcelable {
                uidActivity = true;
            }

            final ArrayMap<String, SparseIntArray> completions = u.getJobCompletionStats();
            for (int ic=completions.size()-1; ic>=0; ic--) {
                SparseIntArray types = completions.valueAt(ic);
                if (types != null) {
                    pw.print(prefix);
                    pw.print("    Job Completions ");
                    pw.print(completions.keyAt(ic));
                    pw.print(":");
                    for (int it=0; it<types.size(); it++) {
                        pw.print(" ");
                        pw.print(JobParameters.getReasonName(types.keyAt(it)));
                        pw.print("(");
                        pw.print(types.valueAt(it));
                        pw.print("x)");
                    }
                    pw.println();
                }
            }

            uidActivity |= printTimer(pw, sb, u.getFlashlightTurnedOnTimer(), rawRealtime, which,
                    prefix, "Flashlight");
            uidActivity |= printTimer(pw, sb, u.getCameraTurnedOnTimer(), rawRealtime, which,
+1 −1
Original line number Diff line number Diff line
@@ -66,7 +66,7 @@ interface IBatteryStats {
    void noteSyncStart(String name, int uid);
    void noteSyncFinish(String name, int uid);
    void noteJobStart(String name, int uid);
    void noteJobFinish(String name, int uid);
    void noteJobFinish(String name, int uid, int stopReason);

    void noteStartWakelock(int uid, int pid, String name, String historyName,
            int type, boolean unimportantForLogging);
+65 −4
Original line number Diff line number Diff line
@@ -119,7 +119,7 @@ public class BatteryStatsImpl extends BatteryStats {
    private static final int MAGIC = 0xBA757475; // 'BATSTATS'

    // Current on-disk Parcel version
    private static final int VERSION = 159 + (USE_OLD_HISTORY ? 1000 : 0);
    private static final int VERSION = 160 + (USE_OLD_HISTORY ? 1000 : 0);

    // Maximum number of items we will record in the history.
    private static final int MAX_HISTORY_ITEMS;
@@ -3664,11 +3664,11 @@ public class BatteryStatsImpl extends BatteryStats {
        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_JOB_START, name, uid);
    }

    public void noteJobFinishLocked(String name, int uid) {
    public void noteJobFinishLocked(String name, int uid, int stopReason) {
        uid = mapUid(uid);
        final long elapsedRealtime = mClocks.elapsedRealtime();
        final long uptime = mClocks.uptimeMillis();
        getUidStatsLocked(uid).noteStopJobLocked(name, elapsedRealtime);
        getUidStatsLocked(uid).noteStopJobLocked(name, elapsedRealtime, stopReason);
        if (!mActiveEvents.updateState(HistoryItem.EVENT_JOB_FINISH, name, uid, 0)) {
            return;
        }
@@ -5701,6 +5701,11 @@ public class BatteryStatsImpl extends BatteryStats {
         */
        final OverflowArrayMap<DualTimer> mJobStats;

        /**
         * Count of the jobs that have completed and the reasons why they completed.
         */
        final ArrayMap<String, SparseIntArray> mJobCompletions = new ArrayMap<>();

        /**
         * The statistics we have collected for this uid's sensor activations.
         */
@@ -5822,6 +5827,11 @@ public class BatteryStatsImpl extends BatteryStats {
            return mJobStats.getMap();
        }

        @Override
        public ArrayMap<String, SparseIntArray> getJobCompletionStats() {
            return mJobCompletions;
        }

        @Override
        public SparseArray<? extends BatteryStats.Uid.Sensor> getSensorStats() {
            return mSensorStats;
@@ -6706,6 +6716,7 @@ public class BatteryStatsImpl extends BatteryStats {
                }
            }
            mJobStats.cleanup();
            mJobCompletions.clear();
            for (int ise=mSensorStats.size()-1; ise>=0; ise--) {
                Sensor s = mSensorStats.valueAt(ise);
                if (s.reset()) {
@@ -6868,6 +6879,21 @@ public class BatteryStatsImpl extends BatteryStats {
            return !active;
        }

        void writeJobCompletionsToParcelLocked(Parcel out) {
            int NJC = mJobCompletions.size();
            out.writeInt(NJC);
            for (int ijc=0; ijc<NJC; ijc++) {
                out.writeString(mJobCompletions.keyAt(ijc));
                SparseIntArray types = mJobCompletions.valueAt(ijc);
                int NT = types.size();
                out.writeInt(NT);
                for (int it=0; it<NT; it++) {
                    out.writeInt(types.keyAt(it));
                    out.writeInt(types.valueAt(it));
                }
            }
        }

        void writeToParcelLocked(Parcel out, long uptimeUs, long elapsedRealtimeUs) {
            mOnBatteryBackgroundTimeBase.writeToParcel(out, uptimeUs, elapsedRealtimeUs);
            mOnBatteryScreenOffBackgroundTimeBase.writeToParcel(out, uptimeUs, elapsedRealtimeUs);
@@ -6899,6 +6925,8 @@ public class BatteryStatsImpl extends BatteryStats {
                Timer.writeTimerToParcel(out, timer, elapsedRealtimeUs);
            }

            writeJobCompletionsToParcelLocked(out);

            int NSE = mSensorStats.size();
            out.writeInt(NSE);
            for (int ise=0; ise<NSE; ise++) {
@@ -7114,6 +7142,24 @@ public class BatteryStatsImpl extends BatteryStats {
            }
        }

        void readJobCompletionsFromParcelLocked(Parcel in) {
            int numJobCompletions = in.readInt();
            mJobCompletions.clear();
            for (int j = 0; j < numJobCompletions; j++) {
                String jobName = in.readString();
                int numTypes = in.readInt();
                if (numTypes > 0) {
                    SparseIntArray types = new SparseIntArray();
                    for (int k = 0; k < numTypes; k++) {
                        int type = in.readInt();
                        int count = in.readInt();
                        types.put(type, count);
                    }
                    mJobCompletions.put(jobName, types);
                }
            }
        }

        void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
            mOnBatteryBackgroundTimeBase.readFromParcel(in);
            mOnBatteryScreenOffBackgroundTimeBase.readFromParcel(in);
@@ -7148,6 +7194,8 @@ public class BatteryStatsImpl extends BatteryStats {
                }
            }

            readJobCompletionsFromParcelLocked(in);

            int numSensors = in.readInt();
            mSensorStats.clear();
            for (int k = 0; k < numSensors; k++) {
@@ -8460,11 +8508,20 @@ public class BatteryStatsImpl extends BatteryStats {
            }
        }

        public void noteStopJobLocked(String name, long elapsedRealtimeMs) {
        public void noteStopJobLocked(String name, long elapsedRealtimeMs, int stopReason) {
            DualTimer t = mJobStats.stopObject(name);
            if (t != null) {
                t.stopRunningLocked(elapsedRealtimeMs);
            }
            if (mBsi.mOnBatteryTimeBase.isRunning()) {
                SparseIntArray types = mJobCompletions.get(name);
                if (types == null) {
                    types = new SparseIntArray();
                    mJobCompletions.put(name, types);
                }
                int last = types.get(stopReason, 0);
                types.put(stopReason, last + 1);
            }
        }

        public StopwatchTimer getWakelockTimerLocked(Wakelock wl, int type) {
@@ -11640,6 +11697,8 @@ public class BatteryStatsImpl extends BatteryStats {
                u.readJobSummaryFromParcelLocked(name, in);
            }

            u.readJobCompletionsFromParcelLocked(in);

            int NP = in.readInt();
            if (NP > 1000) {
                throw new ParcelFormatException("File corrupt: too many sensors " + NP);
@@ -12079,6 +12138,8 @@ public class BatteryStatsImpl extends BatteryStats {
                jobStats.valueAt(ij).writeSummaryFromParcelLocked(out, NOWREAL_SYS);
            }

            u.writeJobCompletionsToParcelLocked(out);

            int NSE = u.mSensorStats.size();
            out.writeInt(NSE);
            for (int ise=0; ise<NSE; ise++) {
+3 −3
Original line number Diff line number Diff line
@@ -280,7 +280,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase {

        // Stop timer
        curr = 1000 * (clocks.realtime = clocks.uptime = 161);
        bi.noteJobFinishLocked(jobName, UID);
        bi.noteJobFinishLocked(jobName, UID, 0);

        // Move to background
        curr = 1000 * (clocks.realtime = clocks.uptime = 202);
@@ -296,7 +296,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase {

        // Stop timer
        curr = 1000 * (clocks.realtime = clocks.uptime = 409);
        bi.noteJobFinishLocked(jobName, UID);
        bi.noteJobFinishLocked(jobName, UID, 0);

        // Test
        curr = 1000 * (clocks.realtime = clocks.uptime = 657);
@@ -319,7 +319,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase {
        final String jobName2 = "second_job";
        bi.noteJobStartLocked(jobName2, UID);
        assertEquals(2, bi.getUidStats().get(UID).getJobStats().size());
        bi.noteJobFinishLocked(jobName2, UID);
        bi.noteJobFinishLocked(jobName2, UID, 0);
    }

    @SmallTest
Loading