ProcessStats is now called ProcessCpuTracker. ProcessTracker is now ProcessStatsService, and its inner State class is broken out into a separate top-level ProcessStats class. This ProcessStats is moved to the framework, so we will be able to use it elsewhere. Change-Id: I6a127bcb835b6b474b72647c0b99b82c2137e5c5
2438 lines
99 KiB
Java
2438 lines
99 KiB
Java
/*
|
|
* Copyright (C) 2013 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;
|
|
|
|
import android.os.Parcel;
|
|
import android.os.SystemClock;
|
|
import android.os.SystemProperties;
|
|
import android.os.UserHandle;
|
|
import android.text.format.DateFormat;
|
|
import android.util.ArrayMap;
|
|
import android.util.ArraySet;
|
|
import android.util.Slog;
|
|
import android.util.SparseArray;
|
|
import android.util.TimeUtils;
|
|
import android.webkit.WebViewFactory;
|
|
import com.android.internal.util.ArrayUtils;
|
|
import com.android.server.ProcessMap;
|
|
import dalvik.system.VMRuntime;
|
|
|
|
import java.io.PrintWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.Objects;
|
|
|
|
final public class ProcessStats {
|
|
static final String TAG = "ProcessStats";
|
|
static final boolean DEBUG = false;
|
|
|
|
public static final int STATE_NOTHING = -1;
|
|
public static final int STATE_PERSISTENT = 0;
|
|
public static final int STATE_TOP = 1;
|
|
public static final int STATE_IMPORTANT_FOREGROUND = 2;
|
|
public static final int STATE_IMPORTANT_BACKGROUND = 3;
|
|
public static final int STATE_BACKUP = 4;
|
|
public static final int STATE_HEAVY_WEIGHT = 5;
|
|
public static final int STATE_SERVICE = 6;
|
|
public static final int STATE_SERVICE_RESTARTING = 7;
|
|
public static final int STATE_RECEIVER = 8;
|
|
public static final int STATE_HOME = 9;
|
|
public static final int STATE_LAST_ACTIVITY = 10;
|
|
public static final int STATE_CACHED_ACTIVITY = 11;
|
|
public static final int STATE_CACHED_ACTIVITY_CLIENT = 12;
|
|
public static final int STATE_CACHED_EMPTY = 13;
|
|
public static final int STATE_COUNT = STATE_CACHED_EMPTY+1;
|
|
|
|
public static final int PSS_SAMPLE_COUNT = 0;
|
|
public static final int PSS_MINIMUM = 1;
|
|
public static final int PSS_AVERAGE = 2;
|
|
public static final int PSS_MAXIMUM = 3;
|
|
public static final int PSS_USS_MINIMUM = 4;
|
|
public static final int PSS_USS_AVERAGE = 5;
|
|
public static final int PSS_USS_MAXIMUM = 6;
|
|
public static final int PSS_COUNT = PSS_USS_MAXIMUM+1;
|
|
|
|
public static final int ADJ_NOTHING = -1;
|
|
public static final int ADJ_MEM_FACTOR_NORMAL = 0;
|
|
public static final int ADJ_MEM_FACTOR_MODERATE = 1;
|
|
public static final int ADJ_MEM_FACTOR_LOW = 2;
|
|
public static final int ADJ_MEM_FACTOR_CRITICAL = 3;
|
|
public static final int ADJ_MEM_FACTOR_COUNT = ADJ_MEM_FACTOR_CRITICAL+1;
|
|
public static final int ADJ_SCREEN_MOD = ADJ_MEM_FACTOR_COUNT;
|
|
public static final int ADJ_SCREEN_OFF = 0;
|
|
public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD;
|
|
public static final int ADJ_COUNT = ADJ_SCREEN_ON*2;
|
|
|
|
public static final int FLAG_COMPLETE = 1<<0;
|
|
public static final int FLAG_SHUTDOWN = 1<<1;
|
|
public static final int FLAG_SYSPROPS = 1<<2;
|
|
|
|
static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, ADJ_MEM_FACTOR_MODERATE,
|
|
ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL };
|
|
static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON };
|
|
static final int[] NON_CACHED_PROC_STATES = new int[] { STATE_PERSISTENT,
|
|
STATE_TOP, STATE_IMPORTANT_FOREGROUND,
|
|
STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT,
|
|
STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, STATE_HOME
|
|
};
|
|
|
|
// Map from process states to the states we track.
|
|
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
|
|
STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
|
|
STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
|
|
STATE_BACKUP, // ActivityManager.PROCESS_STATE_BACKUP
|
|
STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
|
|
STATE_SERVICE, // ActivityManager.PROCESS_STATE_SERVICE
|
|
STATE_RECEIVER, // ActivityManager.PROCESS_STATE_RECEIVER
|
|
STATE_HOME, // ActivityManager.PROCESS_STATE_HOME
|
|
STATE_LAST_ACTIVITY, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
|
|
STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
|
|
STATE_CACHED_ACTIVITY_CLIENT, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
|
|
STATE_CACHED_EMPTY, // ActivityManager.PROCESS_STATE_CACHED_EMPTY
|
|
};
|
|
|
|
public static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT,
|
|
STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP,
|
|
STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER,
|
|
STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY,
|
|
STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY
|
|
};
|
|
|
|
static final String[] STATE_NAMES = new String[] {
|
|
"Persistent", "Top ", "Imp Fg ", "Imp Bg ",
|
|
"Backup ", "Heavy Wght", "Service ", "Service Rs",
|
|
"Receiver ", "Home ",
|
|
"Last Act ", "Cch Act ", "Cch CliAct", "Cch Empty "
|
|
};
|
|
|
|
public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
|
|
"off", "on"
|
|
};
|
|
|
|
public static final String[] ADJ_MEM_NAMES_CSV = new String[] {
|
|
"norm", "mod", "low", "crit"
|
|
};
|
|
|
|
public static final String[] STATE_NAMES_CSV = new String[] {
|
|
"pers", "top", "impfg", "impbg", "backup", "heavy",
|
|
"service", "service-rs", "receiver", "home", "lastact",
|
|
"cch-activity", "cch-aclient", "cch-empty"
|
|
};
|
|
|
|
static final String[] ADJ_SCREEN_TAGS = new String[] {
|
|
"0", "1"
|
|
};
|
|
|
|
static final String[] ADJ_MEM_TAGS = new String[] {
|
|
"n", "m", "l", "c"
|
|
};
|
|
|
|
static final String[] STATE_TAGS = new String[] {
|
|
"p", "t", "f", "b", "u", "w",
|
|
"s", "x", "r", "h", "l", "a", "c", "e"
|
|
};
|
|
|
|
static final String CSV_SEP = "\t";
|
|
|
|
// Current version of the parcel format.
|
|
private static final int PARCEL_VERSION = 9;
|
|
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
|
|
private static final int MAGIC = 0x50535453;
|
|
|
|
// Where the "type"/"state" part of the data appears in an offset integer.
|
|
static int OFFSET_TYPE_SHIFT = 0;
|
|
static int OFFSET_TYPE_MASK = 0xff;
|
|
// Where the "which array" part of the data appears in an offset integer.
|
|
static int OFFSET_ARRAY_SHIFT = 8;
|
|
static int OFFSET_ARRAY_MASK = 0xff;
|
|
// Where the "index into array" part of the data appears in an offset integer.
|
|
static int OFFSET_INDEX_SHIFT = 16;
|
|
static int OFFSET_INDEX_MASK = 0xffff;
|
|
|
|
public String mReadError;
|
|
public String mTimePeriodStartClockStr;
|
|
public int mFlags;
|
|
|
|
public final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>();
|
|
public final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
|
|
|
|
public final long[] mMemFactorDurations = new long[ADJ_COUNT];
|
|
public int mMemFactor = STATE_NOTHING;
|
|
public long mStartTime;
|
|
|
|
public long mTimePeriodStartClock;
|
|
public long mTimePeriodStartRealtime;
|
|
public long mTimePeriodEndRealtime;
|
|
String mRuntime;
|
|
String mWebView;
|
|
boolean mRunning;
|
|
|
|
static final int LONGS_SIZE = 4096;
|
|
final ArrayList<long[]> mLongs = new ArrayList<long[]>();
|
|
int mNextLong;
|
|
|
|
int[] mAddLongTable;
|
|
int mAddLongTableSize;
|
|
|
|
public ProcessStats(boolean running) {
|
|
mRunning = running;
|
|
reset();
|
|
}
|
|
|
|
static private void printScreenLabel(PrintWriter pw, int offset) {
|
|
switch (offset) {
|
|
case ADJ_NOTHING:
|
|
pw.print(" ");
|
|
break;
|
|
case ADJ_SCREEN_OFF:
|
|
pw.print("Screen Off / ");
|
|
break;
|
|
case ADJ_SCREEN_ON:
|
|
pw.print("Screen On / ");
|
|
break;
|
|
default:
|
|
pw.print("?????????? / ");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static public void printScreenLabelCsv(PrintWriter pw, int offset) {
|
|
switch (offset) {
|
|
case ADJ_NOTHING:
|
|
break;
|
|
case ADJ_SCREEN_OFF:
|
|
pw.print(ADJ_SCREEN_NAMES_CSV[0]);
|
|
break;
|
|
case ADJ_SCREEN_ON:
|
|
pw.print(ADJ_SCREEN_NAMES_CSV[1]);
|
|
break;
|
|
default:
|
|
pw.print("???");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static private void printMemLabel(PrintWriter pw, int offset) {
|
|
switch (offset) {
|
|
case ADJ_NOTHING:
|
|
pw.print(" ");
|
|
break;
|
|
case ADJ_MEM_FACTOR_NORMAL:
|
|
pw.print("Norm / ");
|
|
break;
|
|
case ADJ_MEM_FACTOR_MODERATE:
|
|
pw.print("Mod / ");
|
|
break;
|
|
case ADJ_MEM_FACTOR_LOW:
|
|
pw.print("Low / ");
|
|
break;
|
|
case ADJ_MEM_FACTOR_CRITICAL:
|
|
pw.print("Crit / ");
|
|
break;
|
|
default:
|
|
pw.print("???? / ");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static public void printMemLabelCsv(PrintWriter pw, int offset) {
|
|
if (offset >= ADJ_MEM_FACTOR_NORMAL) {
|
|
if (offset <= ADJ_MEM_FACTOR_CRITICAL) {
|
|
pw.print(ADJ_MEM_NAMES_CSV[offset]);
|
|
} else {
|
|
pw.print("???");
|
|
}
|
|
}
|
|
}
|
|
|
|
static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations,
|
|
int curState, long curStartTime, long now) {
|
|
long totalTime = 0;
|
|
int printedScreen = -1;
|
|
for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
|
|
int printedMem = -1;
|
|
for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
|
|
int state = imem+iscreen;
|
|
long time = durations[state];
|
|
String running = "";
|
|
if (curState == state) {
|
|
time += now - curStartTime;
|
|
if (pw != null) {
|
|
running = " (running)";
|
|
}
|
|
}
|
|
if (time != 0) {
|
|
if (pw != null) {
|
|
pw.print(prefix);
|
|
printScreenLabel(pw, printedScreen != iscreen
|
|
? iscreen : STATE_NOTHING);
|
|
printedScreen = iscreen;
|
|
printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
|
|
printedMem = imem;
|
|
TimeUtils.formatDuration(time, pw); pw.println(running);
|
|
}
|
|
totalTime += time;
|
|
}
|
|
}
|
|
}
|
|
if (totalTime != 0 && pw != null) {
|
|
pw.print(prefix);
|
|
printScreenLabel(pw, STATE_NOTHING);
|
|
pw.print("TOTAL: ");
|
|
TimeUtils.formatDuration(totalTime, pw);
|
|
pw.println();
|
|
}
|
|
return totalTime;
|
|
}
|
|
|
|
static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations,
|
|
int curState, long curStartTime, long now) {
|
|
for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
|
|
for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
|
|
int state = imem+iscreen;
|
|
long time = durations[state];
|
|
if (curState == state) {
|
|
time += now - curStartTime;
|
|
}
|
|
if (time != 0) {
|
|
printAdjTagAndValue(pw, state, time);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dumpServiceTimeCheckin(PrintWriter pw, String label, String packageName,
|
|
int uid, String serviceName, ServiceState svc, int serviceType, int opCount,
|
|
int curState, long curStartTime, long now) {
|
|
if (opCount <= 0) {
|
|
return;
|
|
}
|
|
pw.print(label);
|
|
pw.print(",");
|
|
pw.print(packageName);
|
|
pw.print(",");
|
|
pw.print(uid);
|
|
pw.print(",");
|
|
pw.print(serviceName);
|
|
pw.print(",");
|
|
pw.print(opCount);
|
|
boolean didCurState = false;
|
|
for (int i=0; i<svc.mDurationsTableSize; i++) {
|
|
int off = svc.mDurationsTable[i];
|
|
int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
|
|
int memFactor = type / ServiceState.SERVICE_COUNT;
|
|
type %= ServiceState.SERVICE_COUNT;
|
|
if (type != serviceType) {
|
|
continue;
|
|
}
|
|
long time = svc.mProcessStats.getLong(off, 0);
|
|
if (curState == memFactor) {
|
|
didCurState = true;
|
|
time += now - curStartTime;
|
|
}
|
|
printAdjTagAndValue(pw, memFactor, time);
|
|
}
|
|
if (!didCurState && curState != STATE_NOTHING) {
|
|
printAdjTagAndValue(pw, curState, now - curStartTime);
|
|
}
|
|
pw.println();
|
|
}
|
|
|
|
static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) {
|
|
data.totalTime = 0;
|
|
data.numPss = data.minPss = data.avgPss = data.maxPss =
|
|
data.minUss = data.avgUss = data.maxUss = 0;
|
|
for (int is=0; is<data.screenStates.length; is++) {
|
|
for (int im=0; im<data.memStates.length; im++) {
|
|
for (int ip=0; ip<data.procStates.length; ip++) {
|
|
int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT)
|
|
+ data.procStates[ip];
|
|
data.totalTime += proc.getDuration(bucket, now);
|
|
long samples = proc.getPssSampleCount(bucket);
|
|
if (samples > 0) {
|
|
long minPss = proc.getPssMinimum(bucket);
|
|
long avgPss = proc.getPssAverage(bucket);
|
|
long maxPss = proc.getPssMaximum(bucket);
|
|
long minUss = proc.getPssUssMinimum(bucket);
|
|
long avgUss = proc.getPssUssAverage(bucket);
|
|
long maxUss = proc.getPssUssMaximum(bucket);
|
|
if (data.numPss == 0) {
|
|
data.minPss = minPss;
|
|
data.avgPss = avgPss;
|
|
data.maxPss = maxPss;
|
|
data.minUss = minUss;
|
|
data.avgUss = avgUss;
|
|
data.maxUss = maxUss;
|
|
} else {
|
|
if (minPss < data.minPss) {
|
|
data.minPss = minPss;
|
|
}
|
|
data.avgPss = (long)( ((data.avgPss*(double)data.numPss)
|
|
+ (avgPss*(double)samples)) / (data.numPss+samples) );
|
|
if (maxPss > data.maxPss) {
|
|
data.maxPss = maxPss;
|
|
}
|
|
if (minUss < data.minUss) {
|
|
data.minUss = minUss;
|
|
}
|
|
data.avgUss = (long)( ((data.avgUss*(double)data.numPss)
|
|
+ (avgUss*(double)samples)) / (data.numPss+samples) );
|
|
if (maxUss > data.maxUss) {
|
|
data.maxUss = maxUss;
|
|
}
|
|
}
|
|
data.numPss += samples;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates,
|
|
int[] procStates, long now) {
|
|
long totalTime = 0;
|
|
/*
|
|
for (int i=0; i<proc.mDurationsTableSize; i++) {
|
|
int val = proc.mDurationsTable[i];
|
|
totalTime += proc.mState.getLong(val, 0);
|
|
if ((val&0xff) == proc.mCurState) {
|
|
totalTime += now - proc.mStartTime;
|
|
}
|
|
}
|
|
*/
|
|
for (int is=0; is<screenStates.length; is++) {
|
|
for (int im=0; im<memStates.length; im++) {
|
|
for (int ip=0; ip<procStates.length; ip++) {
|
|
int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT)
|
|
+ procStates[ip];
|
|
totalTime += proc.getDuration(bucket, now);
|
|
}
|
|
}
|
|
}
|
|
proc.mTmpTotalTime = totalTime;
|
|
return totalTime;
|
|
}
|
|
|
|
static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc,
|
|
int[] screenStates, int[] memStates, int[] procStates, long now) {
|
|
long totalTime = 0;
|
|
int printedScreen = -1;
|
|
for (int is=0; is<screenStates.length; is++) {
|
|
int printedMem = -1;
|
|
for (int im=0; im<memStates.length; im++) {
|
|
for (int ip=0; ip<procStates.length; ip++) {
|
|
final int iscreen = screenStates[is];
|
|
final int imem = memStates[im];
|
|
final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
|
|
long time = proc.getDuration(bucket, now);
|
|
String running = "";
|
|
if (proc.mCurState == bucket) {
|
|
running = " (running)";
|
|
}
|
|
if (time != 0) {
|
|
pw.print(prefix);
|
|
if (screenStates.length > 1) {
|
|
printScreenLabel(pw, printedScreen != iscreen
|
|
? iscreen : STATE_NOTHING);
|
|
printedScreen = iscreen;
|
|
}
|
|
if (memStates.length > 1) {
|
|
printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
|
|
printedMem = imem;
|
|
}
|
|
pw.print(STATE_NAMES[procStates[ip]]); pw.print(": ");
|
|
TimeUtils.formatDuration(time, pw); pw.println(running);
|
|
totalTime += time;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (totalTime != 0) {
|
|
pw.print(prefix);
|
|
if (screenStates.length > 1) {
|
|
printScreenLabel(pw, STATE_NOTHING);
|
|
}
|
|
if (memStates.length > 1) {
|
|
printMemLabel(pw, STATE_NOTHING);
|
|
}
|
|
pw.print("TOTAL : ");
|
|
TimeUtils.formatDuration(totalTime, pw);
|
|
pw.println();
|
|
}
|
|
}
|
|
|
|
static void dumpProcessPss(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates,
|
|
int[] memStates, int[] procStates) {
|
|
boolean printedHeader = false;
|
|
int printedScreen = -1;
|
|
for (int is=0; is<screenStates.length; is++) {
|
|
int printedMem = -1;
|
|
for (int im=0; im<memStates.length; im++) {
|
|
for (int ip=0; ip<procStates.length; ip++) {
|
|
final int iscreen = screenStates[is];
|
|
final int imem = memStates[im];
|
|
final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
|
|
long count = proc.getPssSampleCount(bucket);
|
|
if (count > 0) {
|
|
if (!printedHeader) {
|
|
pw.print(prefix);
|
|
pw.print("PSS/USS (");
|
|
pw.print(proc.mPssTableSize);
|
|
pw.println(" entries):");
|
|
printedHeader = true;
|
|
}
|
|
pw.print(prefix);
|
|
pw.print(" ");
|
|
if (screenStates.length > 1) {
|
|
printScreenLabel(pw, printedScreen != iscreen
|
|
? iscreen : STATE_NOTHING);
|
|
printedScreen = iscreen;
|
|
}
|
|
if (memStates.length > 1) {
|
|
printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
|
|
printedMem = imem;
|
|
}
|
|
pw.print(STATE_NAMES[procStates[ip]]); pw.print(": ");
|
|
pw.print(count);
|
|
pw.print(" samples ");
|
|
printSizeValue(pw, proc.getPssMinimum(bucket) * 1024);
|
|
pw.print(" ");
|
|
printSizeValue(pw, proc.getPssAverage(bucket) * 1024);
|
|
pw.print(" ");
|
|
printSizeValue(pw, proc.getPssMaximum(bucket) * 1024);
|
|
pw.print(" / ");
|
|
printSizeValue(pw, proc.getPssUssMinimum(bucket) * 1024);
|
|
pw.print(" ");
|
|
printSizeValue(pw, proc.getPssUssAverage(bucket) * 1024);
|
|
pw.print(" ");
|
|
printSizeValue(pw, proc.getPssUssMaximum(bucket) * 1024);
|
|
pw.println();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (proc.mNumExcessiveWake != 0) {
|
|
pw.print(prefix); pw.print("Killed for excessive wake locks: ");
|
|
pw.print(proc.mNumExcessiveWake); pw.println(" times");
|
|
}
|
|
if (proc.mNumExcessiveCpu != 0) {
|
|
pw.print(prefix); pw.print("Killed for excessive CPU use: ");
|
|
pw.print(proc.mNumExcessiveCpu); pw.println(" times");
|
|
}
|
|
}
|
|
|
|
static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
|
|
int[] memStates, int[] procStates) {
|
|
final int NS = screenStates != null ? screenStates.length : 1;
|
|
final int NM = memStates != null ? memStates.length : 1;
|
|
final int NP = procStates != null ? procStates.length : 1;
|
|
for (int is=0; is<NS; is++) {
|
|
for (int im=0; im<NM; im++) {
|
|
for (int ip=0; ip<NP; ip++) {
|
|
pw.print(sep);
|
|
boolean printed = false;
|
|
if (screenStates != null && screenStates.length > 1) {
|
|
printScreenLabelCsv(pw, screenStates[is]);
|
|
printed = true;
|
|
}
|
|
if (memStates != null && memStates.length > 1) {
|
|
if (printed) {
|
|
pw.print("-");
|
|
}
|
|
printMemLabelCsv(pw, memStates[im]);
|
|
printed = true;
|
|
}
|
|
if (procStates != null && procStates.length > 1) {
|
|
if (printed) {
|
|
pw.print("-");
|
|
}
|
|
pw.print(STATE_NAMES_CSV[procStates[ip]]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dumpProcessStateCsv(PrintWriter pw, ProcessState proc,
|
|
boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
|
|
boolean sepProcStates, int[] procStates, long now) {
|
|
final int NSS = sepScreenStates ? screenStates.length : 1;
|
|
final int NMS = sepMemStates ? memStates.length : 1;
|
|
final int NPS = sepProcStates ? procStates.length : 1;
|
|
for (int iss=0; iss<NSS; iss++) {
|
|
for (int ims=0; ims<NMS; ims++) {
|
|
for (int ips=0; ips<NPS; ips++) {
|
|
final int vsscreen = sepScreenStates ? screenStates[iss] : 0;
|
|
final int vsmem = sepMemStates ? memStates[ims] : 0;
|
|
final int vsproc = sepProcStates ? procStates[ips] : 0;
|
|
final int NSA = sepScreenStates ? 1 : screenStates.length;
|
|
final int NMA = sepMemStates ? 1 : memStates.length;
|
|
final int NPA = sepProcStates ? 1 : procStates.length;
|
|
long totalTime = 0;
|
|
for (int isa=0; isa<NSA; isa++) {
|
|
for (int ima=0; ima<NMA; ima++) {
|
|
for (int ipa=0; ipa<NPA; ipa++) {
|
|
final int vascreen = sepScreenStates ? 0 : screenStates[isa];
|
|
final int vamem = sepMemStates ? 0 : memStates[ima];
|
|
final int vaproc = sepProcStates ? 0 : procStates[ipa];
|
|
final int bucket = ((vsscreen + vascreen + vsmem + vamem)
|
|
* STATE_COUNT) + vsproc + vaproc;
|
|
totalTime += proc.getDuration(bucket, now);
|
|
}
|
|
}
|
|
}
|
|
pw.print(CSV_SEP);
|
|
pw.print(totalTime);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs,
|
|
int[] screenStates, int[] memStates, int[] procStates, long now) {
|
|
String innerPrefix = prefix + " ";
|
|
for (int i=procs.size()-1; i>=0; i--) {
|
|
ProcessState proc = procs.get(i);
|
|
pw.print(prefix);
|
|
pw.print(proc.mName);
|
|
pw.print(" / ");
|
|
UserHandle.formatUid(pw, proc.mUid);
|
|
pw.print(" (");
|
|
pw.print(proc.mDurationsTableSize);
|
|
pw.print(" entries)");
|
|
pw.println(":");
|
|
dumpProcessState(pw, innerPrefix, proc, screenStates, memStates, procStates, now);
|
|
if (proc.mPssTableSize > 0) {
|
|
dumpProcessPss(pw, innerPrefix, proc, screenStates, memStates, procStates);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dumpProcessSummaryDetails(PrintWriter pw, ProcessState proc, String prefix,
|
|
String label, int[] screenStates, int[] memStates, int[] procStates,
|
|
long now, long totalTime, boolean full) {
|
|
ProcessDataCollection totals = new ProcessDataCollection(screenStates,
|
|
memStates, procStates);
|
|
computeProcessData(proc, totals, now);
|
|
if (totals.totalTime != 0 || totals.numPss != 0) {
|
|
if (prefix != null) {
|
|
pw.print(prefix);
|
|
}
|
|
if (label != null) {
|
|
pw.print(label);
|
|
}
|
|
totals.print(pw, totalTime, full);
|
|
if (prefix != null) {
|
|
pw.println();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dumpProcessSummaryLocked(PrintWriter pw, String prefix,
|
|
ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates,
|
|
long now, long totalTime) {
|
|
for (int i=procs.size()-1; i>=0; i--) {
|
|
ProcessState proc = procs.get(i);
|
|
pw.print(prefix);
|
|
pw.print("* ");
|
|
pw.print(proc.mName);
|
|
pw.print(" / ");
|
|
UserHandle.formatUid(pw, proc.mUid);
|
|
pw.println(":");
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " TOTAL: ", screenStates, memStates,
|
|
procStates, now, totalTime, true);
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " Persistent: ", screenStates, memStates,
|
|
new int[] { STATE_PERSISTENT }, now, totalTime, true);
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " Top: ", screenStates, memStates,
|
|
new int[] {STATE_TOP}, now, totalTime, true);
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " Imp Fg: ", screenStates, memStates,
|
|
new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true);
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " Imp Bg: ", screenStates, memStates,
|
|
new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true);
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " Backup: ", screenStates, memStates,
|
|
new int[] {STATE_BACKUP}, now, totalTime, true);
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " Heavy Wgt: ", screenStates, memStates,
|
|
new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true);
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " Service: ", screenStates, memStates,
|
|
new int[] {STATE_SERVICE}, now, totalTime, true);
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " Service Rs: ", screenStates, memStates,
|
|
new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true);
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " Receiver: ", screenStates, memStates,
|
|
new int[] {STATE_RECEIVER}, now, totalTime, true);
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " Home: ", screenStates, memStates,
|
|
new int[] {STATE_HOME}, now, totalTime, true);
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " (Last Act): ", screenStates, memStates,
|
|
new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
|
|
dumpProcessSummaryDetails(pw, proc, prefix, " (Cached): ", screenStates, memStates,
|
|
new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT,
|
|
STATE_CACHED_EMPTY}, now, totalTime, true);
|
|
}
|
|
}
|
|
|
|
static void printPercent(PrintWriter pw, double fraction) {
|
|
fraction *= 100;
|
|
if (fraction < 1) {
|
|
pw.print(String.format("%.2f", fraction));
|
|
} else if (fraction < 10) {
|
|
pw.print(String.format("%.1f", fraction));
|
|
} else {
|
|
pw.print(String.format("%.0f", fraction));
|
|
}
|
|
pw.print("%");
|
|
}
|
|
|
|
static void printSizeValue(PrintWriter pw, long number) {
|
|
float result = number;
|
|
String suffix = "";
|
|
if (result > 900) {
|
|
suffix = "KB";
|
|
result = result / 1024;
|
|
}
|
|
if (result > 900) {
|
|
suffix = "MB";
|
|
result = result / 1024;
|
|
}
|
|
if (result > 900) {
|
|
suffix = "GB";
|
|
result = result / 1024;
|
|
}
|
|
if (result > 900) {
|
|
suffix = "TB";
|
|
result = result / 1024;
|
|
}
|
|
if (result > 900) {
|
|
suffix = "PB";
|
|
result = result / 1024;
|
|
}
|
|
String value;
|
|
if (result < 1) {
|
|
value = String.format("%.2f", result);
|
|
} else if (result < 10) {
|
|
value = String.format("%.1f", result);
|
|
} else if (result < 100) {
|
|
value = String.format("%.0f", result);
|
|
} else {
|
|
value = String.format("%.0f", result);
|
|
}
|
|
pw.print(value);
|
|
pw.print(suffix);
|
|
}
|
|
|
|
public static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs,
|
|
boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
|
|
boolean sepProcStates, int[] procStates, long now) {
|
|
pw.print("process");
|
|
pw.print(CSV_SEP);
|
|
pw.print("uid");
|
|
dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null,
|
|
sepMemStates ? memStates : null,
|
|
sepProcStates ? procStates : null);
|
|
pw.println();
|
|
for (int i=procs.size()-1; i>=0; i--) {
|
|
ProcessState proc = procs.get(i);
|
|
pw.print(proc.mName);
|
|
pw.print(CSV_SEP);
|
|
UserHandle.formatUid(pw, proc.mUid);
|
|
dumpProcessStateCsv(pw, proc, sepScreenStates, screenStates,
|
|
sepMemStates, memStates, sepProcStates, procStates, now);
|
|
pw.println();
|
|
}
|
|
}
|
|
|
|
static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) {
|
|
int index = value/mod;
|
|
if (index >= 0 && index < array.length) {
|
|
pw.print(array[index]);
|
|
} else {
|
|
pw.print('?');
|
|
}
|
|
return value - index*mod;
|
|
}
|
|
|
|
static void printProcStateTag(PrintWriter pw, int state) {
|
|
state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD*STATE_COUNT);
|
|
state = printArrayEntry(pw, ADJ_MEM_TAGS, state, STATE_COUNT);
|
|
printArrayEntry(pw, STATE_TAGS, state, 1);
|
|
}
|
|
|
|
static void printAdjTag(PrintWriter pw, int state) {
|
|
state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD);
|
|
printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
|
|
}
|
|
|
|
static void printProcStateTagAndValue(PrintWriter pw, int state, long value) {
|
|
pw.print(',');
|
|
printProcStateTag(pw, state);
|
|
pw.print(':');
|
|
pw.print(value);
|
|
}
|
|
|
|
static void printAdjTagAndValue(PrintWriter pw, int state, long value) {
|
|
pw.print(',');
|
|
printAdjTag(pw, state);
|
|
pw.print(':');
|
|
pw.print(value);
|
|
}
|
|
|
|
static void dumpAllProcessStateCheckin(PrintWriter pw, ProcessState proc, long now) {
|
|
boolean didCurState = false;
|
|
for (int i=0; i<proc.mDurationsTableSize; i++) {
|
|
int off = proc.mDurationsTable[i];
|
|
int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
|
|
long time = proc.mProcessStats.getLong(off, 0);
|
|
if (proc.mCurState == type) {
|
|
didCurState = true;
|
|
time += now - proc.mStartTime;
|
|
}
|
|
printProcStateTagAndValue(pw, type, time);
|
|
}
|
|
if (!didCurState && proc.mCurState != STATE_NOTHING) {
|
|
printProcStateTagAndValue(pw, proc.mCurState, now - proc.mStartTime);
|
|
}
|
|
}
|
|
|
|
static void dumpAllProcessPssCheckin(PrintWriter pw, ProcessState proc) {
|
|
for (int i=0; i<proc.mPssTableSize; i++) {
|
|
int off = proc.mPssTable[i];
|
|
int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
|
|
long count = proc.mProcessStats.getLong(off, PSS_SAMPLE_COUNT);
|
|
long min = proc.mProcessStats.getLong(off, PSS_MINIMUM);
|
|
long avg = proc.mProcessStats.getLong(off, PSS_AVERAGE);
|
|
long max = proc.mProcessStats.getLong(off, PSS_MAXIMUM);
|
|
long umin = proc.mProcessStats.getLong(off, PSS_USS_MINIMUM);
|
|
long uavg = proc.mProcessStats.getLong(off, PSS_USS_AVERAGE);
|
|
long umax = proc.mProcessStats.getLong(off, PSS_USS_MAXIMUM);
|
|
pw.print(',');
|
|
printProcStateTag(pw, type);
|
|
pw.print(':');
|
|
pw.print(count);
|
|
pw.print(':');
|
|
pw.print(min);
|
|
pw.print(':');
|
|
pw.print(avg);
|
|
pw.print(':');
|
|
pw.print(max);
|
|
pw.print(':');
|
|
pw.print(umin);
|
|
pw.print(':');
|
|
pw.print(uavg);
|
|
pw.print(':');
|
|
pw.print(umax);
|
|
}
|
|
}
|
|
|
|
public void reset() {
|
|
if (DEBUG) Slog.d(TAG, "Resetting state of " + mTimePeriodStartClockStr);
|
|
resetCommon();
|
|
mPackages.getMap().clear();
|
|
mProcesses.getMap().clear();
|
|
mMemFactor = STATE_NOTHING;
|
|
mStartTime = 0;
|
|
if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
|
|
}
|
|
|
|
public void resetSafely() {
|
|
if (DEBUG) Slog.d(TAG, "Safely resetting state of " + mTimePeriodStartClockStr);
|
|
resetCommon();
|
|
long now = SystemClock.uptimeMillis();
|
|
ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
|
|
for (int ip=procMap.size()-1; ip>=0; ip--) {
|
|
SparseArray<ProcessState> uids = procMap.valueAt(ip);
|
|
for (int iu=uids.size()-1; iu>=0; iu--) {
|
|
uids.valueAt(iu).resetSafely(now);
|
|
}
|
|
}
|
|
ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
|
|
for (int ip=pkgMap.size()-1; ip>=0; ip--) {
|
|
SparseArray<PackageState> uids = pkgMap.valueAt(ip);
|
|
for (int iu=uids.size()-1; iu>=0; iu--) {
|
|
PackageState pkgState = uids.valueAt(iu);
|
|
for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) {
|
|
pkgState.mProcesses.valueAt(iproc).resetSafely(now);
|
|
}
|
|
for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) {
|
|
ServiceState ss = pkgState.mServices.valueAt(isvc);
|
|
if (ss.isActive()) {
|
|
pkgState.mServices.valueAt(isvc).resetSafely(now);
|
|
} else {
|
|
pkgState.mServices.removeAt(isvc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
mStartTime = SystemClock.uptimeMillis();
|
|
if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
|
|
}
|
|
|
|
private void resetCommon() {
|
|
mTimePeriodStartClock = System.currentTimeMillis();
|
|
buildTimePeriodStartClockStr();
|
|
mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
|
|
mLongs.clear();
|
|
mLongs.add(new long[LONGS_SIZE]);
|
|
mNextLong = 0;
|
|
Arrays.fill(mMemFactorDurations, 0);
|
|
mStartTime = 0;
|
|
mReadError = null;
|
|
mFlags = 0;
|
|
evaluateSystemProperties(true);
|
|
}
|
|
|
|
public boolean evaluateSystemProperties(boolean update) {
|
|
boolean changed = false;
|
|
String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib",
|
|
VMRuntime.getRuntime().vmLibrary());
|
|
if (!Objects.equals(runtime, mRuntime)) {
|
|
changed = true;
|
|
if (update) {
|
|
mRuntime = runtime;
|
|
}
|
|
}
|
|
String webview = WebViewFactory.useExperimentalWebView() ? "chromeview" : "webview";
|
|
if (!Objects.equals(webview, mWebView)) {
|
|
changed = true;
|
|
if (update) {
|
|
mWebView = webview;
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
private void buildTimePeriodStartClockStr() {
|
|
mTimePeriodStartClockStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss",
|
|
mTimePeriodStartClock).toString();
|
|
}
|
|
|
|
static final int[] BAD_TABLE = new int[0];
|
|
|
|
private int[] readTableFromParcel(Parcel in, String name, String what) {
|
|
final int size = in.readInt();
|
|
if (size < 0) {
|
|
Slog.w(TAG, "Ignoring existing stats; bad " + what + " table size: " + size);
|
|
return BAD_TABLE;
|
|
}
|
|
if (size == 0) {
|
|
return null;
|
|
}
|
|
final int[] table = new int[size];
|
|
for (int i=0; i<size; i++) {
|
|
table[i] = in.readInt();
|
|
if (DEBUG) Slog.i(TAG, "Reading in " + name + " table #" + i + ": "
|
|
+ ProcessStats.printLongOffset(table[i]));
|
|
if (!validateLongOffset(table[i])) {
|
|
Slog.w(TAG, "Ignoring existing stats; bad " + what + " table entry: "
|
|
+ ProcessStats.printLongOffset(table[i]));
|
|
return null;
|
|
}
|
|
}
|
|
return table;
|
|
}
|
|
|
|
public void writeToParcel(Parcel out) {
|
|
long now = SystemClock.uptimeMillis();
|
|
out.writeInt(MAGIC);
|
|
out.writeInt(PARCEL_VERSION);
|
|
out.writeInt(STATE_COUNT);
|
|
out.writeInt(ADJ_COUNT);
|
|
out.writeInt(PSS_COUNT);
|
|
out.writeInt(LONGS_SIZE);
|
|
|
|
out.writeLong(mTimePeriodStartClock);
|
|
out.writeLong(mTimePeriodStartRealtime);
|
|
out.writeLong(mTimePeriodEndRealtime);
|
|
out.writeString(mRuntime);
|
|
out.writeString(mWebView);
|
|
out.writeInt(mFlags);
|
|
|
|
out.writeInt(mLongs.size());
|
|
out.writeInt(mNextLong);
|
|
for (int i=0; i<(mLongs.size()-1); i++) {
|
|
out.writeLongArray(mLongs.get(i));
|
|
}
|
|
long[] lastLongs = mLongs.get(mLongs.size()-1);
|
|
for (int i=0; i<mNextLong; i++) {
|
|
out.writeLong(lastLongs[i]);
|
|
if (DEBUG) Slog.d(TAG, "Writing last long #" + i + ": " + lastLongs[i]);
|
|
}
|
|
|
|
if (mMemFactor != STATE_NOTHING) {
|
|
mMemFactorDurations[mMemFactor] += now - mStartTime;
|
|
mStartTime = now;
|
|
}
|
|
out.writeLongArray(mMemFactorDurations);
|
|
|
|
ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
|
|
final int NPROC = procMap.size();
|
|
out.writeInt(NPROC);
|
|
for (int ip=0; ip<NPROC; ip++) {
|
|
out.writeString(procMap.keyAt(ip));
|
|
SparseArray<ProcessState> uids = procMap.valueAt(ip);
|
|
final int NUID = uids.size();
|
|
out.writeInt(NUID);
|
|
for (int iu=0; iu<NUID; iu++) {
|
|
out.writeInt(uids.keyAt(iu));
|
|
ProcessState proc = uids.valueAt(iu);
|
|
out.writeString(proc.mPackage);
|
|
proc.writeToParcel(out, now);
|
|
}
|
|
}
|
|
ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
|
|
final int NPKG = pkgMap.size();
|
|
out.writeInt(NPKG);
|
|
for (int ip=0; ip<NPKG; ip++) {
|
|
out.writeString(pkgMap.keyAt(ip));
|
|
SparseArray<PackageState> uids = pkgMap.valueAt(ip);
|
|
final int NUID = uids.size();
|
|
out.writeInt(NUID);
|
|
for (int iu=0; iu<NUID; iu++) {
|
|
out.writeInt(uids.keyAt(iu));
|
|
PackageState pkgState = uids.valueAt(iu);
|
|
final int NPROCS = pkgState.mProcesses.size();
|
|
out.writeInt(NPROCS);
|
|
for (int iproc=0; iproc<NPROCS; iproc++) {
|
|
out.writeString(pkgState.mProcesses.keyAt(iproc));
|
|
ProcessState proc = pkgState.mProcesses.valueAt(iproc);
|
|
if (proc.mCommonProcess == proc) {
|
|
// This is the same as the common process we wrote above.
|
|
out.writeInt(0);
|
|
} else {
|
|
// There is separate data for this package's process.
|
|
out.writeInt(1);
|
|
proc.writeToParcel(out, now);
|
|
}
|
|
}
|
|
final int NSRVS = pkgState.mServices.size();
|
|
out.writeInt(NSRVS);
|
|
for (int isvc=0; isvc<NSRVS; isvc++) {
|
|
out.writeString(pkgState.mServices.keyAt(isvc));
|
|
ServiceState svc = pkgState.mServices.valueAt(isvc);
|
|
svc.writeToParcel(out, now);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean readCheckedInt(Parcel in, int val, String what) {
|
|
int got;
|
|
if ((got=in.readInt()) != val) {
|
|
mReadError = "bad " + what + ": " + got;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void readFromParcel(Parcel in) {
|
|
final boolean hadData = mPackages.getMap().size() > 0
|
|
|| mProcesses.getMap().size() > 0;
|
|
if (hadData) {
|
|
resetSafely();
|
|
}
|
|
|
|
if (!readCheckedInt(in, MAGIC, "magic number")) {
|
|
return;
|
|
}
|
|
int version = in.readInt();
|
|
if (version != PARCEL_VERSION && version != 6) {
|
|
mReadError = "bad version: " + version;
|
|
return;
|
|
}
|
|
if (!readCheckedInt(in, STATE_COUNT, "state count")) {
|
|
return;
|
|
}
|
|
if (!readCheckedInt(in, ADJ_COUNT, "adj count")) {
|
|
return;
|
|
}
|
|
if (!readCheckedInt(in, PSS_COUNT, "pss count")) {
|
|
return;
|
|
}
|
|
if (!readCheckedInt(in, LONGS_SIZE, "longs size")) {
|
|
return;
|
|
}
|
|
|
|
mTimePeriodStartClock = in.readLong();
|
|
buildTimePeriodStartClockStr();
|
|
mTimePeriodStartRealtime = in.readLong();
|
|
mTimePeriodEndRealtime = in.readLong();
|
|
if (version == PARCEL_VERSION) {
|
|
mRuntime = in.readString();
|
|
mWebView = in.readString();
|
|
}
|
|
mFlags = in.readInt();
|
|
|
|
final int NLONGS = in.readInt();
|
|
final int NEXTLONG = in.readInt();
|
|
mLongs.clear();
|
|
for (int i=0; i<(NLONGS-1); i++) {
|
|
while (i >= mLongs.size()) {
|
|
mLongs.add(new long[LONGS_SIZE]);
|
|
}
|
|
in.readLongArray(mLongs.get(i));
|
|
}
|
|
long[] longs = new long[LONGS_SIZE];
|
|
mNextLong = NEXTLONG;
|
|
for (int i=0; i<NEXTLONG; i++) {
|
|
longs[i] = in.readLong();
|
|
if (DEBUG) Slog.d(TAG, "Reading last long #" + i + ": " + longs[i]);
|
|
}
|
|
mLongs.add(longs);
|
|
|
|
in.readLongArray(mMemFactorDurations);
|
|
|
|
int NPROC = in.readInt();
|
|
if (NPROC < 0) {
|
|
mReadError = "bad process count: " + NPROC;
|
|
return;
|
|
}
|
|
while (NPROC > 0) {
|
|
NPROC--;
|
|
String procName = in.readString();
|
|
if (procName == null) {
|
|
mReadError = "bad process name";
|
|
return;
|
|
}
|
|
int NUID = in.readInt();
|
|
if (NUID < 0) {
|
|
mReadError = "bad uid count: " + NUID;
|
|
return;
|
|
}
|
|
while (NUID > 0) {
|
|
NUID--;
|
|
int uid = in.readInt();
|
|
if (uid < 0) {
|
|
mReadError = "bad uid: " + uid;
|
|
return;
|
|
}
|
|
String pkgName = in.readString();
|
|
if (pkgName == null) {
|
|
mReadError = "bad process package name";
|
|
return;
|
|
}
|
|
ProcessState proc = hadData ? mProcesses.get(procName, uid) : null;
|
|
if (proc != null) {
|
|
if (!proc.readFromParcel(in, false)) {
|
|
return;
|
|
}
|
|
} else {
|
|
proc = new ProcessState(this, pkgName, uid, procName);
|
|
if (!proc.readFromParcel(in, true)) {
|
|
return;
|
|
}
|
|
}
|
|
if (DEBUG) Slog.d(TAG, "Adding process: " + procName + " " + uid + " " + proc);
|
|
mProcesses.put(procName, uid, proc);
|
|
}
|
|
}
|
|
|
|
if (DEBUG) Slog.d(TAG, "Read " + mProcesses.getMap().size() + " processes");
|
|
|
|
int NPKG = in.readInt();
|
|
if (NPKG < 0) {
|
|
mReadError = "bad package count: " + NPKG;
|
|
return;
|
|
}
|
|
while (NPKG > 0) {
|
|
NPKG--;
|
|
String pkgName = in.readString();
|
|
if (pkgName == null) {
|
|
mReadError = "bad package name";
|
|
return;
|
|
}
|
|
int NUID = in.readInt();
|
|
if (NUID < 0) {
|
|
mReadError = "bad uid count: " + NUID;
|
|
return;
|
|
}
|
|
while (NUID > 0) {
|
|
NUID--;
|
|
int uid = in.readInt();
|
|
if (uid < 0) {
|
|
mReadError = "bad uid: " + uid;
|
|
return;
|
|
}
|
|
PackageState pkgState = new PackageState(uid);
|
|
mPackages.put(pkgName, uid, pkgState);
|
|
int NPROCS = in.readInt();
|
|
if (NPROCS < 0) {
|
|
mReadError = "bad package process count: " + NPROCS;
|
|
return;
|
|
}
|
|
while (NPROCS > 0) {
|
|
NPROCS--;
|
|
String procName = in.readString();
|
|
if (procName == null) {
|
|
mReadError = "bad package process name";
|
|
return;
|
|
}
|
|
int hasProc = in.readInt();
|
|
if (DEBUG) Slog.d(TAG, "Reading package " + pkgName + " " + uid
|
|
+ " process " + procName + " hasProc=" + hasProc);
|
|
ProcessState commonProc = mProcesses.get(procName, uid);
|
|
if (DEBUG) Slog.d(TAG, "Got common proc " + procName + " " + uid
|
|
+ ": " + commonProc);
|
|
if (commonProc == null) {
|
|
mReadError = "no common proc: " + procName;
|
|
return;
|
|
}
|
|
if (hasProc != 0) {
|
|
// The process for this package is unique to the package; we
|
|
// need to load it. We don't need to do anything about it if
|
|
// it is not unique because if someone later looks for it
|
|
// 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)) {
|
|
return;
|
|
}
|
|
} else {
|
|
proc = new ProcessState(commonProc, pkgName, uid, procName, 0);
|
|
if (!proc.readFromParcel(in, true)) {
|
|
return;
|
|
}
|
|
}
|
|
if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: "
|
|
+ procName + " " + uid + " " + proc);
|
|
pkgState.mProcesses.put(procName, proc);
|
|
} else {
|
|
if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: "
|
|
+ procName + " " + uid + " " + commonProc);
|
|
pkgState.mProcesses.put(procName, commonProc);
|
|
}
|
|
}
|
|
int NSRVS = in.readInt();
|
|
if (NSRVS < 0) {
|
|
mReadError = "bad package service count: " + NSRVS;
|
|
return;
|
|
}
|
|
while (NSRVS > 0) {
|
|
NSRVS--;
|
|
String serviceName = in.readString();
|
|
if (serviceName == null) {
|
|
mReadError = "bad package service name";
|
|
return;
|
|
}
|
|
ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null;
|
|
if (serv == null) {
|
|
serv = new ServiceState(this, pkgName, null);
|
|
}
|
|
if (!serv.readFromParcel(in)) {
|
|
return;
|
|
}
|
|
if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " service: "
|
|
+ serviceName + " " + uid + " " + serv);
|
|
pkgState.mServices.put(serviceName, serv);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DEBUG) Slog.d(TAG, "Successfully read procstats!");
|
|
}
|
|
|
|
int addLongData(int index, int type, int num) {
|
|
int tableLen = mAddLongTable != null ? mAddLongTable.length : 0;
|
|
if (mAddLongTableSize >= tableLen) {
|
|
int newSize = ArrayUtils.idealIntArraySize(tableLen + 1);
|
|
int[] newTable = new int[newSize];
|
|
if (tableLen > 0) {
|
|
System.arraycopy(mAddLongTable, 0, newTable, 0, tableLen);
|
|
}
|
|
mAddLongTable = newTable;
|
|
}
|
|
if (mAddLongTableSize > 0 && mAddLongTableSize - index != 0) {
|
|
System.arraycopy(mAddLongTable, index, mAddLongTable, index + 1,
|
|
mAddLongTableSize - index);
|
|
}
|
|
int off = allocLongData(num);
|
|
mAddLongTable[index] = type | off;
|
|
mAddLongTableSize++;
|
|
return off;
|
|
}
|
|
|
|
int allocLongData(int num) {
|
|
int whichLongs = mLongs.size()-1;
|
|
long[] longs = mLongs.get(whichLongs);
|
|
if (mNextLong + num > longs.length) {
|
|
longs = new long[LONGS_SIZE];
|
|
mLongs.add(longs);
|
|
whichLongs++;
|
|
mNextLong = 0;
|
|
}
|
|
int off = (whichLongs<<OFFSET_ARRAY_SHIFT) | (mNextLong<<OFFSET_INDEX_SHIFT);
|
|
mNextLong += num;
|
|
return off;
|
|
}
|
|
|
|
boolean validateLongOffset(int off) {
|
|
int arr = (off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK;
|
|
if (arr >= mLongs.size()) {
|
|
return false;
|
|
}
|
|
int idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
|
|
if (idx >= LONGS_SIZE) {
|
|
return false;
|
|
}
|
|
if (DEBUG) Slog.d(TAG, "Validated long " + printLongOffset(off)
|
|
+ ": " + getLong(off, 0));
|
|
return true;
|
|
}
|
|
|
|
static String printLongOffset(int off) {
|
|
StringBuilder sb = new StringBuilder(16);
|
|
sb.append("a"); sb.append((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
|
|
sb.append("i"); sb.append((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK);
|
|
sb.append("t"); sb.append((off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK);
|
|
return sb.toString();
|
|
}
|
|
|
|
void setLong(int off, int index, long value) {
|
|
long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
|
|
longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)] = value;
|
|
}
|
|
|
|
long getLong(int off, int index) {
|
|
long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
|
|
return longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)];
|
|
}
|
|
|
|
static int binarySearch(int[] array, int size, int value) {
|
|
int lo = 0;
|
|
int hi = size - 1;
|
|
|
|
while (lo <= hi) {
|
|
int mid = (lo + hi) >>> 1;
|
|
int midVal = (array[mid] >> OFFSET_TYPE_SHIFT) & OFFSET_TYPE_MASK;
|
|
|
|
if (midVal < value) {
|
|
lo = mid + 1;
|
|
} else if (midVal > value) {
|
|
hi = mid - 1;
|
|
} else {
|
|
return mid; // value found
|
|
}
|
|
}
|
|
return ~lo; // value not present
|
|
}
|
|
|
|
public PackageState getPackageStateLocked(String packageName, int uid) {
|
|
PackageState as = mPackages.get(packageName, uid);
|
|
if (as != null) {
|
|
return as;
|
|
}
|
|
as = new PackageState(uid);
|
|
mPackages.put(packageName, uid, as);
|
|
return as;
|
|
}
|
|
|
|
public ProcessState getProcessStateLocked(String packageName, int uid, String processName) {
|
|
final PackageState pkgState = getPackageStateLocked(packageName, uid);
|
|
ProcessState ps = pkgState.mProcesses.get(processName);
|
|
if (ps != null) {
|
|
return ps;
|
|
}
|
|
ProcessState commonProc = mProcesses.get(processName, uid);
|
|
if (commonProc == null) {
|
|
commonProc = new ProcessState(this, packageName, uid, processName);
|
|
mProcesses.put(processName, uid, commonProc);
|
|
}
|
|
if (!commonProc.mMultiPackage) {
|
|
if (packageName.equals(commonProc.mPackage)) {
|
|
// This common process is not in use by multiple packages, and
|
|
// is for the calling package, so we can just use it directly.
|
|
ps = commonProc;
|
|
} else {
|
|
// This common process has not been in use by multiple packages,
|
|
// but it was created for a different package than the caller.
|
|
// We need to convert it to a multi-package process.
|
|
commonProc.mMultiPackage = true;
|
|
// The original package it was created for now needs to point
|
|
// to its own copy.
|
|
long now = SystemClock.uptimeMillis();
|
|
pkgState.mProcesses.put(commonProc.mName, commonProc.clone(
|
|
commonProc.mPackage, now));
|
|
ps = new ProcessState(commonProc, packageName, uid, processName, now);
|
|
}
|
|
} else {
|
|
// The common process is for multiple packages, we need to create a
|
|
// separate object for the per-package data.
|
|
ps = new ProcessState(commonProc, packageName, uid, processName,
|
|
SystemClock.uptimeMillis());
|
|
}
|
|
pkgState.mProcesses.put(processName, ps);
|
|
return ps;
|
|
}
|
|
|
|
public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpAll) {
|
|
long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
|
|
mStartTime, now);
|
|
ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
|
|
boolean printedHeader = false;
|
|
for (int ip=0; ip<pkgMap.size(); ip++) {
|
|
String pkgName = pkgMap.keyAt(ip);
|
|
if (reqPackage != null && !reqPackage.equals(pkgName)) {
|
|
continue;
|
|
}
|
|
SparseArray<PackageState> uids = pkgMap.valueAt(ip);
|
|
for (int iu=0; iu<uids.size(); iu++) {
|
|
int uid = uids.keyAt(iu);
|
|
PackageState pkgState = uids.valueAt(iu);
|
|
final int NPROCS = pkgState.mProcesses.size();
|
|
final int NSRVS = pkgState.mServices.size();
|
|
if (NPROCS > 0 || NSRVS > 0) {
|
|
if (!printedHeader) {
|
|
pw.println("Per-Package Process Stats:");
|
|
printedHeader = true;
|
|
}
|
|
pw.print(" * "); pw.print(pkgName); pw.print(" / ");
|
|
UserHandle.formatUid(pw, uid); pw.println(":");
|
|
}
|
|
if (dumpAll) {
|
|
for (int iproc=0; iproc<NPROCS; iproc++) {
|
|
ProcessState proc = pkgState.mProcesses.valueAt(iproc);
|
|
pw.print(" Process ");
|
|
pw.print(pkgState.mProcesses.keyAt(iproc));
|
|
pw.print(" (");
|
|
pw.print(proc.mDurationsTableSize);
|
|
pw.print(" entries)");
|
|
pw.println(":");
|
|
dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
|
|
ALL_PROC_STATES, now);
|
|
dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
|
|
ALL_PROC_STATES);
|
|
if (dumpAll) {
|
|
pw.print(" mNumStartedServices=");
|
|
pw.println(proc.mNumStartedServices);
|
|
}
|
|
}
|
|
} else {
|
|
ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
|
|
for (int iproc=0; iproc<NPROCS; iproc++) {
|
|
procs.add(pkgState.mProcesses.valueAt(iproc));
|
|
}
|
|
dumpProcessSummaryLocked(pw, " ", procs, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
|
|
NON_CACHED_PROC_STATES, now, totalTime);
|
|
}
|
|
for (int isvc=0; isvc<NSRVS; isvc++) {
|
|
if (dumpAll) {
|
|
pw.print(" Service ");
|
|
} else {
|
|
pw.print(" * ");
|
|
}
|
|
pw.print(pkgState.mServices.keyAt(isvc));
|
|
pw.println(":");
|
|
ServiceState svc = pkgState.mServices.valueAt(isvc);
|
|
dumpServiceStats(pw, " ", " ", " ", "Started", svc,
|
|
svc.mStartedCount, ServiceState.SERVICE_STARTED, svc.mStartedState,
|
|
svc.mStartedStartTime, now, totalTime, dumpAll);
|
|
dumpServiceStats(pw, " ", " ", " ", "Bound", svc,
|
|
svc.mBoundCount, ServiceState.SERVICE_BOUND, svc.mBoundState,
|
|
svc.mBoundStartTime, now, totalTime, dumpAll);
|
|
dumpServiceStats(pw, " ", " ", " ", "Executing", svc,
|
|
svc.mExecCount, ServiceState.SERVICE_EXEC, svc.mExecState,
|
|
svc.mExecStartTime, now, totalTime, dumpAll);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (reqPackage == null) {
|
|
ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
|
|
printedHeader = false;
|
|
for (int ip=0; ip<procMap.size(); ip++) {
|
|
String procName = procMap.keyAt(ip);
|
|
SparseArray<ProcessState> uids = procMap.valueAt(ip);
|
|
for (int iu=0; iu<uids.size(); iu++) {
|
|
int uid = uids.keyAt(iu);
|
|
ProcessState proc = uids.valueAt(iu);
|
|
if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING
|
|
&& proc.mPssTableSize == 0) {
|
|
continue;
|
|
}
|
|
if (!printedHeader) {
|
|
pw.println("Process Stats:");
|
|
printedHeader = true;
|
|
}
|
|
pw.print(" * "); pw.print(procName); pw.print(" / ");
|
|
UserHandle.formatUid(pw, uid);
|
|
pw.print(" ("); pw.print(proc.mDurationsTableSize);
|
|
pw.print(" entries)"); pw.println(":");
|
|
dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
|
|
ALL_PROC_STATES, now);
|
|
dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
|
|
ALL_PROC_STATES);
|
|
}
|
|
}
|
|
|
|
pw.println();
|
|
pw.println("Summary:");
|
|
dumpSummaryLocked(pw, reqPackage, now);
|
|
} else {
|
|
pw.println();
|
|
dumpTotalsLocked(pw, now);
|
|
}
|
|
|
|
if (dumpAll) {
|
|
pw.println();
|
|
pw.println("Internal state:");
|
|
pw.print(" Num long arrays: "); pw.println(mLongs.size());
|
|
pw.print(" Next long entry: "); pw.println(mNextLong);
|
|
pw.print(" mRunning="); pw.println(mRunning);
|
|
}
|
|
}
|
|
|
|
static long dumpSingleServiceTime(PrintWriter pw, String prefix, ServiceState service,
|
|
int serviceType, int curState, long curStartTime, long now) {
|
|
long totalTime = 0;
|
|
int printedScreen = -1;
|
|
for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
|
|
int printedMem = -1;
|
|
for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
|
|
int state = imem+iscreen;
|
|
long time = service.getDuration(serviceType, curState, curStartTime,
|
|
state, now);
|
|
String running = "";
|
|
if (curState == state) {
|
|
time += now - curStartTime;
|
|
if (pw != null) {
|
|
running = " (running)";
|
|
}
|
|
}
|
|
if (time != 0) {
|
|
if (pw != null) {
|
|
pw.print(prefix);
|
|
printScreenLabel(pw, printedScreen != iscreen
|
|
? iscreen : STATE_NOTHING);
|
|
printedScreen = iscreen;
|
|
printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
|
|
printedMem = imem;
|
|
TimeUtils.formatDuration(time, pw); pw.println(running);
|
|
}
|
|
totalTime += time;
|
|
}
|
|
}
|
|
}
|
|
if (totalTime != 0 && pw != null) {
|
|
pw.print(prefix);
|
|
printScreenLabel(pw, STATE_NOTHING);
|
|
pw.print("TOTAL: ");
|
|
TimeUtils.formatDuration(totalTime, pw);
|
|
pw.println();
|
|
}
|
|
return totalTime;
|
|
}
|
|
|
|
void dumpServiceStats(PrintWriter pw, String prefix, String prefixInner,
|
|
String headerPrefix, String header, ServiceState service,
|
|
int count, int serviceType, int state, long startTime, long now, long totalTime,
|
|
boolean dumpAll) {
|
|
if (count != 0) {
|
|
if (dumpAll) {
|
|
pw.print(prefix); pw.print(header);
|
|
pw.print(" op count "); pw.print(count); pw.println(":");
|
|
dumpSingleServiceTime(pw, prefixInner, service, serviceType, state, startTime,
|
|
now);
|
|
} else {
|
|
long myTime = dumpSingleServiceTime(null, null, service, serviceType, state,
|
|
startTime, now);
|
|
pw.print(prefix); pw.print(headerPrefix); pw.print(header);
|
|
pw.print(" count "); pw.print(count);
|
|
pw.print(" / time ");
|
|
printPercent(pw, (double)myTime/(double)totalTime);
|
|
pw.println();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now) {
|
|
long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
|
|
mStartTime, now);
|
|
dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
|
|
NON_CACHED_PROC_STATES, now, totalTime, reqPackage);
|
|
pw.println();
|
|
dumpTotalsLocked(pw, now);
|
|
}
|
|
|
|
void dumpTotalsLocked(PrintWriter pw, long now) {
|
|
pw.println("Run time Stats:");
|
|
dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now);
|
|
pw.println();
|
|
pw.print(" Start time: ");
|
|
pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
|
|
pw.println();
|
|
pw.print(" Total elapsed time: ");
|
|
TimeUtils.formatDuration(
|
|
(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
|
|
- mTimePeriodStartRealtime, pw);
|
|
boolean partial = true;
|
|
if ((mFlags&FLAG_SHUTDOWN) != 0) {
|
|
pw.print(" (shutdown)");
|
|
partial = false;
|
|
}
|
|
if ((mFlags&FLAG_SYSPROPS) != 0) {
|
|
pw.print(" (sysprops)");
|
|
partial = false;
|
|
}
|
|
if ((mFlags&FLAG_COMPLETE) != 0) {
|
|
pw.print(" (complete)");
|
|
partial = false;
|
|
}
|
|
if (partial) {
|
|
pw.print(" (partial)");
|
|
}
|
|
pw.print(' ');
|
|
pw.print(mRuntime);
|
|
pw.print(' ');
|
|
pw.print(mWebView);
|
|
pw.println();
|
|
}
|
|
|
|
void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix,
|
|
int[] screenStates, int[] memStates, int[] procStates, long now, long totalTime,
|
|
String reqPackage) {
|
|
ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
|
|
procStates, now, reqPackage);
|
|
if (procs.size() > 0) {
|
|
if (header != null) {
|
|
pw.println();
|
|
pw.println(header);
|
|
}
|
|
dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates, procStates,
|
|
now, totalTime);
|
|
}
|
|
}
|
|
|
|
public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates,
|
|
int[] procStates, long now, String reqPackage) {
|
|
ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
|
|
ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
|
|
for (int ip=0; ip<pkgMap.size(); ip++) {
|
|
if (reqPackage != null && !reqPackage.equals(pkgMap.keyAt(ip))) {
|
|
continue;
|
|
}
|
|
SparseArray<PackageState> procs = pkgMap.valueAt(ip);
|
|
for (int iu=0; iu<procs.size(); iu++) {
|
|
PackageState state = procs.valueAt(iu);
|
|
for (int iproc=0; iproc<state.mProcesses.size(); iproc++) {
|
|
ProcessState proc = state.mProcesses.valueAt(iproc);
|
|
foundProcs.add(proc.mCommonProcess);
|
|
}
|
|
}
|
|
}
|
|
ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>(foundProcs.size());
|
|
for (int i=0; i<foundProcs.size(); i++) {
|
|
ProcessState proc = foundProcs.valueAt(i);
|
|
if (computeProcessTimeLocked(proc, screenStates, memStates,
|
|
procStates, now) > 0) {
|
|
outProcs.add(proc);
|
|
}
|
|
}
|
|
Collections.sort(outProcs, new Comparator<ProcessState>() {
|
|
@Override
|
|
public int compare(ProcessState lhs, ProcessState rhs) {
|
|
if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) {
|
|
return -1;
|
|
} else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
});
|
|
return outProcs;
|
|
}
|
|
|
|
String collapseString(String pkgName, String itemName) {
|
|
if (itemName.startsWith(pkgName)) {
|
|
final int ITEMLEN = itemName.length();
|
|
final int PKGLEN = pkgName.length();
|
|
if (ITEMLEN == PKGLEN) {
|
|
return "";
|
|
} else if (ITEMLEN >= PKGLEN) {
|
|
if (itemName.charAt(PKGLEN) == '.') {
|
|
return itemName.substring(PKGLEN);
|
|
}
|
|
}
|
|
}
|
|
return itemName;
|
|
}
|
|
|
|
public void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
|
|
final long now = SystemClock.uptimeMillis();
|
|
ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
|
|
pw.println("vers,3");
|
|
pw.print("period,"); pw.print(mTimePeriodStartClockStr);
|
|
pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
|
|
pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
|
|
boolean partial = true;
|
|
if ((mFlags&FLAG_SHUTDOWN) != 0) {
|
|
pw.print(",shutdown");
|
|
partial = false;
|
|
}
|
|
if ((mFlags&FLAG_SYSPROPS) != 0) {
|
|
pw.print(",sysprops");
|
|
partial = false;
|
|
}
|
|
if ((mFlags&FLAG_COMPLETE) != 0) {
|
|
pw.print(",complete");
|
|
partial = false;
|
|
}
|
|
if (partial) {
|
|
pw.print(",partial");
|
|
}
|
|
pw.println();
|
|
pw.print("config,"); pw.print(mRuntime); pw.print(','); pw.println(mWebView);
|
|
for (int ip=0; ip<pkgMap.size(); ip++) {
|
|
String pkgName = pkgMap.keyAt(ip);
|
|
if (reqPackage != null && !reqPackage.equals(pkgName)) {
|
|
continue;
|
|
}
|
|
SparseArray<PackageState> uids = pkgMap.valueAt(ip);
|
|
for (int iu=0; iu<uids.size(); iu++) {
|
|
int uid = uids.keyAt(iu);
|
|
PackageState pkgState = uids.valueAt(iu);
|
|
final int NPROCS = pkgState.mProcesses.size();
|
|
final int NSRVS = pkgState.mServices.size();
|
|
for (int iproc=0; iproc<NPROCS; iproc++) {
|
|
ProcessState proc = pkgState.mProcesses.valueAt(iproc);
|
|
pw.print("pkgproc,");
|
|
pw.print(pkgName);
|
|
pw.print(",");
|
|
pw.print(uid);
|
|
pw.print(",");
|
|
pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
|
|
dumpAllProcessStateCheckin(pw, proc, now);
|
|
pw.println();
|
|
if (proc.mPssTableSize > 0) {
|
|
pw.print("pkgpss,");
|
|
pw.print(pkgName);
|
|
pw.print(",");
|
|
pw.print(uid);
|
|
pw.print(",");
|
|
pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
|
|
dumpAllProcessPssCheckin(pw, proc);
|
|
pw.println();
|
|
}
|
|
if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0) {
|
|
pw.print("pkgkills,");
|
|
pw.print(pkgName);
|
|
pw.print(",");
|
|
pw.print(uid);
|
|
pw.print(",");
|
|
pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
|
|
pw.print(",");
|
|
pw.print(proc.mNumExcessiveWake);
|
|
pw.print(",");
|
|
pw.print(proc.mNumExcessiveCpu);
|
|
pw.println();
|
|
}
|
|
}
|
|
for (int isvc=0; isvc<NSRVS; isvc++) {
|
|
String serviceName = collapseString(pkgName,
|
|
pkgState.mServices.keyAt(isvc));
|
|
ServiceState svc = pkgState.mServices.valueAt(isvc);
|
|
dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, serviceName,
|
|
svc, ServiceState.SERVICE_STARTED, svc.mStartedCount,
|
|
svc.mStartedState, svc.mStartedStartTime, now);
|
|
dumpServiceTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, serviceName,
|
|
svc, ServiceState.SERVICE_BOUND, svc.mBoundCount,
|
|
svc.mBoundState, svc.mBoundStartTime, now);
|
|
dumpServiceTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, serviceName,
|
|
svc, ServiceState.SERVICE_EXEC, svc.mExecCount,
|
|
svc.mExecState, svc.mExecStartTime, now);
|
|
}
|
|
}
|
|
}
|
|
|
|
ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
|
|
for (int ip=0; ip<procMap.size(); ip++) {
|
|
String procName = procMap.keyAt(ip);
|
|
SparseArray<ProcessState> uids = procMap.valueAt(ip);
|
|
for (int iu=0; iu<uids.size(); iu++) {
|
|
int uid = uids.keyAt(iu);
|
|
ProcessState procState = uids.valueAt(iu);
|
|
if (procState.mDurationsTableSize > 0) {
|
|
pw.print("proc,");
|
|
pw.print(procName);
|
|
pw.print(",");
|
|
pw.print(uid);
|
|
dumpAllProcessStateCheckin(pw, procState, now);
|
|
pw.println();
|
|
}
|
|
if (procState.mPssTableSize > 0) {
|
|
pw.print("pss,");
|
|
pw.print(procName);
|
|
pw.print(",");
|
|
pw.print(uid);
|
|
dumpAllProcessPssCheckin(pw, procState);
|
|
pw.println();
|
|
}
|
|
if (procState.mNumExcessiveWake > 0 || procState.mNumExcessiveCpu > 0) {
|
|
pw.print("kills,");
|
|
pw.print(procName);
|
|
pw.print(",");
|
|
pw.print(uid);
|
|
pw.print(",");
|
|
pw.print(procState.mNumExcessiveWake);
|
|
pw.print(",");
|
|
pw.print(procState.mNumExcessiveCpu);
|
|
pw.println();
|
|
}
|
|
}
|
|
}
|
|
pw.print("total");
|
|
dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor,
|
|
mStartTime, now);
|
|
pw.println();
|
|
}
|
|
|
|
public static final class ProcessState {
|
|
final ProcessStats mProcessStats;
|
|
final ProcessState mCommonProcess;
|
|
final String mPackage;
|
|
final int mUid;
|
|
final String mName;
|
|
|
|
int[] mDurationsTable;
|
|
int mDurationsTableSize;
|
|
|
|
//final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT];
|
|
int mCurState = STATE_NOTHING;
|
|
long mStartTime;
|
|
|
|
int mLastPssState = STATE_NOTHING;
|
|
long mLastPssTime;
|
|
int[] mPssTable;
|
|
int mPssTableSize;
|
|
|
|
int mNumStartedServices;
|
|
|
|
int mNumExcessiveWake;
|
|
int mNumExcessiveCpu;
|
|
|
|
boolean mMultiPackage;
|
|
|
|
long mTmpTotalTime;
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
public ProcessState(ProcessStats processStats, String pkg, int uid, String name) {
|
|
mProcessStats = processStats;
|
|
mCommonProcess = this;
|
|
mPackage = pkg;
|
|
mUid = uid;
|
|
mName = name;
|
|
}
|
|
|
|
/**
|
|
* Create a new per-package process state for an existing top-level process
|
|
* state. The current running state of the top-level process is also copied,
|
|
* marked as started running at 'now'.
|
|
*/
|
|
public ProcessState(ProcessState commonProcess, String pkg, int uid, String name,
|
|
long now) {
|
|
mProcessStats = commonProcess.mProcessStats;
|
|
mCommonProcess = commonProcess;
|
|
mPackage = pkg;
|
|
mUid = uid;
|
|
mName = name;
|
|
mCurState = commonProcess.mCurState;
|
|
mStartTime = now;
|
|
}
|
|
|
|
ProcessState clone(String pkg, long now) {
|
|
ProcessState pnew = new ProcessState(this, pkg, mUid, mName, now);
|
|
if (mDurationsTable != null) {
|
|
mProcessStats.mAddLongTable = new int[mDurationsTable.length];
|
|
mProcessStats.mAddLongTableSize = 0;
|
|
for (int i=0; i<mDurationsTableSize; i++) {
|
|
int origEnt = mDurationsTable[i];
|
|
int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
|
|
int newOff = mProcessStats.addLongData(i, type, 1);
|
|
mProcessStats.mAddLongTable[i] = newOff | type;
|
|
mProcessStats.setLong(newOff, 0, mProcessStats.getLong(origEnt, 0));
|
|
}
|
|
pnew.mDurationsTable = mProcessStats.mAddLongTable;
|
|
pnew.mDurationsTableSize = mProcessStats.mAddLongTableSize;
|
|
}
|
|
if (mPssTable != null) {
|
|
mProcessStats.mAddLongTable = new int[mPssTable.length];
|
|
mProcessStats.mAddLongTableSize = 0;
|
|
for (int i=0; i<mPssTableSize; i++) {
|
|
int origEnt = mPssTable[i];
|
|
int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
|
|
int newOff = mProcessStats.addLongData(i, type, PSS_COUNT);
|
|
mProcessStats.mAddLongTable[i] = newOff | type;
|
|
for (int j=0; j<PSS_COUNT; j++) {
|
|
mProcessStats.setLong(newOff, j, mProcessStats.getLong(origEnt, j));
|
|
}
|
|
}
|
|
pnew.mPssTable = mProcessStats.mAddLongTable;
|
|
pnew.mPssTableSize = mProcessStats.mAddLongTableSize;
|
|
}
|
|
pnew.mNumExcessiveWake = mNumExcessiveWake;
|
|
pnew.mNumExcessiveCpu = mNumExcessiveCpu;
|
|
pnew.mNumStartedServices = mNumStartedServices;
|
|
return pnew;
|
|
}
|
|
|
|
void resetSafely(long now) {
|
|
mDurationsTable = null;
|
|
mDurationsTableSize = 0;
|
|
mStartTime = now;
|
|
mLastPssState = STATE_NOTHING;
|
|
mLastPssTime = 0;
|
|
mPssTable = null;
|
|
mPssTableSize = 0;
|
|
mNumExcessiveWake = 0;
|
|
mNumExcessiveCpu = 0;
|
|
}
|
|
|
|
void writeToParcel(Parcel out, long now) {
|
|
commitStateTime(now);
|
|
out.writeInt(mMultiPackage ? 1 : 0);
|
|
out.writeInt(mDurationsTableSize);
|
|
for (int i=0; i<mDurationsTableSize; i++) {
|
|
if (DEBUG) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": "
|
|
+ printLongOffset(mDurationsTable[i]));
|
|
out.writeInt(mDurationsTable[i]);
|
|
}
|
|
out.writeInt(mPssTableSize);
|
|
for (int i=0; i<mPssTableSize; i++) {
|
|
if (DEBUG) Slog.i(TAG, "Writing in " + mName + " pss #" + i + ": "
|
|
+ printLongOffset(mPssTable[i]));
|
|
out.writeInt(mPssTable[i]);
|
|
}
|
|
out.writeInt(mNumExcessiveWake);
|
|
out.writeInt(mNumExcessiveCpu);
|
|
}
|
|
|
|
boolean readFromParcel(Parcel in, boolean fully) {
|
|
boolean multiPackage = in.readInt() != 0;
|
|
if (fully) {
|
|
mMultiPackage = multiPackage;
|
|
}
|
|
if (DEBUG) Slog.d(TAG, "Reading durations table...");
|
|
mDurationsTable = mProcessStats.readTableFromParcel(in, mName, "durations");
|
|
if (mDurationsTable == BAD_TABLE) {
|
|
return false;
|
|
}
|
|
mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
|
|
if (DEBUG) Slog.d(TAG, "Reading pss table...");
|
|
mPssTable = mProcessStats.readTableFromParcel(in, mName, "pss");
|
|
if (mPssTable == BAD_TABLE) {
|
|
return false;
|
|
}
|
|
mPssTableSize = mPssTable != null ? mPssTable.length : 0;
|
|
mNumExcessiveWake = in.readInt();
|
|
mNumExcessiveCpu = in.readInt();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Update the current state of the given list of processes.
|
|
*
|
|
* @param state Current ActivityManager.PROCESS_STATE_*
|
|
* @param memFactor Current mem factor constant.
|
|
* @param now Current time.
|
|
* @param pkgList Processes to update.
|
|
*/
|
|
public void setState(int state, int memFactor, long now,
|
|
ArrayMap<String, ProcessState> pkgList) {
|
|
if (state < 0) {
|
|
state = mNumStartedServices > 0
|
|
? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING;
|
|
} else {
|
|
state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT);
|
|
}
|
|
|
|
// First update the common process.
|
|
mCommonProcess.setState(state, now);
|
|
|
|
// If the common process is not multi-package, there is nothing else to do.
|
|
if (!mCommonProcess.mMultiPackage) {
|
|
return;
|
|
}
|
|
|
|
if (pkgList != null) {
|
|
for (int ip=pkgList.size()-1; ip>=0; ip--) {
|
|
pullFixedProc(pkgList, ip).setState(state, now);
|
|
}
|
|
}
|
|
}
|
|
|
|
void setState(int state, long now) {
|
|
if (mCurState != state) {
|
|
//Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state);
|
|
commitStateTime(now);
|
|
mCurState = state;
|
|
}
|
|
}
|
|
|
|
void commitStateTime(long now) {
|
|
if (mCurState != STATE_NOTHING) {
|
|
long dur = now - mStartTime;
|
|
if (dur > 0) {
|
|
int idx = binarySearch(mDurationsTable, mDurationsTableSize, mCurState);
|
|
int off;
|
|
if (idx >= 0) {
|
|
off = mDurationsTable[idx];
|
|
} else {
|
|
mProcessStats.mAddLongTable = mDurationsTable;
|
|
mProcessStats.mAddLongTableSize = mDurationsTableSize;
|
|
off = mProcessStats.addLongData(~idx, mCurState, 1);
|
|
mDurationsTable = mProcessStats.mAddLongTable;
|
|
mDurationsTableSize = mProcessStats.mAddLongTableSize;
|
|
}
|
|
long[] longs = mProcessStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
|
|
longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
|
|
}
|
|
}
|
|
mStartTime = now;
|
|
}
|
|
|
|
void incStartedServices(int memFactor, long now) {
|
|
if (mCommonProcess != this) {
|
|
mCommonProcess.incStartedServices(memFactor, now);
|
|
}
|
|
mNumStartedServices++;
|
|
if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) {
|
|
setState(STATE_NOTHING, memFactor, now, null);
|
|
}
|
|
}
|
|
|
|
void decStartedServices(int memFactor, long now) {
|
|
if (mCommonProcess != this) {
|
|
mCommonProcess.decStartedServices(memFactor, now);
|
|
}
|
|
mNumStartedServices--;
|
|
if (mNumStartedServices == 0 && mCurState == STATE_SERVICE_RESTARTING) {
|
|
setState(STATE_NOTHING, memFactor, now, null);
|
|
} else if (mNumStartedServices < 0) {
|
|
throw new IllegalStateException("Proc started services underrun: pkg="
|
|
+ mPackage + " uid=" + mUid + " name=" + mName);
|
|
}
|
|
}
|
|
|
|
public void addPss(long pss, long uss, boolean always) {
|
|
if (!always) {
|
|
if (mLastPssState == mCurState && SystemClock.uptimeMillis()
|
|
< (mLastPssTime+(30*1000))) {
|
|
return;
|
|
}
|
|
}
|
|
mLastPssState = mCurState;
|
|
mLastPssTime = SystemClock.uptimeMillis();
|
|
if (mCurState != STATE_NOTHING) {
|
|
int idx = binarySearch(mPssTable, mPssTableSize, mCurState);
|
|
int off;
|
|
if (idx >= 0) {
|
|
off = mPssTable[idx];
|
|
} else {
|
|
mProcessStats.mAddLongTable = mPssTable;
|
|
mProcessStats.mAddLongTableSize = mPssTableSize;
|
|
off = mProcessStats.addLongData(~idx, mCurState, PSS_COUNT);
|
|
mPssTable = mProcessStats.mAddLongTable;
|
|
mPssTableSize = mProcessStats.mAddLongTableSize;
|
|
}
|
|
long[] longs = mProcessStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
|
|
idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
|
|
long count = longs[idx+PSS_SAMPLE_COUNT];
|
|
if (count == 0) {
|
|
longs[idx+PSS_SAMPLE_COUNT] = 1;
|
|
longs[idx+PSS_MINIMUM] = pss;
|
|
longs[idx+PSS_AVERAGE] = pss;
|
|
longs[idx+PSS_MAXIMUM] = pss;
|
|
longs[idx+PSS_USS_MINIMUM] = uss;
|
|
longs[idx+PSS_USS_AVERAGE] = uss;
|
|
longs[idx+PSS_USS_MAXIMUM] = uss;
|
|
} else {
|
|
longs[idx+PSS_SAMPLE_COUNT] = count+1;
|
|
if (longs[idx+PSS_MINIMUM] > pss) {
|
|
longs[idx+PSS_MINIMUM] = pss;
|
|
}
|
|
longs[idx+PSS_AVERAGE] = (long)(
|
|
((longs[idx+PSS_AVERAGE]*(double)count)+pss) / (count+1) );
|
|
if (longs[idx+PSS_MAXIMUM] < pss) {
|
|
longs[idx+PSS_MAXIMUM] = pss;
|
|
}
|
|
if (longs[idx+PSS_USS_MINIMUM] > uss) {
|
|
longs[idx+PSS_USS_MINIMUM] = uss;
|
|
}
|
|
longs[idx+PSS_USS_AVERAGE] = (long)(
|
|
((longs[idx+PSS_USS_AVERAGE]*(double)count)+uss) / (count+1) );
|
|
if (longs[idx+PSS_USS_MAXIMUM] < uss) {
|
|
longs[idx+PSS_USS_MAXIMUM] = uss;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void reportExcessiveWake(ArrayMap<String, ProcessState> pkgList) {
|
|
mCommonProcess.mNumExcessiveWake++;
|
|
if (!mCommonProcess.mMultiPackage) {
|
|
return;
|
|
}
|
|
|
|
for (int ip=pkgList.size()-1; ip>=0; ip--) {
|
|
pullFixedProc(pkgList, ip).mNumExcessiveWake++;
|
|
}
|
|
}
|
|
|
|
public void reportExcessiveCpu(ArrayMap<String, ProcessState> pkgList) {
|
|
mCommonProcess.mNumExcessiveCpu++;
|
|
if (!mCommonProcess.mMultiPackage) {
|
|
return;
|
|
}
|
|
|
|
for (int ip=pkgList.size()-1; ip>=0; ip--) {
|
|
pullFixedProc(pkgList, ip).mNumExcessiveCpu++;
|
|
}
|
|
}
|
|
|
|
ProcessState pullFixedProc(String pkgName) {
|
|
if (mMultiPackage) {
|
|
// The array map is still pointing to a common process state
|
|
// that is now shared across packages. Update it to point to
|
|
// the new per-package state.
|
|
ProcessState proc = mProcessStats.mPackages.get(pkgName,
|
|
mUid).mProcesses.get(mName);
|
|
if (proc == null) {
|
|
throw new IllegalStateException("Didn't create per-package process");
|
|
}
|
|
return proc;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
private ProcessState pullFixedProc(ArrayMap<String, ProcessState> pkgList,
|
|
int index) {
|
|
ProcessState proc = pkgList.valueAt(index);
|
|
if (proc.mMultiPackage) {
|
|
// The array map is still pointing to a common process state
|
|
// that is now shared across packages. Update it to point to
|
|
// the new per-package state.
|
|
proc = mProcessStats.mPackages.get(pkgList.keyAt(index),
|
|
proc.mUid).mProcesses.get(proc.mName);
|
|
if (proc == null) {
|
|
throw new IllegalStateException("Didn't create per-package process");
|
|
}
|
|
pkgList.setValueAt(index, proc);
|
|
}
|
|
return proc;
|
|
}
|
|
|
|
long getDuration(int state, long now) {
|
|
int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
|
|
long time = idx >= 0 ? mProcessStats.getLong(mDurationsTable[idx], 0) : 0;
|
|
if (mCurState == state) {
|
|
time += now - mStartTime;
|
|
}
|
|
return time;
|
|
}
|
|
|
|
long getPssSampleCount(int state) {
|
|
int idx = binarySearch(mPssTable, mPssTableSize, state);
|
|
return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_SAMPLE_COUNT) : 0;
|
|
}
|
|
|
|
long getPssMinimum(int state) {
|
|
int idx = binarySearch(mPssTable, mPssTableSize, state);
|
|
return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_MINIMUM) : 0;
|
|
}
|
|
|
|
long getPssAverage(int state) {
|
|
int idx = binarySearch(mPssTable, mPssTableSize, state);
|
|
return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_AVERAGE) : 0;
|
|
}
|
|
|
|
long getPssMaximum(int state) {
|
|
int idx = binarySearch(mPssTable, mPssTableSize, state);
|
|
return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_MAXIMUM) : 0;
|
|
}
|
|
|
|
long getPssUssMinimum(int state) {
|
|
int idx = binarySearch(mPssTable, mPssTableSize, state);
|
|
return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0;
|
|
}
|
|
|
|
long getPssUssAverage(int state) {
|
|
int idx = binarySearch(mPssTable, mPssTableSize, state);
|
|
return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0;
|
|
}
|
|
|
|
long getPssUssMaximum(int state) {
|
|
int idx = binarySearch(mPssTable, mPssTableSize, state);
|
|
return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0;
|
|
}
|
|
}
|
|
|
|
public static final class ServiceState {
|
|
final ProcessStats mProcessStats;
|
|
final String mPackage;
|
|
ProcessState mProc;
|
|
|
|
int mActive = 1;
|
|
|
|
static final int SERVICE_STARTED = 0;
|
|
static final int SERVICE_BOUND = 1;
|
|
static final int SERVICE_EXEC = 2;
|
|
static final int SERVICE_COUNT = 3;
|
|
|
|
int[] mDurationsTable;
|
|
int mDurationsTableSize;
|
|
|
|
int mStartedCount;
|
|
public int mStartedState = STATE_NOTHING;
|
|
long mStartedStartTime;
|
|
|
|
int mBoundCount;
|
|
public int mBoundState = STATE_NOTHING;
|
|
long mBoundStartTime;
|
|
|
|
int mExecCount;
|
|
public int mExecState = STATE_NOTHING;
|
|
long mExecStartTime;
|
|
|
|
public ServiceState(ProcessStats processStats, String pkg, ProcessState proc) {
|
|
mProcessStats = processStats;
|
|
mPackage = pkg;
|
|
mProc = proc;
|
|
}
|
|
|
|
public void makeActive() {
|
|
mActive++;
|
|
}
|
|
|
|
public void makeInactive() {
|
|
/*
|
|
RuntimeException here = new RuntimeException("here");
|
|
here.fillInStackTrace();
|
|
Slog.i(TAG, "Making " + this + " inactive", here);
|
|
*/
|
|
mActive--;
|
|
}
|
|
|
|
public boolean isActive() {
|
|
return mActive > 0;
|
|
}
|
|
|
|
void resetSafely(long now) {
|
|
mDurationsTable = null;
|
|
mDurationsTableSize = 0;
|
|
mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0;
|
|
mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0;
|
|
mExecCount = mExecState != STATE_NOTHING ? 1 : 0;
|
|
mStartedStartTime = mBoundStartTime = mExecStartTime = now;
|
|
}
|
|
|
|
void writeToParcel(Parcel out, long now) {
|
|
if (mStartedState != STATE_NOTHING) {
|
|
addStateTime(SERVICE_STARTED, mStartedState, now - mStartedStartTime);
|
|
mStartedStartTime = now;
|
|
}
|
|
if (mBoundState != STATE_NOTHING) {
|
|
addStateTime(SERVICE_BOUND, mBoundState, now - mBoundStartTime);
|
|
mBoundStartTime = now;
|
|
}
|
|
if (mExecState != STATE_NOTHING) {
|
|
addStateTime(SERVICE_EXEC, mExecState, now - mExecStartTime);
|
|
mExecStartTime = now;
|
|
}
|
|
out.writeInt(mDurationsTableSize);
|
|
for (int i=0; i<mDurationsTableSize; i++) {
|
|
if (DEBUG) Slog.i(TAG, "Writing service in " + mPackage + " dur #" + i + ": "
|
|
+ printLongOffset(mDurationsTable[i]));
|
|
out.writeInt(mDurationsTable[i]);
|
|
}
|
|
out.writeInt(mStartedCount);
|
|
out.writeInt(mBoundCount);
|
|
out.writeInt(mExecCount);
|
|
}
|
|
|
|
boolean readFromParcel(Parcel in) {
|
|
if (DEBUG) Slog.d(TAG, "Reading durations table...");
|
|
mDurationsTable = mProcessStats.readTableFromParcel(in, mPackage, "service");
|
|
if (mDurationsTable == BAD_TABLE) {
|
|
return false;
|
|
}
|
|
mStartedCount = in.readInt();
|
|
mBoundCount = in.readInt();
|
|
mExecCount = in.readInt();
|
|
return true;
|
|
}
|
|
|
|
void addStateTime(int opType, int memFactor, long time) {
|
|
if (time > 0) {
|
|
int state = opType + (memFactor*SERVICE_COUNT);
|
|
int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
|
|
int off;
|
|
if (idx >= 0) {
|
|
off = mDurationsTable[idx];
|
|
} else {
|
|
mProcessStats.mAddLongTable = mDurationsTable;
|
|
mProcessStats.mAddLongTableSize = mDurationsTableSize;
|
|
off = mProcessStats.addLongData(~idx, state, 1);
|
|
mDurationsTable = mProcessStats.mAddLongTable;
|
|
mDurationsTableSize = mProcessStats.mAddLongTableSize;
|
|
}
|
|
long[] longs = mProcessStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
|
|
longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += time;
|
|
}
|
|
}
|
|
|
|
public void setStarted(boolean started, int memFactor, long now) {
|
|
if (mActive <= 0) {
|
|
throw new IllegalStateException("Service " + this + " has mActive=" + mActive);
|
|
}
|
|
int state = started ? memFactor : STATE_NOTHING;
|
|
if (mStartedState != state) {
|
|
if (mStartedState != STATE_NOTHING) {
|
|
addStateTime(SERVICE_STARTED, mStartedState, now - mStartedStartTime);
|
|
} else if (started) {
|
|
mStartedCount++;
|
|
}
|
|
mStartedState = state;
|
|
mStartedStartTime = now;
|
|
if (mProc != null) {
|
|
mProc = mProc.pullFixedProc(mPackage);
|
|
if (started) {
|
|
mProc.incStartedServices(memFactor, now);
|
|
} else {
|
|
mProc.decStartedServices(memFactor, now);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setBound(boolean bound, int memFactor, long now) {
|
|
if (mActive <= 0) {
|
|
throw new IllegalStateException("Service " + this + " has mActive=" + mActive);
|
|
}
|
|
int state = bound ? memFactor : STATE_NOTHING;
|
|
if (mBoundState != state) {
|
|
if (mBoundState != STATE_NOTHING) {
|
|
addStateTime(SERVICE_BOUND, mBoundState, now - mBoundStartTime);
|
|
} else if (bound) {
|
|
mBoundCount++;
|
|
}
|
|
mBoundState = state;
|
|
mBoundStartTime = now;
|
|
}
|
|
}
|
|
|
|
public void setExecuting(boolean executing, int memFactor, long now) {
|
|
if (mActive <= 0) {
|
|
throw new IllegalStateException("Service " + this + " has mActive=" + mActive);
|
|
}
|
|
int state = executing ? memFactor : STATE_NOTHING;
|
|
if (mExecState != state) {
|
|
if (mExecState != STATE_NOTHING) {
|
|
addStateTime(SERVICE_EXEC, mExecState, now - mExecStartTime);
|
|
} else if (executing) {
|
|
mExecCount++;
|
|
}
|
|
mExecState = state;
|
|
mExecStartTime = now;
|
|
}
|
|
}
|
|
|
|
long getStartDuration(int opType, int memFactor, long now) {
|
|
switch (opType) {
|
|
case SERVICE_STARTED:
|
|
return getDuration(opType, mStartedState, mStartedStartTime, memFactor, now);
|
|
case SERVICE_BOUND:
|
|
return getDuration(opType, mBoundState, mBoundStartTime, memFactor, now);
|
|
case SERVICE_EXEC:
|
|
return getDuration(opType, mExecState, mExecStartTime, memFactor, now);
|
|
default:
|
|
throw new IllegalArgumentException("Bad opType: " + opType);
|
|
}
|
|
}
|
|
|
|
|
|
private long getDuration(int opType, int curState, long startTime, int memFactor,
|
|
long now) {
|
|
int state = opType + (memFactor*SERVICE_COUNT);
|
|
int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
|
|
long time = idx >= 0 ? mProcessStats.getLong(mDurationsTable[idx], 0) : 0;
|
|
if (curState == memFactor) {
|
|
time += now - startTime;
|
|
}
|
|
return time;
|
|
}
|
|
}
|
|
|
|
public static final class PackageState {
|
|
public final ArrayMap<String, ProcessState> mProcesses
|
|
= new ArrayMap<String, ProcessState>();
|
|
public final ArrayMap<String, ServiceState> mServices
|
|
= new ArrayMap<String, ServiceState>();
|
|
final int mUid;
|
|
|
|
public PackageState(int uid) {
|
|
mUid = uid;
|
|
}
|
|
}
|
|
|
|
static final class ProcessDataCollection {
|
|
final int[] screenStates;
|
|
final int[] memStates;
|
|
final int[] procStates;
|
|
|
|
long totalTime;
|
|
long numPss;
|
|
long minPss;
|
|
long avgPss;
|
|
long maxPss;
|
|
long minUss;
|
|
long avgUss;
|
|
long maxUss;
|
|
|
|
ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) {
|
|
screenStates = _screenStates;
|
|
memStates = _memStates;
|
|
procStates = _procStates;
|
|
}
|
|
|
|
void print(PrintWriter pw, long overallTime, boolean full) {
|
|
printPercent(pw, (double) totalTime / (double) overallTime);
|
|
if (numPss > 0) {
|
|
pw.print(" (");
|
|
printSizeValue(pw, minPss * 1024);
|
|
pw.print("-");
|
|
printSizeValue(pw, avgPss * 1024);
|
|
pw.print("-");
|
|
printSizeValue(pw, maxPss * 1024);
|
|
pw.print("/");
|
|
printSizeValue(pw, minUss * 1024);
|
|
pw.print("-");
|
|
printSizeValue(pw, avgUss * 1024);
|
|
pw.print("-");
|
|
printSizeValue(pw, maxUss * 1024);
|
|
if (full) {
|
|
pw.print(" over ");
|
|
pw.print(numPss);
|
|
}
|
|
pw.print(")");
|
|
}
|
|
}
|
|
}
|
|
}
|