Files
frameworks_base/services/java/com/android/server/am/ProcessTracker.java
Dianne Hackborn 91268cf21e More work on process tracking.
Re-arranged code to be more flexible, now track
state of services, dump ordered list of running
processes while memory was critical and low.

Also rename battery stats service from "batteryinfo" to "batterystats".

Change-Id: I0f4f0c8d443c49d255cb84d0fc917e8ec18b152e
2013-06-14 17:30:17 -07:00

536 lines
21 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.server.am;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.SparseArray;
import android.util.TimeUtils;
import com.android.server.ProcessMap;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public final class ProcessTracker {
public static final int STATE_NOTHING = -1;
public static final int STATE_TOP = 0;
public static final int STATE_FOREGROUND = 1;
public static final int STATE_VISIBLE = 2;
public static final int STATE_PERCEPTIBLE = 3;
public static final int STATE_BACKUP = 4;
public static final int STATE_SERVICE = 5;
public static final int STATE_HOME = 6;
public static final int STATE_PREVIOUS = 7;
public static final int STATE_CACHED = 8;
public static final int STATE_COUNT = STATE_CACHED+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;
static String[] STATE_NAMES = new String[] {
"Top ", "Foreground ", "Visible ", "Perceptible", "Backup ",
"Service ", "Home ", "Previous ", "Cached "
};
public static final class ProcessState {
final String mPackage;
final int mUid;
final String mName;
final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT];
int mCurState = STATE_NOTHING;
long mStartTime;
long mTmpTotalTime;
public ProcessState(String pkg, int uid, String name) {
mPackage = pkg;
mUid = uid;
mName = name;
}
public void setState(int state, int memFactor, long now) {
if (state != STATE_NOTHING) {
state += memFactor*STATE_COUNT;
}
if (mCurState != state) {
if (mCurState != STATE_NOTHING) {
mDurations[mCurState] += now - mStartTime;
}
mCurState = state;
mStartTime = now;
}
}
}
public static final class ServiceState {
final long[] mStartedDurations = new long[ADJ_COUNT];
int mStartedCount;
int mStartedState = STATE_NOTHING;
long mStartedStartTime;
final long[] mBoundDurations = new long[ADJ_COUNT];
int mBoundCount;
int mBoundState = STATE_NOTHING;
long mBoundStartTime;
final long[] mExecDurations = new long[ADJ_COUNT];
int mExecCount;
int mExecState = STATE_NOTHING;
long mExecStartTime;
public void setStarted(boolean started, int memFactor, long now) {
int state = started ? memFactor : STATE_NOTHING;
if (mStartedState != state) {
if (mStartedState != STATE_NOTHING) {
mStartedDurations[mStartedState] += now - mStartedStartTime;
} else if (started) {
mStartedCount++;
}
mStartedState = state;
mStartedStartTime = now;
}
}
public void setBound(boolean bound, int memFactor, long now) {
int state = bound ? memFactor : STATE_NOTHING;
if (mBoundState != state) {
if (mBoundState != STATE_NOTHING) {
mBoundDurations[mBoundState] += now - mBoundStartTime;
} else if (bound) {
mBoundCount++;
}
mBoundState = state;
mBoundStartTime = now;
}
}
public void setExecuting(boolean executing, int memFactor, long now) {
int state = executing ? memFactor : STATE_NOTHING;
if (mExecState != state) {
if (mExecState != STATE_NOTHING) {
mExecDurations[mExecState] += now - mExecStartTime;
} else if (executing) {
mExecCount++;
}
mExecState = state;
mExecStartTime = now;
}
}
}
public static final class PackageState {
final ArrayMap<String, ProcessState> mProcesses = new ArrayMap<String, ProcessState>();
final ArrayMap<String, ServiceState> mServices = new ArrayMap<String, ServiceState>();
final int mUid;
public PackageState(int uid) {
mUid = uid;
}
}
static final class State {
final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>();
final long[] mMemFactorDurations = new long[ADJ_COUNT];
int mMemFactor = STATE_NOTHING;
long mStartTime;
}
final State mState = new State();
public ProcessTracker() {
}
private PackageState getPackageStateLocked(String packageName, int uid) {
PackageState as = mState.mPackages.get(packageName, uid);
if (as != null) {
return as;
}
as = new PackageState(uid);
mState.mPackages.put(packageName, uid, as);
return as;
}
public ProcessState getProcessStateLocked(String packageName, int uid, String processName) {
final PackageState as = getPackageStateLocked(packageName, uid);
ProcessState ps = as.mProcesses.get(processName);
if (ps != null) {
return ps;
}
ps = new ProcessState(packageName, uid, processName);
as.mProcesses.put(processName, ps);
return ps;
}
public ServiceState getServiceStateLocked(String packageName, int uid, String className) {
final PackageState as = getPackageStateLocked(packageName, uid);
ServiceState ss = as.mServices.get(className);
if (ss != null) {
return ss;
}
ss = new ServiceState();
as.mServices.put(className, ss);
return ss;
}
public boolean setMemFactor(int memFactor, boolean screenOn, long now) {
if (screenOn) {
memFactor += ADJ_SCREEN_ON;
}
if (memFactor != mState.mMemFactor) {
if (mState.mMemFactor != STATE_NOTHING) {
mState.mMemFactorDurations[mState.mMemFactor] += now - mState.mStartTime;
}
mState.mMemFactor = memFactor;
mState.mStartTime = now;
ArrayMap<String, SparseArray<PackageState>> pmap = mState.mPackages.getMap();
for (int i=0; i<pmap.size(); i++) {
SparseArray<PackageState> uids = pmap.valueAt(i);
for (int j=0; j<uids.size(); j++) {
PackageState pkg = uids.valueAt(j);
ArrayMap<String, ServiceState> services = pkg.mServices;
for (int k=0; k<services.size(); k++) {
ServiceState service = services.valueAt(k);
if (service.mStartedState != STATE_NOTHING) {
service.setStarted(true, memFactor, now);
}
if (service.mBoundState != STATE_NOTHING) {
service.setBound(true, memFactor, now);
}
}
}
}
return true;
}
return false;
}
public int getMemFactor() {
return mState.mMemFactor != STATE_NOTHING ? mState.mMemFactor : 0;
}
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 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 void 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;
running = " (running)";
}
if (time != 0) {
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.print(prefix);
printScreenLabel(pw, STATE_NOTHING);
pw.print("TOTAL: ");
TimeUtils.formatDuration(totalTime, pw);
pw.println();
}
}
long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates,
int[] procStates, long now) {
long totalTime = 0;
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.mDurations[bucket];
if (proc.mCurState == bucket) {
totalTime += now - proc.mStartTime;
}
}
}
}
proc.mTmpTotalTime = totalTime;
return totalTime;
}
ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates,
int[] procStates, long now) {
ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>();
ArrayMap<String, SparseArray<PackageState>> pmap = mState.mPackages.getMap();
for (int ip=0; ip<pmap.size(); ip++) {
SparseArray<PackageState> procs = pmap.valueAt(ip);
for (int iu=0; iu<procs.size(); iu++) {
PackageState state = procs.valueAt(iu);
for (int iproc=0; iproc<state.mProcesses.size(); iproc++) {
if (computeProcessTimeLocked(state.mProcesses.valueAt(iproc),
screenStates, memStates, procStates, now) > 0) {
outProcs.add(state.mProcesses.valueAt(iproc));
}
}
}
}
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;
}
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.mDurations[bucket];
String running = "";
if (proc.mCurState == bucket) {
time += now - proc.mStartTime;
running = " (running)";
}
totalTime += time;
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();
}
}
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.mPackage);
pw.print(" / ");
UserHandle.formatUid(pw, proc.mUid);
pw.print(" / ");
pw.print(proc.mName);
pw.println(":");
dumpProcessState(pw, innerPrefix, proc, screenStates, memStates, procStates, now);
}
}
void dumpFilteredProcesses(PrintWriter pw, String header, String prefix,
int[] screenStates, int[] memStates, int[] procStates, long now) {
ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
procStates, now);
if (procs.size() > 0) {
pw.println();
pw.println(header);
dumpProcessList(pw, prefix, procs, screenStates, memStates, procStates, now);
}
}
public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
final long now = SystemClock.uptimeMillis();
ArrayMap<String, SparseArray<PackageState>> pmap = mState.mPackages.getMap();
pw.println("Per-Package Process Stats:");
for (int ip=0; ip<pmap.size(); ip++) {
String procName = pmap.keyAt(ip);
SparseArray<PackageState> procs = pmap.valueAt(ip);
for (int iu=0; iu<procs.size(); iu++) {
int uid = procs.keyAt(iu);
PackageState state = procs.valueAt(iu);
pw.print(" * "); pw.print(procName); pw.print(" / ");
UserHandle.formatUid(pw, uid); pw.println(":");
for (int iproc=0; iproc<state.mProcesses.size(); iproc++) {
pw.print(" Process ");
pw.print(state.mProcesses.keyAt(iproc));
pw.println(":");
long totalTime = 0;
ProcessState proc = state.mProcesses.valueAt(iproc);
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++) {
for (int is=0; is<STATE_NAMES.length; is++) {
int bucket = is+(STATE_COUNT*(imem+iscreen));
long time = proc.mDurations[bucket];
String running = "";
if (proc.mCurState == bucket) {
time += now - proc.mStartTime;
running = " (running)";
}
if (time != 0) {
pw.print(" ");
printScreenLabel(pw, printedScreen != iscreen
? iscreen : STATE_NOTHING);
printedScreen = iscreen;
printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
printedMem = imem;
pw.print(STATE_NAMES[is]); pw.print(": ");
TimeUtils.formatDuration(time, pw); pw.println(running);
totalTime += time;
}
}
}
}
if (totalTime != 0) {
pw.print(" ");
printScreenLabel(pw, STATE_NOTHING);
printMemLabel(pw, STATE_NOTHING);
pw.print("TOTAL : ");
TimeUtils.formatDuration(totalTime, pw);
pw.println();
}
}
for (int isvc=0; isvc<state.mServices.size(); isvc++) {
pw.print(" Service ");
pw.print(state.mServices.keyAt(isvc));
pw.println(":");
ServiceState svc = state.mServices.valueAt(isvc);
if (svc.mStartedCount != 0) {
pw.print(" Started op count "); pw.print(svc.mStartedCount);
pw.println(":");
dumpSingleTime(pw, " ", svc.mStartedDurations, svc.mStartedState,
svc.mStartedStartTime, now);
}
if (svc.mBoundCount != 0) {
pw.print(" Bound op count "); pw.print(svc.mBoundCount);
pw.println(":");
dumpSingleTime(pw, " ", svc.mBoundDurations, svc.mBoundState,
svc.mBoundStartTime, now);
}
if (svc.mExecCount != 0) {
pw.print(" Executing op count "); pw.print(svc.mExecCount);
pw.println(":");
dumpSingleTime(pw, " ", svc.mExecDurations, svc.mExecState,
svc.mExecStartTime, now);
}
}
}
}
dumpFilteredProcesses(pw, "Processes running while critical mem:", " ",
new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
new int[] {ADJ_MEM_FACTOR_CRITICAL},
new int[] {STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE,
STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS},
now);
dumpFilteredProcesses(pw, "Processes running while low mem:", " ",
new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
new int[] {ADJ_MEM_FACTOR_LOW},
new int[] {STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE,
STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS},
now);
pw.println();
pw.println("Run time Stats:");
dumpSingleTime(pw, " ", mState.mMemFactorDurations, mState.mMemFactor,
mState.mStartTime, now);
}
}