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

Commit 95031ef2 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Now track "active time" in procstats.

Associations now keep track of the time they are
actively involved in impacting their target application.
This is based on the procstate propagating through the
association being the same as the procstate of its target
process...  so it may count as active when there is
another reason for that process to be in the same state.

To do this, we now maintain a set of "tracking
associations" -- these are in-use associations that
we know we need to be tracking to determine whether
they are active.  This list is built based on whether
we at all consider an association during an oom_adj
computation, and at the end of that walked to determine
which of those associations are currently active.

Also add tracking of associations through external
provider references, with a tag name now needing to be
passed through so we can mark up the reason for the
external reference.

Test: manual
Bug: 110957691
Change-Id: I426a499834e20a9d7f2b439faf9cb398d9792fa2
parent bad8de91
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -462,7 +462,7 @@ public class Content {
                IBinder token = new Binder();
                try {
                    ContentProviderHolder holder = activityManager.getContentProviderExternal(
                            providerName, mUserId, token);
                            providerName, mUserId, token, "*cmd*");
                    if (holder == null) {
                        throw new IllegalStateException("Could not find provider: " + providerName);
                    }
+1 −1
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ public class ShellUiAutomatorBridge extends UiAutomatorBridge {
            IBinder token = new Binder();
            try {
                ContentProviderHolder holder = activityManager.getContentProviderExternal(
                        providerName, UserHandle.USER_SYSTEM, token);
                        providerName, UserHandle.USER_SYSTEM, token, "*uiautomator*");
                if (holder == null) {
                    throw new IllegalStateException("Could not find provider: " + providerName);
                }
+1 −1
Original line number Diff line number Diff line
@@ -268,7 +268,7 @@ interface IActivityManager {
    void showBootMessage(in CharSequence msg, boolean always);
    void killAllBackgroundProcesses();
    ContentProviderHolder getContentProviderExternal(in String name, int userId,
            in IBinder token);
            in IBinder token, String tag);
    void removeContentProviderExternal(in String name, in IBinder token);
    // Get memory information about the calling process.
    void getMyMemoryState(out ActivityManager.RunningAppProcessInfo outInfo);
+206 −27
Original line number Diff line number Diff line
@@ -21,37 +21,143 @@ import android.os.Parcel;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.TimeUtils;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Objects;

public final class AssociationState {
    private static final String TAG = "ProcessStats";
    private static final boolean DEBUG = false;

    private final String mPackage;
    private final ProcessStats mProcessStats;
    private final ProcessStats.PackageState mPackageState;
    private final String mProcessName;
    private final String mName;
    private final DurationsTable mDurations;

    public final class SourceState {
        final SourceKey mKey;
        int mProcStateSeq = -1;
        int mProcState = ProcessStats.STATE_NOTHING;
        boolean mInTrackingList;
        int mNesting;
        int mCount;
        long mStartUptime;
        long mDuration;
        long mTrackingUptime;
        int mActiveCount;
        long mActiveStartUptime;
        long mActiveDuration;

        SourceState(SourceKey key) {
            mKey = key;
        }

        public AssociationState getAssociationState() {
            return AssociationState.this;
        }

        public String getProcessName() {
            return mKey.mProcess;
        }

        public int getUid() {
            return mKey.mUid;
        }

        public void trackProcState(int procState, int seq, long now) {
            procState = ProcessState.PROCESS_STATE_TO_STATE[procState];
            if (seq != mProcStateSeq) {
                mProcStateSeq = seq;
                mProcState = procState;
            } else if (procState < mProcState) {
                mProcState = procState;
            }
            if (procState < ProcessStats.STATE_HOME) {
                if (!mInTrackingList) {
                    mInTrackingList = true;
                    mTrackingUptime = now;
                    mProcessStats.mTrackingAssociations.add(this);
                }
            } else {
                stopTracking(now);
            }
        }

        public void stop() {
            mNesting--;
            if (mNesting == 0) {
                mDuration += SystemClock.uptimeMillis() - mStartTime;
                mDuration += SystemClock.uptimeMillis() - mStartUptime;
                mNumActive--;
                stopTracking(SystemClock.uptimeMillis());
            }
        }

        int mNesting;
        int mCount;
        long mStartTime;
        long mDuration;
        void startActive(long now) {
            if (mInTrackingList) {
                if (mActiveStartUptime == 0) {
                    mActiveStartUptime = now;
                    mActiveCount++;
                }
            } else {
                Slog.wtf(TAG, "startActive while not tracking: " + this);
            }
        }

        void stopActive(long now) {
            if (mActiveStartUptime != 0) {
                if (!mInTrackingList) {
                    Slog.wtf(TAG, "stopActive while not tracking: " + this);
                }
                mActiveDuration += now - mActiveStartUptime;
                mActiveStartUptime = 0;
            }
        }

        void stopTracking(long now) {
            stopActive(now);
            if (mInTrackingList) {
                mInTrackingList = false;
                // Do a manual search for where to remove, since these objects will typically
                // be towards the end of the array.
                final ArrayList<SourceState> list = mProcessStats.mTrackingAssociations;
                for (int i = list.size() - 1; i >= 0; i--) {
                    if (list.get(i) == this) {
                        list.remove(i);
                        return;
                    }
                }
                Slog.wtf(TAG, "Stop tracking didn't find in tracking list: " + this);
            }
        }

    final static class SourceKey {
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder(64);
            sb.append("SourceState{").append(Integer.toHexString(System.identityHashCode(this)))
                    .append(" ").append(mKey.mProcess).append("/").append(mKey.mUid);
            if (mProcState != ProcessStats.STATE_NOTHING) {
                sb.append(" ").append(DumpUtils.STATE_NAMES[mProcState]).append(" #")
                        .append(mProcStateSeq);
            }
            sb.append("}");
            return sb.toString();
        }
    }

    private final static class SourceKey {
        /**
         * UID, consider this final.  Not final just to avoid a temporary object during lookup.
         */
        int mUid;

        /**
         * Process name, consider this final.  Not final just to avoid a temporary object during
         * lookup.
         */
        String mProcess;

        SourceKey(int uid, String process) {
@@ -82,7 +188,6 @@ public final class AssociationState {
            sb.append('}');
            return sb.toString();
        }

    }

    /**
@@ -92,18 +197,26 @@ public final class AssociationState {

    private final SourceKey mTmpSourceKey = new SourceKey(0, null);

    private ProcessState mProc;

    private int mNumActive;

    public AssociationState(ProcessStats processStats, String pkg, String name,
            String processName) {
        mPackage = pkg;
    public AssociationState(ProcessStats processStats, ProcessStats.PackageState packageState,
            String name, String processName, ProcessState proc) {
        mProcessStats = processStats;
        mPackageState = packageState;
        mName = name;
        mProcessName = processName;
        mDurations = new DurationsTable(processStats.mTableData);
        mProc = proc;
    }

    public int getUid() {
        return mPackageState.mUid;
    }

    public String getPackage() {
        return mPackage;
        return mPackageState.mPackageName;
    }

    public String getProcessName() {
@@ -114,18 +227,27 @@ public final class AssociationState {
        return mName;
    }

    public ProcessState getProcess() {
        return mProc;
    }

    public void setProcess(ProcessState proc) {
        mProc = proc;
    }

    public SourceState startSource(int uid, String processName) {
        mTmpSourceKey.mUid = uid;
        mTmpSourceKey.mProcess = processName;
        SourceState src = mSources.get(mTmpSourceKey);
        if (src == null) {
            src = new SourceState();
            mSources.put(new SourceKey(uid, processName), src);
            SourceKey key = new SourceKey(uid, processName);
            src = new SourceState(key);
            mSources.put(key, src);
        }
        src.mNesting++;
        if (src.mNesting == 1) {
            src.mCount++;
            src.mStartTime = SystemClock.uptimeMillis();
            src.mStartUptime = SystemClock.uptimeMillis();
            mNumActive++;
        }
        return src;
@@ -138,11 +260,13 @@ public final class AssociationState {
            final SourceState otherSrc = other.mSources.valueAt(isrc);
            SourceState mySrc = mSources.get(key);
            if (mySrc == null) {
                mySrc = new SourceState();
                mySrc = new SourceState(key);
                mSources.put(key, mySrc);
            }
            mySrc.mCount += otherSrc.mCount;
            mySrc.mDuration += otherSrc.mDuration;
            mySrc.mActiveCount += otherSrc.mActiveCount;
            mySrc.mActiveDuration += otherSrc.mActiveDuration;
        }
    }

@@ -160,8 +284,15 @@ public final class AssociationState {
                SourceState src = mSources.valueAt(isrc);
                if (src.mNesting > 0) {
                    src.mCount = 1;
                    src.mStartTime = now;
                    src.mStartUptime = now;
                    src.mDuration = 0;
                    if (src.mActiveStartUptime > 0) {
                        src.mActiveCount = 1;
                        src.mActiveStartUptime = now;
                    } else {
                        src.mActiveCount = 0;
                    }
                    src.mActiveDuration = 0;
                } else {
                    mSources.removeAt(isrc);
                }
@@ -169,7 +300,7 @@ public final class AssociationState {
        }
    }

    public void writeToParcel(ProcessStats stats, Parcel out, long now) {
    public void writeToParcel(ProcessStats stats, Parcel out, long nowUptime) {
        mDurations.writeToParcel(out);
        final int NSRC = mSources.size();
        out.writeInt(NSRC);
@@ -180,9 +311,15 @@ public final class AssociationState {
            stats.writeCommonString(out, key.mProcess);
            out.writeInt(src.mCount);
            out.writeLong(src.mDuration);
            out.writeInt(src.mActiveCount);
            out.writeLong(src.mActiveDuration);
        }
    }

    /**
     * Returns non-null if all else fine, else a String that describes the error that
     * caused it to fail.
     */
    public String readFromParcel(ProcessStats stats, Parcel in, int parcelVersion) {
        if (!mDurations.readFromParcel(in)) {
            return "Duration table corrupt";
@@ -195,21 +332,27 @@ public final class AssociationState {
            final int uid = in.readInt();
            final String procName = stats.readCommonString(in, parcelVersion);
            final SourceKey key = new SourceKey(uid, procName);
            final SourceState src = new SourceState();
            final SourceState src = new SourceState(key);
            src.mCount = in.readInt();
            src.mDuration = in.readLong();
            src.mActiveCount = in.readInt();
            src.mActiveDuration = in.readLong();
            mSources.put(key, src);
        }
        return null;
    }

    public void commitStateTime(long now) {
    public void commitStateTime(long nowUptime) {
        if (isInUse()) {
            for (int isrc = mSources.size() - 1; isrc >= 0; isrc--) {
                SourceState src = mSources.valueAt(isrc);
                if (src.mNesting > 0) {
                    src.mDuration += now - src.mStartTime;
                    src.mStartTime = now;
                    src.mDuration += nowUptime - src.mStartUptime;
                    src.mStartUptime = nowUptime;
                }
                if (src.mActiveStartUptime > 0) {
                    src.mActiveDuration += nowUptime - src.mActiveStartUptime;
                    src.mActiveStartUptime = nowUptime;
                }
            }
        }
@@ -237,7 +380,7 @@ public final class AssociationState {
            pw.print(src.mCount);
            long duration = src.mDuration;
            if (src.mNesting > 0) {
                duration += now - src.mStartTime;
                duration += now - src.mStartUptime;
            }
            if (dumpAll) {
                pw.print(" / Duration ");
@@ -248,11 +391,39 @@ public final class AssociationState {
            }
            DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
            if (src.mNesting > 0) {
                pw.print(" (running");
                if (src.mProcState != ProcessStats.STATE_NOTHING) {
                    pw.print(" / ");
                    pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
                    pw.print(" #");
                    pw.print(src.mProcStateSeq);
                }
                pw.print(")");
            }
            pw.println();
            if (src.mActiveCount > 0) {
                pw.print(prefixInner);
                pw.print("   Active count ");
                pw.print(src.mActiveCount);
                duration = src.mActiveDuration;
                if (src.mActiveStartUptime > 0) {
                    duration += now - src.mActiveStartUptime;
                }
                if (dumpAll) {
                    pw.print(" / Duration ");
                    TimeUtils.formatDuration(duration, pw);
                    pw.print(" / ");
                } else {
                    pw.print(" / time ");
                }
                DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
                if (src.mActiveStartUptime > 0) {
                    pw.print(" (running)");
                }
                pw.println();
            }
        }
    }

    public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers,
            String associationName, long now) {
@@ -277,7 +448,15 @@ public final class AssociationState {
            pw.print(src.mCount);
            long duration = src.mDuration;
            if (src.mNesting > 0) {
                duration += now - src.mStartTime;
                duration += now - src.mStartUptime;
            }
            pw.print(",");
            pw.print(duration);
            pw.print(",");
            pw.print(src.mActiveCount);
            duration = src.mActiveDuration;
            if (src.mActiveStartUptime > 0) {
                duration += now - src.mActiveStartUptime;
            }
            pw.print(",");
            pw.print(duration);
@@ -287,7 +466,7 @@ public final class AssociationState {

    public String toString() {
        return "AssociationState{" + Integer.toHexString(System.identityHashCode(this))
                + " " + mName + " pkg=" + mPackage + " proc="
                + Integer.toHexString(System.identityHashCode(this)) + "}";
                + " " + mName + " pkg=" + mPackageState.mPackageName + " proc="
                + Integer.toHexString(System.identityHashCode(mProc)) + "}";
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -71,7 +71,7 @@ public final class ProcessState {
    private static final boolean DEBUG_PARCEL = false;

    // Map from process states to the states we track.
    private static final int[] PROCESS_STATE_TO_STATE = new int[] {
    static final int[] PROCESS_STATE_TO_STATE = new int[] {
        STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT
        STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT_UI
        STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP
@@ -378,6 +378,10 @@ public final class ProcessState {
        }
    }

    public int getState() {
        return mCurState;
    }

    public void commitStateTime(long now) {
        if (mCurState != STATE_NOTHING) {
            long dur = now - mStartTime;
Loading