blob: dea35669b591747d7617190ce491d9b7e8537a02 [file] [log] [blame]
/*
* Copyright (C) 2018 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.app.procstats;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.SystemClock;
import android.os.UserHandle;
import android.service.procstats.PackageAssociationProcessStatsProto;
import android.service.procstats.PackageAssociationSourceProcessStatsProto;
import android.util.ArrayMap;
import android.util.Pair;
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Objects;
public final class AssociationState {
private static final String TAG = "ProcessStats";
private static final boolean DEBUG = false;
private static final boolean VALIDATE_TIMES = false;
private final ProcessStats mProcessStats;
private final ProcessStats.PackageState mPackageState;
private final String mProcessName;
private final String mName;
private int mTotalNesting;
private long mTotalStartUptime;
private int mTotalCount;
private long mTotalDuration;
private int mTotalActiveNesting;
private long mTotalActiveStartUptime;
private int mTotalActiveCount;
private long mTotalActiveDuration;
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;
int mActiveProcState = ProcessStats.STATE_NOTHING;
long mActiveStartUptime;
long mActiveDuration;
DurationsTable mActiveDurations;
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 the proc state has become better than cached, then we want to
// start tracking it to count when it is actually active. If it drops
// down to cached, we will clean it up when we later evaluate all currently
// tracked associations in ProcessStats.updateTrackingAssociationsLocked().
if (!mInTrackingList) {
mInTrackingList = true;
mTrackingUptime = now;
mProcessStats.mTrackingAssociations.add(this);
}
}
}
public void stop() {
mNesting--;
if (mNesting == 0) {
final long now = SystemClock.uptimeMillis();
mDuration += now - mStartUptime;
stopTracking(now);
}
}
void startActive(long now) {
if (mInTrackingList) {
if (mActiveStartUptime == 0) {
mActiveStartUptime = now;
mActiveCount++;
AssociationState.this.mTotalActiveNesting++;
if (AssociationState.this.mTotalActiveNesting == 1) {
AssociationState.this.mTotalActiveCount++;
AssociationState.this.mTotalActiveStartUptime = now;
}
}
if (mActiveProcState != mProcState) {
if (mActiveProcState != ProcessStats.STATE_NOTHING) {
// Currently active proc state changed, need to store the duration
// so far and switch tracking to the new proc state.
final long addedDuration = mActiveDuration + now - mActiveStartUptime;
mActiveStartUptime = now;
if (addedDuration != 0) {
if (mActiveDurations == null) {
makeDurations();
}
mActiveDurations.addDuration(mActiveProcState, addedDuration);
mActiveDuration = 0;
}
}
mActiveProcState = mProcState;
}
} 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);
}
final long addedDuration = now - mActiveStartUptime;
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);
}
}
}
}
}
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;
}
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<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);
}
}
@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();
}
}
public final class SourceDumpContainer {
public final SourceState mState;
public long mTotalTime;
public long mActiveTime;
public SourceDumpContainer(SourceState state) {
mState = state;
}
}
public static final 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;
/**
* Optional package name, or null; consider this final. Not final just to avoid a
* temporary object during lookup.
*/
@Nullable String mPackage;
SourceKey(int uid, String process, String pkg) {
mUid = uid;
mProcess = process;
mPackage = pkg;
}
public boolean equals(Object o) {
if (!(o instanceof SourceKey)) {
return false;
}
SourceKey s = (SourceKey) o;
return s.mUid == mUid && Objects.equals(s.mProcess, mProcess)
&& Objects.equals(s.mPackage, mPackage);
}
@Override
public int hashCode() {
return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode())
^ (mPackage == null ? 0 : (mPackage.hashCode() * 33));
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
sb.append("SourceKey{");
UserHandle.formatUid(sb, mUid);
sb.append(' ');
sb.append(mProcess);
sb.append(' ');
sb.append(mPackage);
sb.append('}');
return sb.toString();
}
}
/**
* All known sources for this target component... uid -> process name -> source state.
*/
private final ArrayMap<SourceKey, SourceState> mSources = new ArrayMap<>();
private static final SourceKey sTmpSourceKey = new SourceKey(0, null, null);
private ProcessState mProc;
public AssociationState(ProcessStats processStats, ProcessStats.PackageState packageState,
String name, String processName, ProcessState proc) {
mProcessStats = processStats;
mPackageState = packageState;
mName = name;
mProcessName = processName;
mProc = proc;
}
public int getUid() {
return mPackageState.mUid;
}
public String getPackage() {
return mPackageState.mPackageName;
}
public String getProcessName() {
return mProcessName;
}
public String getName() {
return mName;
}
public ProcessState getProcess() {
return mProc;
}
public void setProcess(ProcessState proc) {
mProc = proc;
}
public long getTotalDuration(long now) {
return mTotalDuration
+ (mTotalNesting > 0 ? (now - mTotalStartUptime) : 0);
}
public long getActiveDuration(long now) {
return mTotalActiveDuration
+ (mTotalActiveNesting > 0 ? (now - mTotalActiveStartUptime) : 0);
}
public SourceState startSource(int uid, String processName, String packageName) {
SourceState src;
synchronized (sTmpSourceKey) {
sTmpSourceKey.mUid = uid;
sTmpSourceKey.mProcess = processName;
sTmpSourceKey.mPackage = packageName;
src = mSources.get(sTmpSourceKey);
}
if (src == null) {
SourceKey key = new SourceKey(uid, processName, packageName);
src = new SourceState(key);
mSources.put(key, src);
}
src.mNesting++;
if (src.mNesting == 1) {
final long now = SystemClock.uptimeMillis();
src.mCount++;
src.mStartUptime = now;
mTotalNesting++;
if (mTotalNesting == 1) {
mTotalCount++;
mTotalStartUptime = now;
}
}
return src;
}
public void add(AssociationState other) {
mTotalCount += other.mTotalCount;
final long origDuration = mTotalDuration;
mTotalDuration += other.mTotalDuration;
mTotalActiveCount += other.mTotalActiveCount;
mTotalActiveDuration += other.mTotalActiveDuration;
for (int isrc = other.mSources.size() - 1; isrc >= 0; isrc--) {
final SourceKey key = other.mSources.keyAt(isrc);
final SourceState otherSrc = other.mSources.valueAt(isrc);
SourceState mySrc = mSources.get(key);
boolean newSrc = false;
if (mySrc == null) {
mySrc = new SourceState(key);
mSources.put(key, mySrc);
newSrc = true;
}
if (VALIDATE_TIMES) {
Slog.w(TAG, "Adding tot duration " + mySrc.mDuration + "+"
+ otherSrc.mDuration
+ (newSrc ? " (new)" : " (old)") + " (total "
+ origDuration + "+" + other.mTotalDuration + ") in source "
+ mySrc.mKey.mProcess + " to assoc " + mName);
if ((mySrc.mDuration + otherSrc.mDuration) > mTotalDuration) {
RuntimeException ex = new RuntimeException();
ex.fillInStackTrace();
Slog.w(TAG, "Source tot duration " + mySrc.mDuration + "+"
+ otherSrc.mDuration
+ (newSrc ? " (new)" : " (old)") + " exceeds total "
+ origDuration + "+" + other.mTotalDuration + " in source "
+ mySrc.mKey.mProcess + " to assoc " + mName, ex);
}
if (mySrc.mActiveDurations == null && otherSrc.mActiveDurations == null) {
Slog.w(TAG, "Adding act duration " + mySrc.mActiveDuration
+ "+" + otherSrc.mActiveDuration
+ (newSrc ? " (new)" : " (old)") + " (total "
+ origDuration + "+" + other.mTotalDuration + ") in source "
+ mySrc.mKey.mProcess + " to assoc " + mName);
if ((mySrc.mActiveDuration + otherSrc.mActiveDuration) > mTotalDuration) {
RuntimeException ex = new RuntimeException();
ex.fillInStackTrace();
Slog.w(TAG, "Source act duration " + mySrc.mActiveDuration + "+"
+ otherSrc.mActiveDuration
+ (newSrc ? " (new)" : " (old)") + " exceeds total "
+ origDuration + "+" + other.mTotalDuration + " in source "
+ mySrc.mKey.mProcess + " to assoc " + mName, ex);
}
}
}
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;
}
}
}
}
public boolean isInUse() {
return mTotalNesting > 0;
}
public void resetSafely(long now) {
if (!isInUse()) {
mSources.clear();
mTotalCount = mTotalActiveCount = 0;
} else {
// 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;
} else {
mSources.removeAt(isrc);
}
}
mTotalCount = 1;
mTotalStartUptime = now;
if (mTotalActiveNesting > 0) {
mTotalActiveCount = 1;
mTotalActiveStartUptime = now;
} else {
mTotalActiveCount = 0;
}
}
mTotalDuration = mTotalActiveDuration = 0;
}
public void writeToParcel(ProcessStats stats, Parcel out, long nowUptime) {
out.writeInt(mTotalCount);
out.writeLong(mTotalDuration);
out.writeInt(mTotalActiveCount);
out.writeLong(mTotalActiveDuration);
final int NSRC = mSources.size();
out.writeInt(NSRC);
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);
}
}
}
/**
* 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) {
mTotalCount = in.readInt();
mTotalDuration = in.readLong();
mTotalActiveCount = in.readInt();
mTotalActiveDuration = in.readLong();
final int NSRC = in.readInt();
if (NSRC < 0 || NSRC > 100000) {
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();
}
if (VALIDATE_TIMES) {
if (src.mDuration > mTotalDuration) {
RuntimeException ex = new RuntimeException();
ex.fillInStackTrace();
Slog.w(TAG, "Reading tot duration " + src.mDuration
+ " exceeds total " + mTotalDuration + " in source "
+ src.mKey.mProcess + " to assoc " + mName, ex);
}
if (src.mActiveDurations == null && src.mActiveDuration > mTotalDuration) {
RuntimeException ex = new RuntimeException();
ex.fillInStackTrace();
Slog.w(TAG, "Reading act duration " + src.mActiveDuration
+ " exceeds total " + mTotalDuration + " in source "
+ src.mKey.mProcess + " to assoc " + mName, ex);
}
}
mSources.put(key, src);
}
return null;
}
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 += 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;
}
}
}
if (mTotalNesting > 0) {
mTotalDuration += nowUptime - mTotalStartUptime;
mTotalStartUptime = nowUptime;
}
if (mTotalActiveNesting > 0) {
mTotalActiveDuration += nowUptime - mTotalActiveStartUptime;
mTotalActiveStartUptime = nowUptime;
}
}
}
public boolean hasProcessOrPackage(String procName) {
if (mProcessName.equals(procName)) {
return true;
}
final int NSRC = mSources.size();
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceKey key = mSources.keyAt(isrc);
if (procName.equals(key.mProcess) || procName.equals(key.mPackage)) {
return true;
}
}
return false;
}
static final Comparator<Pair<SourceKey, SourceDumpContainer>> ASSOCIATION_COMPARATOR =
(o1, o2) -> {
if (o1.second.mActiveTime != o2.second.mActiveTime) {
return o1.second.mActiveTime > o2.second.mActiveTime ? -1 : 1;
}
if (o1.second.mTotalTime != o2.second.mTotalTime) {
return o1.second.mTotalTime > o2.second.mTotalTime ? -1 : 1;
}
if (o1.first.mUid != o2.first.mUid) {
return o1.first.mUid < o2.first.mUid ? -1 : 1;
}
if (o1.first.mProcess != o2.first.mProcess) {
int diff = o1.first.mProcess.compareTo(o2.first.mProcess);
if (diff != 0) {
return diff;
}
}
return 0;
};
public ArrayList<Pair<SourceKey, SourceDumpContainer>> createSortedAssociations(long now,
long totalTime) {
final int NSRC = mSources.size();
ArrayList<Pair<SourceKey, SourceDumpContainer>> sources = new ArrayList<>(NSRC);
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceState src = mSources.valueAt(isrc);
final SourceDumpContainer cont = new SourceDumpContainer(src);
long duration = src.mDuration;
if (src.mNesting > 0) {
duration += now - src.mStartUptime;
}
cont.mTotalTime = duration;
cont.mActiveTime = dumpTime(null, null, src, totalTime, now, false, false);
if (cont.mActiveTime < 0) {
cont.mActiveTime = -cont.mActiveTime;
}
sources.add(new Pair<>(mSources.keyAt(isrc), cont));
}
Collections.sort(sources, ASSOCIATION_COMPARATOR);
return sources;
}
public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
ArrayList<Pair<SourceKey, SourceDumpContainer>> sources, long now, long totalTime,
String reqPackage, boolean dumpDetails, boolean dumpAll) {
final String prefixInnerInner = prefixInner + " ";
long totalDuration = mTotalActiveDuration;
if (mTotalActiveNesting > 0) {
totalDuration += now - mTotalActiveStartUptime;
}
if (totalDuration > 0 || mTotalActiveCount != 0) {
pw.print(prefix);
pw.print("Active count ");
pw.print(mTotalActiveCount);
if (dumpAll) {
pw.print(": ");
TimeUtils.formatDuration(totalDuration, pw);
pw.print(" / ");
} else {
pw.print(": time ");
}
DumpUtils.printPercent(pw, (double) totalDuration / (double) totalTime);
pw.println();
}
if (dumpAll && mTotalActiveNesting != 0) {
pw.print(prefix);
pw.print("mTotalActiveNesting=");
pw.print(mTotalActiveNesting);
pw.print(" mTotalActiveStartUptime=");
TimeUtils.formatDuration(mTotalActiveStartUptime, now, pw);
pw.println();
}
totalDuration = mTotalDuration;
if (mTotalNesting > 0) {
totalDuration += now - mTotalStartUptime;
}
if (totalDuration > 0 || mTotalCount != 0) {
pw.print(prefix);
pw.print("Total count ");
pw.print(mTotalCount);
if (dumpAll) {
pw.print(": ");
TimeUtils.formatDuration(totalDuration, pw);
pw.print(" / ");
} else {
pw.print(": time ");
}
DumpUtils.printPercent(pw, (double) totalDuration / (double) totalTime);
pw.println();
}
if (dumpAll && mTotalNesting != 0) {
pw.print(prefix);
pw.print("mTotalNesting=");
pw.print(mTotalNesting);
pw.print(" mTotalStartUptime=");
TimeUtils.formatDuration(mTotalStartUptime, now, pw);
pw.println();
}
final int NSRC = sources.size();
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceKey key = sources.get(isrc).first;
final SourceDumpContainer cont = sources.get(isrc).second;
final SourceState src = cont.mState;
pw.print(prefix);
pw.print("<- ");
pw.print(key.mProcess);
pw.print("/");
UserHandle.formatUid(pw, key.mUid);
if (key.mPackage != null) {
pw.print(" (");
pw.print(key.mPackage);
pw.print(")");
}
// If we are skipping this one, we still print the first line just to give
// context for the others (so it is clear the total times for the overall
// association come from other sources whose times are not shown).
if (reqPackage != null && !reqPackage.equals(key.mProcess)
&& !reqPackage.equals(key.mPackage)) {
pw.println();
continue;
}
pw.println(":");
if (src.mActiveCount != 0 || src.mActiveDurations != null || src.mActiveDuration != 0
|| src.mActiveStartUptime != 0) {
pw.print(prefixInner);
pw.print(" Active count ");
pw.print(src.mActiveCount);
if (dumpDetails) {
if (dumpAll) {
if (src.mActiveDurations != null) {
pw.print(" (multi-state)");
} else if (src.mActiveProcState >= ProcessStats.STATE_PERSISTENT) {
pw.print(" (");
pw.print(DumpUtils.STATE_NAMES[src.mActiveProcState]);
pw.print(")");
} else {
pw.print(" (*UNKNOWN STATE*)");
}
}
if (dumpAll) {
pw.print(": ");
TimeUtils.formatDuration(cont.mActiveTime, pw);
pw.print(" / ");
} else {
pw.print(": time ");
}
DumpUtils.printPercent(pw, (double) cont.mActiveTime / (double) totalTime);
if (src.mActiveStartUptime != 0) {
pw.print(" (running)");
}
pw.println();
if (src.mActiveDurations != null) {
dumpTime(pw, prefixInnerInner, src, totalTime, now, dumpDetails, dumpAll);
}
} else {
pw.print(": ");
dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
}
}
pw.print(prefixInner);
pw.print(" Total count ");
pw.print(src.mCount);
if (dumpAll) {
pw.print(": ");
TimeUtils.formatDuration(cont.mTotalTime, pw);
pw.print(" / ");
} else {
pw.print(": time ");
}
DumpUtils.printPercent(pw, (double) cont.mTotalTime / (double) totalTime);
if (src.mNesting > 0) {
pw.print(" (running");
if (dumpAll) {
pw.print(" nest=");
pw.print(src.mNesting);
}
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 (dumpAll) {
if (src.mInTrackingList) {
pw.print(prefixInner);
pw.print(" mInTrackingList=");
pw.println(src.mInTrackingList);
}
if (src.mProcState != ProcessStats.STATE_NOTHING) {
pw.print(prefixInner);
pw.print(" mProcState=");
pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
pw.print(" mProcStateSeq=");
pw.println(src.mProcStateSeq);
}
}
}
}
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;
if (isRunning) {
duration = -duration;
}
if (dumpAll) {
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();
}
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++) {
long time;
if (src.mActiveDurations != null) {
time = src.mActiveDurations.getValueForId((byte) iprocstate);
} else {
time = src.mActiveProcState == iprocstate ? src.mActiveDuration : 0;
}
final String running;
if (src.mActiveStartUptime != 0 && src.mActiveProcState == iprocstate) {
running = " (running)";
isRunning = true;
time += now - src.mActiveStartUptime;
} else {
running = null;
}
if (time != 0) {
if (pw != null) {
pw.print(prefix);
pw.print(DumpUtils.STATE_LABELS[iprocstate]);
pw.print(": ");
if (dumpAll) {
TimeUtils.formatDuration(time, pw);
pw.print(" / ");
} else {
pw.print("time ");
}
DumpUtils.printPercent(pw, (double) time / (double) overallTime);
if (running != null) {
pw.print(running);
}
pw.println();
}
totalTime += time;
}
}
return isRunning ? -totalTime : totalTime;
}
public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers,
String associationName, long now) {
final int NSRC = mSources.size();
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceKey key = mSources.keyAt(isrc);
final SourceState src = mSources.valueAt(isrc);
pw.print("pkgasc");
pw.print(",");
pw.print(pkgName);
pw.print(",");
pw.print(uid);
pw.print(",");
pw.print(vers);
pw.print(",");
pw.print(associationName);
pw.print(",");
pw.print(key.mProcess);
pw.print(",");
pw.print(key.mUid);
pw.print(",");
pw.print(src.mCount);
long duration = src.mDuration;
if (src.mNesting > 0) {
duration += now - src.mStartUptime;
}
pw.print(",");
pw.print(duration);
pw.print(",");
pw.print(src.mActiveCount);
final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
if (src.mActiveDurations != null) {
final int N = src.mActiveDurations.getKeyCount();
for (int i=0; i<N; i++) {
final int dkey = src.mActiveDurations.getKeyAt(i);
duration = src.mActiveDurations.getValue(dkey);
if (dkey == src.mActiveProcState) {
duration += timeNow;
}
final int procState = SparseMappingTable.getIdFromKey(dkey);
pw.print(",");
DumpUtils.printArrayEntry(pw, DumpUtils.STATE_TAGS, procState, 1);
pw.print(':');
pw.print(duration);
}
} else {
duration = src.mActiveDuration + timeNow;
if (duration != 0) {
pw.print(",");
DumpUtils.printArrayEntry(pw, DumpUtils.STATE_TAGS, src.mActiveProcState, 1);
pw.print(':');
pw.print(duration);
}
}
pw.println();
}
}
public void dumpDebug(ProtoOutputStream proto, long fieldId, long now) {
final long token = proto.start(fieldId);
proto.write(PackageAssociationProcessStatsProto.COMPONENT_NAME, mName);
proto.write(PackageAssociationProcessStatsProto.TOTAL_COUNT, mTotalCount);
proto.write(PackageAssociationProcessStatsProto.TOTAL_DURATION_MS, getTotalDuration(now));
if (mTotalActiveCount != 0) {
proto.write(PackageAssociationProcessStatsProto.ACTIVE_COUNT, mTotalActiveCount);
proto.write(PackageAssociationProcessStatsProto.ACTIVE_DURATION_MS,
getActiveDuration(now));
}
final int NSRC = mSources.size();
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceKey key = mSources.keyAt(isrc);
final SourceState src = mSources.valueAt(isrc);
final long sourceToken = proto.start(PackageAssociationProcessStatsProto.SOURCES);
proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_NAME, key.mProcess);
proto.write(PackageAssociationSourceProcessStatsProto.PACKAGE_NAME, key.mPackage);
proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_UID, key.mUid);
proto.write(PackageAssociationSourceProcessStatsProto.TOTAL_COUNT, src.mCount);
long duration = src.mDuration;
if (src.mNesting > 0) {
duration += now - src.mStartUptime;
}
proto.write(PackageAssociationSourceProcessStatsProto.TOTAL_DURATION_MS, duration);
if (src.mActiveCount != 0) {
proto.write(PackageAssociationSourceProcessStatsProto.ACTIVE_COUNT,
src.mActiveCount);
}
final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
if (src.mActiveDurations != null) {
final int N = src.mActiveDurations.getKeyCount();
for (int i=0; i<N; i++) {
final int dkey = src.mActiveDurations.getKeyAt(i);
duration = src.mActiveDurations.getValue(dkey);
if (dkey == src.mActiveProcState) {
duration += timeNow;
}
final int procState = SparseMappingTable.getIdFromKey(dkey);
final long stateToken = proto.start(
PackageAssociationSourceProcessStatsProto.ACTIVE_STATE_STATS);
DumpUtils.printProto(proto,
PackageAssociationSourceProcessStatsProto.StateStats.PROCESS_STATE,
DumpUtils.STATE_PROTO_ENUMS, procState, 1);
proto.write(PackageAssociationSourceProcessStatsProto.StateStats.DURATION_MS,
duration);
proto.end(stateToken);
}
} else {
duration = src.mActiveDuration + timeNow;
if (duration != 0) {
final long stateToken = proto.start(
PackageAssociationSourceProcessStatsProto.ACTIVE_STATE_STATS);
DumpUtils.printProto(proto,
PackageAssociationSourceProcessStatsProto.StateStats.PROCESS_STATE,
DumpUtils.STATE_PROTO_ENUMS, src.mActiveProcState, 1);
proto.write(PackageAssociationSourceProcessStatsProto.StateStats.DURATION_MS,
duration);
proto.end(stateToken);
}
}
proto.end(sourceToken);
}
proto.end(token);
}
public String toString() {
return "AssociationState{" + Integer.toHexString(System.identityHashCode(this))
+ " " + mName + " pkg=" + mPackageState.mPackageName + " proc="
+ Integer.toHexString(System.identityHashCode(mProc)) + "}";
}
}