diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java index 8fef8378f33cc..4133c4d41aaab 100644 --- a/core/java/com/android/internal/app/procstats/AssociationState.java +++ b/core/java/com/android/internal/app/procstats/AssociationState.java @@ -16,8 +16,10 @@ package com.android.internal.app.procstats; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; +import android.os.Parcelable; import android.os.SystemClock; import android.os.UserHandle; import android.service.procstats.PackageAssociationProcessStatsProto; @@ -54,7 +56,14 @@ public final class AssociationState { private int mTotalActiveCount; private long mTotalActiveDuration; - public final class SourceState { + /** + * The state of the source process of an association. + */ + public static final class SourceState implements Parcelable { + private @NonNull final ProcessStats mProcessStats; + private @Nullable final AssociationState mAssociationState; + private @Nullable final ProcessState mTargetProcess; + private @Nullable SourceState mCommonSourceState; final SourceKey mKey; int mProcStateSeq = -1; int mProcState = ProcessStats.STATE_NOTHING; @@ -64,18 +73,24 @@ public final class AssociationState { long mStartUptime; long mDuration; long mTrackingUptime; + int mActiveNesting; int mActiveCount; int mActiveProcState = ProcessStats.STATE_NOTHING; long mActiveStartUptime; long mActiveDuration; DurationsTable mActiveDurations; - SourceState(SourceKey key) { + SourceState(@NonNull ProcessStats processStats, @Nullable AssociationState associationState, + @NonNull ProcessState targetProcess, SourceKey key) { + mProcessStats = processStats; + mAssociationState = associationState; + mTargetProcess = targetProcess; mKey = key; } + @Nullable public AssociationState getAssociationState() { - return AssociationState.this; + return mAssociationState; } public String getProcessName() { @@ -86,7 +101,20 @@ public final class AssociationState { return mKey.mUid; } + @Nullable + private SourceState getCommonSourceState(boolean createIfNeeded) { + if (mCommonSourceState == null) { + if (createIfNeeded) { + mCommonSourceState = mTargetProcess.getOrCreateSourceState(mKey); + } else { + Slog.wtf(TAG, "Unable to find common source state for " + mKey.mProcess); + } + } + return mCommonSourceState; + } + public void trackProcState(int procState, int seq, long now) { + final int processState = procState; procState = ProcessState.PROCESS_STATE_TO_STATE[procState]; if (seq != mProcStateSeq) { mProcStateSeq = seq; @@ -102,30 +130,81 @@ public final class AssociationState { if (!mInTrackingList) { mInTrackingList = true; mTrackingUptime = now; - mProcessStats.mTrackingAssociations.add(this); + if (mAssociationState != null) { + mProcessStats.mTrackingAssociations.add(this); + } + } + } + if (mAssociationState != null) { + final SourceState commonSource = getCommonSourceState(true); + if (commonSource != null) { + commonSource.trackProcState(processState, seq, now); } } } + long start() { + final long now = start(-1); + if (mAssociationState != null) { + final SourceState commonSource = getCommonSourceState(true); + if (commonSource != null) { + commonSource.start(now); + } + } + return now; + } + + long start(long now) { + mNesting++; + if (mNesting == 1) { + if (now < 0) { + now = SystemClock.uptimeMillis(); + } + mCount++; + mStartUptime = now; + } + return now; + } + public void stop() { - mNesting--; - if (mNesting == 0) { - final long now = SystemClock.uptimeMillis(); - mDuration += now - mStartUptime; - stopTracking(now); + final long now = stop(-1); + if (mAssociationState != null) { + final SourceState commonSource = getCommonSourceState(false); + if (commonSource != null) { + commonSource.stop(now); + } } } + long stop(long now) { + mNesting--; + if (mNesting == 0) { + if (now < 0) { + now = SystemClock.uptimeMillis(); + } + mDuration += now - mStartUptime; + stopTracking(now); + } + return now; + } + void startActive(long now) { + boolean startActive = false; if (mInTrackingList) { if (mActiveStartUptime == 0) { mActiveStartUptime = now; + mActiveNesting++; mActiveCount++; - AssociationState.this.mTotalActiveNesting++; - if (AssociationState.this.mTotalActiveNesting == 1) { - AssociationState.this.mTotalActiveCount++; - AssociationState.this.mTotalActiveStartUptime = now; + startActive = true; + if (mAssociationState != null) { + mAssociationState.mTotalActiveNesting++; + if (mAssociationState.mTotalActiveNesting == 1) { + mAssociationState.mTotalActiveCount++; + mAssociationState.mTotalActiveStartUptime = now; + } } + } else if (mAssociationState == null) { + mActiveNesting++; } if (mActiveProcState != mProcState) { if (mActiveProcState != ProcessStats.STATE_NOTHING) { @@ -133,6 +212,9 @@ public final class AssociationState { // so far and switch tracking to the new proc state. final long addedDuration = mActiveDuration + now - mActiveStartUptime; mActiveStartUptime = now; + if (mAssociationState != null) { + startActive = true; + } if (addedDuration != 0) { if (mActiveDurations == null) { makeDurations(); @@ -146,68 +228,233 @@ public final class AssociationState { } else { Slog.wtf(TAG, "startActive while not tracking: " + this); } + if (mAssociationState != null) { + final SourceState commonSource = getCommonSourceState(true); + if (commonSource != null && startActive) { + commonSource.startActive(now); + } + } } void stopActive(long now) { + boolean stopActive = false; if (mActiveStartUptime != 0) { - if (!mInTrackingList) { + if (!mInTrackingList && mAssociationState != null) { Slog.wtf(TAG, "stopActive while not tracking: " + this); } + mActiveNesting--; final long addedDuration = now - mActiveStartUptime; - mActiveStartUptime = 0; + mActiveStartUptime = mAssociationState != null || mActiveNesting == 0 ? 0 : now; + stopActive = mActiveStartUptime == 0; if (mActiveDurations != null) { mActiveDurations.addDuration(mActiveProcState, addedDuration); } else { mActiveDuration += addedDuration; } - AssociationState.this.mTotalActiveNesting--; - if (AssociationState.this.mTotalActiveNesting == 0) { - AssociationState.this.mTotalActiveDuration += now - - AssociationState.this.mTotalActiveStartUptime; - AssociationState.this.mTotalActiveStartUptime = 0; - if (VALIDATE_TIMES) { - if (mActiveDuration > AssociationState.this.mTotalActiveDuration) { - RuntimeException ex = new RuntimeException(); - ex.fillInStackTrace(); - Slog.w(TAG, "Source act duration " + mActiveDurations - + " exceeds total " + AssociationState.this.mTotalActiveDuration - + " in procstate " + mActiveProcState + " in source " - + mKey.mProcess + " to assoc " - + AssociationState.this.mName, ex); - } + if (mAssociationState != null) { + mAssociationState.mTotalActiveNesting--; + if (mAssociationState.mTotalActiveNesting == 0) { + mAssociationState.mTotalActiveDuration += now + - mAssociationState.mTotalActiveStartUptime; + mAssociationState.mTotalActiveStartUptime = 0; + if (VALIDATE_TIMES) { + if (mActiveDuration > mAssociationState.mTotalActiveDuration) { + RuntimeException ex = new RuntimeException(); + ex.fillInStackTrace(); + Slog.w(TAG, "Source act duration " + mActiveDurations + + " exceeds total " + mAssociationState.mTotalActiveDuration + + " in procstate " + mActiveProcState + " in source " + + mKey.mProcess + " to assoc " + + mAssociationState.mName, ex); + } + } } } } + + if (mAssociationState != null) { + final SourceState commonSource = getCommonSourceState(false); + if (commonSource != null && stopActive) { + commonSource.stopActive(now); + } + } + } + + boolean stopActiveIfNecessary(int curSeq, long now) { + if (mProcStateSeq != curSeq || mProcState >= ProcessStats.STATE_HOME) { + // If this association did not get touched the last time we computed + // process states, or its state ended up down in cached, then we no + // longer have a reason to track it at all. + stopActive(now); + stopTrackingProcState(); + return true; + } + return false; + } + + private void stopTrackingProcState() { + mInTrackingList = false; + mProcState = ProcessStats.STATE_NOTHING; + if (mAssociationState != null) { + final SourceState commonSource = getCommonSourceState(false); + if (commonSource != null) { + commonSource.stopTrackingProcState(); + } + } + } + + boolean isInUse() { + return mNesting > 0; + } + + void resetSafely(long now) { + if (isInUse()) { + mCount = 1; + mStartUptime = now; + mDuration = 0; + if (mActiveStartUptime > 0) { + mActiveCount = 1; + mActiveStartUptime = now; + } else { + mActiveCount = 0; + } + mActiveDuration = 0; + mActiveDurations = null; + } + } + + void commitStateTime(long nowUptime) { + if (mNesting > 0) { + mDuration += nowUptime - mStartUptime; + mStartUptime = nowUptime; + } + if (mActiveStartUptime > 0) { + final long addedDuration = nowUptime - mActiveStartUptime; + mActiveStartUptime = nowUptime; + if (mActiveDurations != null) { + mActiveDurations.addDuration(mActiveProcState, addedDuration); + } else { + mActiveDuration += addedDuration; + } + } } void makeDurations() { mActiveDurations = new DurationsTable(mProcessStats.mTableData); } - void stopTracking(long now) { - AssociationState.this.mTotalNesting--; - if (AssociationState.this.mTotalNesting == 0) { - AssociationState.this.mTotalDuration += now - - AssociationState.this.mTotalStartUptime; + private void stopTracking(long now) { + if (mAssociationState != null) { + mAssociationState.mTotalNesting--; + if (mAssociationState.mTotalNesting == 0) { + mAssociationState.mTotalDuration += now + - mAssociationState.mTotalStartUptime; + } } stopActive(now); if (mInTrackingList) { mInTrackingList = false; mProcState = ProcessStats.STATE_NOTHING; - // Do a manual search for where to remove, since these objects will typically - // be towards the end of the array. - final ArrayList list = mProcessStats.mTrackingAssociations; - for (int i = list.size() - 1; i >= 0; i--) { - if (list.get(i) == this) { - list.remove(i); - return; + if (mAssociationState != null) { + // Do a manual search for where to remove, since these objects will typically + // be towards the end of the array. + final ArrayList 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); } - Slog.wtf(TAG, "Stop tracking didn't find in tracking list: " + this); } } + void add(SourceState otherSrc) { + mCount += otherSrc.mCount; + mDuration += otherSrc.mDuration; + mActiveCount += otherSrc.mActiveCount; + if (otherSrc.mActiveDuration != 0 || otherSrc.mActiveDurations != null) { + // Only need to do anything if the other one has some duration data. + if (mActiveDurations != null) { + // If the target already has multiple durations, just add in whatever + // we have in the other. + if (otherSrc.mActiveDurations != null) { + mActiveDurations.addDurations(otherSrc.mActiveDurations); + } else { + mActiveDurations.addDuration(otherSrc.mActiveProcState, + otherSrc.mActiveDuration); + } + } else if (otherSrc.mActiveDurations != null) { + // The other one has multiple durations, but we don't. Expand to + // multiple durations and copy over. + makeDurations(); + mActiveDurations.addDurations(otherSrc.mActiveDurations); + if (mActiveDuration != 0) { + mActiveDurations.addDuration(mActiveProcState, mActiveDuration); + mActiveDuration = 0; + mActiveProcState = ProcessStats.STATE_NOTHING; + } + } else if (mActiveDuration != 0) { + // Both have a single inline duration... we can either add them together, + // or need to expand to multiple durations. + if (mActiveProcState == otherSrc.mActiveProcState) { + mActiveDuration += otherSrc.mActiveDuration; + } else { + // The two have durations with different proc states, need to turn + // in to multiple durations. + makeDurations(); + mActiveDurations.addDuration(mActiveProcState, mActiveDuration); + mActiveDurations.addDuration(otherSrc.mActiveProcState, + otherSrc.mActiveDuration); + mActiveDuration = 0; + mActiveProcState = ProcessStats.STATE_NOTHING; + } + } else { + // The other one has a duration, and we know the target doesn't. Copy over. + mActiveProcState = otherSrc.mActiveProcState; + mActiveDuration = otherSrc.mActiveDuration; + } + } + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mCount); + out.writeLong(mDuration); + out.writeInt(mActiveCount); + if (mActiveDurations != null) { + out.writeInt(1); + mActiveDurations.writeToParcel(out); + } else { + out.writeInt(0); + out.writeInt(mActiveProcState); + out.writeLong(mActiveDuration); + } + } + + @Override + public int describeContents() { + return 0; + } + + String readFromParcel(Parcel in) { + mCount = in.readInt(); + mDuration = in.readLong(); + mActiveCount = in.readInt(); + if (in.readInt() != 0) { + makeDurations(); + if (!mActiveDurations.readFromParcel(in)) { + return "Duration table corrupt: " + mKey + " <- " + toString(); + } + } else { + mActiveProcState = in.readInt(); + mActiveDuration = in.readLong(); + } + return null; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(64); @@ -222,7 +469,7 @@ public final class AssociationState { } } - public final class SourceDumpContainer { + static final class SourceDumpContainer { public final SourceState mState; public long mTotalTime; public long mActiveTime; @@ -256,6 +503,18 @@ public final class AssociationState { mPackage = pkg; } + SourceKey(ProcessStats stats, Parcel in, int parcelVersion) { + mUid = in.readInt(); + mProcess = stats.readCommonString(in, parcelVersion); + mPackage = stats.readCommonString(in, parcelVersion); + } + + void writeToParcel(ProcessStats stats, Parcel out) { + out.writeInt(mUid); + stats.writeCommonString(out, mProcess); + stats.writeCommonString(out, mPackage); + } + public boolean equals(Object o) { if (!(o instanceof SourceKey)) { return false; @@ -347,14 +606,11 @@ public final class AssociationState { } if (src == null) { SourceKey key = new SourceKey(uid, processName, packageName); - src = new SourceState(key); + src = new SourceState(mProcessStats, this, mProc, key); mSources.put(key, src); } - src.mNesting++; - if (src.mNesting == 1) { - final long now = SystemClock.uptimeMillis(); - src.mCount++; - src.mStartUptime = now; + final long now = src.start(); + if (now > 0) { mTotalNesting++; if (mTotalNesting == 1) { mTotalCount++; @@ -376,7 +632,7 @@ public final class AssociationState { SourceState mySrc = mSources.get(key); boolean newSrc = false; if (mySrc == null) { - mySrc = new SourceState(key); + mySrc = new SourceState(mProcessStats, this, mProc, key); mSources.put(key, mySrc); newSrc = true; } @@ -412,53 +668,7 @@ public final class AssociationState { } } } - mySrc.mCount += otherSrc.mCount; - mySrc.mDuration += otherSrc.mDuration; - mySrc.mActiveCount += otherSrc.mActiveCount; - if (otherSrc.mActiveDuration != 0 || otherSrc.mActiveDurations != null) { - // Only need to do anything if the other one has some duration data. - if (mySrc.mActiveDurations != null) { - // If the target already has multiple durations, just add in whatever - // we have in the other. - if (otherSrc.mActiveDurations != null) { - mySrc.mActiveDurations.addDurations(otherSrc.mActiveDurations); - } else { - mySrc.mActiveDurations.addDuration(otherSrc.mActiveProcState, - otherSrc.mActiveDuration); - } - } else if (otherSrc.mActiveDurations != null) { - // The other one has multiple durations, but we don't. Expand to - // multiple durations and copy over. - mySrc.makeDurations(); - mySrc.mActiveDurations.addDurations(otherSrc.mActiveDurations); - if (mySrc.mActiveDuration != 0) { - mySrc.mActiveDurations.addDuration(mySrc.mActiveProcState, - mySrc.mActiveDuration); - mySrc.mActiveDuration = 0; - mySrc.mActiveProcState = ProcessStats.STATE_NOTHING; - } - } else if (mySrc.mActiveDuration != 0) { - // Both have a single inline duration... we can either add them together, - // or need to expand to multiple durations. - if (mySrc.mActiveProcState == otherSrc.mActiveProcState) { - mySrc.mActiveDuration += otherSrc.mActiveDuration; - } else { - // The two have durations with different proc states, need to turn - // in to multiple durations. - mySrc.makeDurations(); - mySrc.mActiveDurations.addDuration(mySrc.mActiveProcState, - mySrc.mActiveDuration); - mySrc.mActiveDurations.addDuration(otherSrc.mActiveProcState, - otherSrc.mActiveDuration); - mySrc.mActiveDuration = 0; - mySrc.mActiveProcState = ProcessStats.STATE_NOTHING; - } - } else { - // The other one has a duration, and we know the target doesn't. Copy over. - mySrc.mActiveProcState = otherSrc.mActiveProcState; - mySrc.mActiveDuration = otherSrc.mActiveDuration; - } - } + mySrc.add(otherSrc); } } @@ -474,18 +684,8 @@ public final class AssociationState { // We have some active sources... clear out everything but those. for (int isrc = mSources.size() - 1; isrc >= 0; isrc--) { SourceState src = mSources.valueAt(isrc); - if (src.mNesting > 0) { - src.mCount = 1; - src.mStartUptime = now; - src.mDuration = 0; - if (src.mActiveStartUptime > 0) { - src.mActiveCount = 1; - src.mActiveStartUptime = now; - } else { - src.mActiveCount = 0; - } - src.mActiveDuration = 0; - src.mActiveDurations = null; + if (src.isInUse()) { + src.resetSafely(now); } else { mSources.removeAt(isrc); } @@ -512,20 +712,8 @@ public final class AssociationState { for (int isrc = 0; isrc < NSRC; isrc++) { final SourceKey key = mSources.keyAt(isrc); final SourceState src = mSources.valueAt(isrc); - out.writeInt(key.mUid); - stats.writeCommonString(out, key.mProcess); - stats.writeCommonString(out, key.mPackage); - out.writeInt(src.mCount); - out.writeLong(src.mDuration); - out.writeInt(src.mActiveCount); - if (src.mActiveDurations != null) { - out.writeInt(1); - src.mActiveDurations.writeToParcel(out); - } else { - out.writeInt(0); - out.writeInt(src.mActiveProcState); - out.writeLong(src.mActiveDuration); - } + key.writeToParcel(stats, out); + src.writeToParcel(out, 0); } } @@ -543,22 +731,11 @@ public final class AssociationState { return "Association with bad src count: " + NSRC; } for (int isrc = 0; isrc < NSRC; isrc++) { - final int uid = in.readInt(); - final String procName = stats.readCommonString(in, parcelVersion); - final String pkgName = stats.readCommonString(in, parcelVersion); - final SourceKey key = new SourceKey(uid, procName, pkgName); - final SourceState src = new SourceState(key); - src.mCount = in.readInt(); - src.mDuration = in.readLong(); - src.mActiveCount = in.readInt(); - if (in.readInt() != 0) { - src.makeDurations(); - if (!src.mActiveDurations.readFromParcel(in)) { - return "Duration table corrupt: " + key + " <- " + src; - } - } else { - src.mActiveProcState = in.readInt(); - src.mActiveDuration = in.readLong(); + final SourceKey key = new SourceKey(stats, in, parcelVersion); + final SourceState src = new SourceState(mProcessStats, this, mProc, key); + final String errMsg = src.readFromParcel(in); + if (errMsg != null) { + return errMsg; } if (VALIDATE_TIMES) { if (src.mDuration > mTotalDuration) { @@ -585,19 +762,7 @@ public final class AssociationState { if (isInUse()) { for (int isrc = mSources.size() - 1; isrc >= 0; isrc--) { SourceState src = mSources.valueAt(isrc); - if (src.mNesting > 0) { - src.mDuration += nowUptime - src.mStartUptime; - src.mStartUptime = nowUptime; - } - if (src.mActiveStartUptime > 0) { - final long addedDuration = nowUptime - src.mActiveStartUptime; - src.mActiveStartUptime = nowUptime; - if (src.mActiveDurations != null) { - src.mActiveDurations.addDuration(src.mActiveProcState, addedDuration); - } else { - src.mActiveDuration += addedDuration; - } - } + src.commitStateTime(nowUptime); } if (mTotalNesting > 0) { mTotalDuration += nowUptime - mTotalStartUptime; @@ -644,12 +809,12 @@ public final class AssociationState { return 0; }; - public ArrayList> createSortedAssociations(long now, - long totalTime) { - final int NSRC = mSources.size(); - ArrayList> sources = new ArrayList<>(NSRC); - for (int isrc = 0; isrc < NSRC; isrc++) { - final SourceState src = mSources.valueAt(isrc); + static ArrayList> createSortedAssociations(long now, + long totalTime, ArrayMap inSources) { + final int numOfSources = inSources.size(); + ArrayList> sources = new ArrayList<>(numOfSources); + for (int isrc = 0; isrc < numOfSources; isrc++) { + final SourceState src = inSources.valueAt(isrc); final SourceDumpContainer cont = new SourceDumpContainer(src); long duration = src.mDuration; if (src.mNesting > 0) { @@ -660,7 +825,7 @@ public final class AssociationState { if (cont.mActiveTime < 0) { cont.mActiveTime = -cont.mActiveTime; } - sources.add(new Pair<>(mSources.keyAt(isrc), cont)); + sources.add(new Pair<>(inSources.keyAt(isrc), cont)); } Collections.sort(sources, ASSOCIATION_COMPARATOR); return sources; @@ -722,6 +887,14 @@ public final class AssociationState { TimeUtils.formatDuration(mTotalStartUptime, now, pw); pw.println(); } + + dumpSources(pw, prefix, prefixInner, prefixInnerInner, sources, now, totalTime, + reqPackage, dumpDetails, dumpAll); + } + + static void dumpSources(PrintWriter pw, String prefix, String prefixInner, + String prefixInnerInner, ArrayList> sources, + long now, long totalTime, String reqPackage, boolean dumpDetails, boolean dumpAll) { final int NSRC = sources.size(); for (int isrc = 0; isrc < NSRC; isrc++) { final SourceKey key = sources.get(isrc).first; @@ -826,7 +999,7 @@ public final class AssociationState { } } - void dumpActiveDurationSummary(PrintWriter pw, final SourceState src, long totalTime, + static void dumpActiveDurationSummary(PrintWriter pw, final SourceState src, long totalTime, long now, boolean dumpAll) { long duration = dumpTime(null, null, src, totalTime, now, false, false); final boolean isRunning = duration < 0; @@ -846,8 +1019,8 @@ public final class AssociationState { pw.println(); } - long dumpTime(PrintWriter pw, String prefix, final SourceState src, long overallTime, long now, - boolean dumpDetails, boolean dumpAll) { + static long dumpTime(PrintWriter pw, String prefix, final SourceState src, long overallTime, + long now, boolean dumpDetails, boolean dumpAll) { long totalTime = 0; boolean isRunning = false; for (int iprocstate = 0; iprocstate < ProcessStats.STATE_COUNT; iprocstate++) { diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java index ab58fc0eeafc1..0e01ab5d57c8e 100644 --- a/core/java/com/android/internal/app/procstats/ProcessState.java +++ b/core/java/com/android/internal/app/procstats/ProcessState.java @@ -63,6 +63,8 @@ import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoUtils; import com.android.internal.app.ProcessMap; +import com.android.internal.app.procstats.AssociationState.SourceKey; +import com.android.internal.app.procstats.AssociationState.SourceState; import com.android.internal.app.procstats.ProcessStats.PackageState; import com.android.internal.app.procstats.ProcessStats.ProcessStateHolder; import com.android.internal.app.procstats.ProcessStats.TotalMemoryUseCollection; @@ -162,6 +164,11 @@ public final class ProcessState { // Set in computeProcessTimeLocked and used by COMPARATOR to sort. Be careful. private long mTmpTotalTime; + /** + * The combined source states which has or had an association with this process. + */ + ArrayMap mCommonSources; + /** * Create a new top-level process state, for the initial case where there is only * a single package running in a process. The initial state is not running. @@ -267,6 +274,21 @@ public final class ProcessState { addCachedKill(other.mNumCachedKill, other.mMinCachedKillPss, other.mAvgCachedKillPss, other.mMaxCachedKillPss); } + if (other.mCommonSources != null) { + if (mCommonSources == null) { + mCommonSources = new ArrayMap<>(); + } + int size = other.mCommonSources.size(); + for (int i = 0; i < size; i++) { + final SourceKey key = other.mCommonSources.keyAt(i); + SourceState state = mCommonSources.get(key); + if (state == null) { + state = new SourceState(mStats, null, this, key); + mCommonSources.put(key, state); + } + state.add(other.mCommonSources.valueAt(i)); + } + } } public void resetSafely(long now) { @@ -278,6 +300,17 @@ public final class ProcessState { mNumExcessiveCpu = 0; mNumCachedKill = 0; mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0; + // Reset the combine source state. + if (mCommonSources != null) { + for (int ip = mCommonSources.size() - 1; ip >= 0; ip--) { + final SourceState state = mCommonSources.valueAt(ip); + if (state.isInUse()) { + state.resetSafely(now); + } else { + mCommonSources.removeAt(ip); + } + } + } } public void makeDead() { @@ -308,9 +341,18 @@ public final class ProcessState { out.writeLong(mAvgCachedKillPss); out.writeLong(mMaxCachedKillPss); } + // The combined source state of all associations. + final int numOfSources = mCommonSources != null ? mCommonSources.size() : 0; + out.writeInt(numOfSources); + for (int i = 0; i < numOfSources; i++) { + final SourceKey key = mCommonSources.keyAt(i); + final SourceState src = mCommonSources.valueAt(i); + key.writeToParcel(mStats, out); + src.writeToParcel(out, 0); + } } - public boolean readFromParcel(Parcel in, boolean fully) { + boolean readFromParcel(Parcel in, int version, boolean fully) { boolean multiPackage = in.readInt() != 0; if (fully) { mMultiPackage = multiPackage; @@ -337,6 +379,19 @@ public final class ProcessState { } else { mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0; } + + // The combined source state of all associations. + final int numOfSources = in.readInt(); + if (numOfSources > 0) { + mCommonSources = new ArrayMap<>(numOfSources); + for (int i = 0; i < numOfSources; i++) { + final SourceKey key = new SourceKey(mStats, in, version); + final SourceState src = new SourceState(mStats, null, this, key); + src.readFromParcel(in); + mCommonSources.put(key, src); + } + } + return true; } @@ -433,6 +488,12 @@ public final class ProcessState { mTotalRunningStartTime = now; } mStartTime = now; + if (mCommonSources != null) { + for (int ip = mCommonSources.size() - 1; ip >= 0; ip--) { + final SourceState src = mCommonSources.valueAt(ip); + src.commitStateTime(now); + } + } } public void incActiveServices(String serviceName) { @@ -722,6 +783,18 @@ public final class ProcessState { return mPssTable.getValueForId((byte)state, PSS_RSS_MAXIMUM); } + SourceState getOrCreateSourceState(SourceKey key) { + if (mCommonSources == null) { + mCommonSources = new ArrayMap<>(); + } + SourceState state = mCommonSources.get(key); + if (state == null) { + state = new SourceState(mStats, null, this, key); + mCommonSources.put(key, state); + } + return state; + } + /** * Sums up the PSS data and adds it to 'data'. * @@ -1038,7 +1111,8 @@ public final class ProcessState { } } - public void dumpInternalLocked(PrintWriter pw, String prefix, boolean dumpAll) { + void dumpInternalLocked(PrintWriter pw, String prefix, String reqPackage, + long totalTime, long now, boolean dumpAll) { if (dumpAll) { pw.print(prefix); pw.print("myID="); pw.print(Integer.toHexString(System.identityHashCode(this))); @@ -1053,6 +1127,13 @@ public final class ProcessState { pw.print("/"); pw.print(mCommonProcess.mUid); pw.print(" pkg="); pw.println(mCommonProcess.mPackage); } + if (mCommonSources != null) { + pw.print(prefix); pw.println("Aggregated Association Sources:"); + AssociationState.dumpSources( + pw, prefix + " ", prefix + " ", prefix + " ", + AssociationState.createSortedAssociations(now, totalTime, mCommonSources), + now, totalTime, reqPackage, true, dumpAll); + } } if (mActive) { pw.print(prefix); pw.print("mActive="); pw.println(mActive); @@ -1559,7 +1640,7 @@ public final class ProcessState { } mStats.dumpFilteredAssociationStatesProtoForProc(proto, ProcessStatsProto.ASSOCS, - now, this, procToPkgMap, uidToPkgMap); + now, this, uidToPkgMap); proto.end(token); } } diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java index 11e55b852516a..a9061aca1aa6e 100644 --- a/core/java/com/android/internal/app/procstats/ProcessStats.java +++ b/core/java/com/android/internal/app/procstats/ProcessStats.java @@ -29,7 +29,6 @@ import android.service.procstats.ProcessStatsAssociationProto; import android.service.procstats.ProcessStatsAvailablePagesProto; import android.service.procstats.ProcessStatsPackageProto; import android.service.procstats.ProcessStatsSectionProto; -import android.text.TextUtils; import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; @@ -187,7 +186,7 @@ public final class ProcessStats implements Parcelable { {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"}; // Current version of the parcel format. - private static final int PARCEL_VERSION = 38; + private static final int PARCEL_VERSION = 39; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0x50535454; @@ -1113,12 +1112,12 @@ public final class ProcessStats implements Parcelable { final long vers = in.readLong(); ProcessState proc = hadData ? mProcesses.get(procName, uid) : null; if (proc != null) { - if (!proc.readFromParcel(in, false)) { + if (!proc.readFromParcel(in, version, false)) { return; } } else { proc = new ProcessState(this, pkgName, uid, vers, procName); - if (!proc.readFromParcel(in, true)) { + if (!proc.readFromParcel(in, version, true)) { return; } } @@ -1198,13 +1197,13 @@ public final class ProcessStats implements Parcelable { // they will find and use it from the global procs. ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null; if (proc != null) { - if (!proc.readFromParcel(in, false)) { + if (!proc.readFromParcel(in, version, false)) { return; } } else { proc = new ProcessState(commonProc, pkgName, uid, vers, procName, 0); - if (!proc.readFromParcel(in, true)) { + if (!proc.readFromParcel(in, version, true)) { return; } } @@ -1439,16 +1438,15 @@ public final class ProcessStats implements Parcelable { final int NUM = mTrackingAssociations.size(); for (int i = NUM - 1; i >= 0; i--) { final AssociationState.SourceState act = mTrackingAssociations.get(i); - if (act.mProcStateSeq != curSeq || act.mProcState >= ProcessStats.STATE_HOME) { - // If this association did not get touched the last time we computed - // process states, or its state ended up down in cached, then we no - // longer have a reason to track it at all. - act.stopActive(now); - act.mInTrackingList = false; - act.mProcState = ProcessStats.STATE_NOTHING; + if (act.stopActiveIfNecessary(curSeq, now)) { mTrackingAssociations.remove(i); } else { - final ProcessState proc = act.getAssociationState().getProcess(); + final AssociationState asc = act.getAssociationState(); + if (asc == null) { + Slog.wtf(TAG, act.toString() + " shouldn't be in the tracking list."); + continue; + } + final ProcessState proc = asc.getProcess(); if (proc != null) { final int procState = proc.getCombinedState() % STATE_COUNT; if (act.mProcState == procState) { @@ -1476,7 +1474,7 @@ public final class ProcessStats implements Parcelable { } else { // Don't need rate limiting on it. Slog.wtf(TAG, "Tracking association without process: " + act - + " in " + act.getAssociationState()); + + " in " + asc); } } } @@ -1640,7 +1638,8 @@ public final class ProcessStats implements Parcelable { ALL_PROC_STATES, now); proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES, now); - proc.dumpInternalLocked(pw, " ", dumpAll); + proc.dumpInternalLocked(pw, " ", reqPackage, + totalTime, now, dumpAll); } } else { ArrayList procs = new ArrayList(); @@ -1696,7 +1695,8 @@ public final class ProcessStats implements Parcelable { } final AssociationDumpContainer cont = new AssociationDumpContainer(asc); - cont.mSources = asc.createSortedAssociations(now, totalTime); + cont.mSources = AssociationState + .createSortedAssociations(now, totalTime, asc.mSources); cont.mTotalTime = asc.getTotalDuration(now); cont.mActiveTime = asc.getActiveDuration(now); associations.add(cont); @@ -1777,7 +1777,7 @@ public final class ProcessStats implements Parcelable { proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES, now); proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES, now); - proc.dumpInternalLocked(pw, " ", dumpAll); + proc.dumpInternalLocked(pw, " ", reqPackage, totalTime, now, dumpAll); } } pw.print(" Total procs: "); pw.print(numShownProcs); @@ -1792,6 +1792,10 @@ public final class ProcessStats implements Parcelable { for (int i = 0; i < mTrackingAssociations.size(); i++) { final AssociationState.SourceState src = mTrackingAssociations.get(i); final AssociationState asc = src.getAssociationState(); + if (asc == null) { + Slog.wtf(TAG, src.toString() + " shouldn't be in the tracking list."); + continue; + } pw.print(" #"); pw.print(i); pw.print(": "); @@ -2353,85 +2357,47 @@ public final class ProcessStats implements Parcelable { * @param fieldId The proto output field ID * @param now The timestamp when the dump was initiated. * @param procState The target process where its association states should be dumped. - * @param proc2Pkg The map between process to packages running within it. * @param uidToPkgMap The map between UID to packages with this UID */ public void dumpFilteredAssociationStatesProtoForProc(ProtoOutputStream proto, long fieldId, long now, ProcessState procState, - final ProcessMap> proc2Pkg, final SparseArray> uidToPkgMap) { if (procState.isMultiPackage() && procState.getCommonProcess() != procState) { // It's a per-package process state, don't bother to write into statsd return; } - ArrayMap assocVals = new ArrayMap<>(); - final String procName = procState.getName(); - final int procUid = procState.getUid(); - final long procVersion = procState.getVersion(); - final ArraySet packages = proc2Pkg.get(procName, procUid); - if (packages == null || packages.isEmpty()) { - // Shouldn't happen - return; - } - for (int i = packages.size() - 1; i >= 0; i--) { - final PackageState pkgState = packages.valueAt(i); - final ArrayMap associations = pkgState.mAssociations; - for (int j = associations.size() - 1; j >= 0; j--) { - final AssociationState assoc = associations.valueAt(j); - // Make sure this association is really about this process - if (!TextUtils.equals(assoc.getProcessName(), procName)) { - continue; - } - final ArrayMap sources = assoc.mSources; - for (int k = sources.size() - 1; k >= 0; k--) { - final SourceKey key = sources.keyAt(k); - final SourceState state = sources.valueAt(k); - long[] vals = assocVals.get(key); - if (vals == null) { - vals = new long[2]; - assocVals.put(key, vals); - } - vals[0] += state.mDuration; - vals[1] += state.mCount; - if (state.mNesting > 0) { - vals[0] += now - state.mStartUptime; - } - } - } - } - final IProcessStats procStatsService = IProcessStats.Stub.asInterface( - ServiceManager.getService(SERVICE_NAME)); - if (procStatsService != null) { - try { - final long minimum = procStatsService.getMinAssociationDumpDuration(); - if (minimum > 0) { - // Now filter out unnecessary ones. - for (int i = assocVals.size() - 1; i >= 0; i--) { - final long[] vals = assocVals.valueAt(i); - if (vals[0] < minimum) { - assocVals.removeAt(i); + final ArrayMap sources = procState.mCommonSources; + if (sources != null && !sources.isEmpty()) { + final IProcessStats procStatsService = IProcessStats.Stub.asInterface( + ServiceManager.getService(SERVICE_NAME)); + if (procStatsService != null) { + try { + final long minimum = procStatsService.getMinAssociationDumpDuration(); + for (int i = sources.size() - 1; i >= 0; i--) { + final SourceState src = sources.valueAt(i); + long duration = src.mDuration; + if (src.mNesting > 0) { + duration += now - src.mStartUptime; } + if (duration < minimum) { + continue; + } + final SourceKey key = sources.keyAt(i); + final long token = proto.start(fieldId); + final int idx = uidToPkgMap.indexOfKey(key.mUid); + ProcessState.writeCompressedProcessName(proto, + ProcessStatsAssociationProto.ASSOC_PROCESS_NAME, + key.mProcess, key.mPackage, + idx >= 0 && uidToPkgMap.valueAt(idx).size() > 1); + proto.write(ProcessStatsAssociationProto.ASSOC_UID, key.mUid); + proto.write(ProcessStatsAssociationProto.TOTAL_COUNT, src.mCount); + proto.write(ProcessStatsAssociationProto.TOTAL_DURATION_SECS, + (int) (duration / 1000)); + proto.end(token); } + } catch (RemoteException e) { + // ignore. } - } catch (RemoteException e) { - // ignore. - } - } - if (!assocVals.isEmpty()) { - for (int i = assocVals.size() - 1; i >= 0; i--) { - final SourceKey key = assocVals.keyAt(i); - final long[] vals = assocVals.valueAt(i); - final long token = proto.start(fieldId); - final int idx = uidToPkgMap.indexOfKey(key.mUid); - ProcessState.writeCompressedProcessName(proto, - ProcessStatsAssociationProto.ASSOC_PROCESS_NAME, - key.mProcess, key.mPackage, - idx >= 0 && uidToPkgMap.valueAt(idx).size() > 1); - proto.write(ProcessStatsAssociationProto.ASSOC_UID, key.mUid); - proto.write(ProcessStatsAssociationProto.TOTAL_COUNT, (int) vals[1]); - proto.write(ProcessStatsAssociationProto.TOTAL_DURATION_SECS, - (int) (vals[0] / 1000)); - proto.end(token); } } }