Don't kill processes for excessive wake lock use, even if they are in the background, as long as they have running services. Also fix some problems with this, such as not noting the kill in battery stats. And add killing of processes for cpu usage as well, along with some optimizations to computing CPU usage. And fix BatteryWaster to be better behaving for testing these cases. Add new "monitor" command to am to watch as the activity manager does stuff (so we can catch things at the point of ANR). Finally some miscellaneous debug output for the stuff here, as well as in progress debugging of an ANR. Change-Id: Ib32f55ca50fb7486b4be4eb5e695f8f60c882cd1
845 lines
30 KiB
Java
845 lines
30 KiB
Java
/*
|
|
* Copyright (C) 2007 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;
|
|
|
|
import static android.os.Process.*;
|
|
|
|
import android.os.Process;
|
|
import android.os.SystemClock;
|
|
import android.util.Config;
|
|
import android.util.Slog;
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.PrintWriter;
|
|
import java.io.StringWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.StringTokenizer;
|
|
|
|
public class ProcessStats {
|
|
private static final String TAG = "ProcessStats";
|
|
private static final boolean DEBUG = false;
|
|
private static final boolean localLOGV = DEBUG || Config.LOGV;
|
|
|
|
private static final int[] PROCESS_STATS_FORMAT = new int[] {
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM|PROC_PARENS,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 9: minor faults
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 11: major faults
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime
|
|
PROC_SPACE_TERM|PROC_OUT_LONG // 14: stime
|
|
};
|
|
|
|
static final int PROCESS_STAT_MINOR_FAULTS = 0;
|
|
static final int PROCESS_STAT_MAJOR_FAULTS = 1;
|
|
static final int PROCESS_STAT_UTIME = 2;
|
|
static final int PROCESS_STAT_STIME = 3;
|
|
|
|
/** Stores user time and system time in 100ths of a second. */
|
|
private final long[] mProcessStatsData = new long[4];
|
|
/** Stores user time and system time in 100ths of a second. */
|
|
private final long[] mSinglePidStatsData = new long[4];
|
|
|
|
private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM|PROC_PARENS|PROC_OUT_STRING, // 1: name
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 9: minor faults
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 11: major faults
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 14: stime
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM,
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 21: vsize
|
|
};
|
|
|
|
static final int PROCESS_FULL_STAT_MINOR_FAULTS = 1;
|
|
static final int PROCESS_FULL_STAT_MAJOR_FAULTS = 2;
|
|
static final int PROCESS_FULL_STAT_UTIME = 3;
|
|
static final int PROCESS_FULL_STAT_STIME = 4;
|
|
static final int PROCESS_FULL_STAT_VSIZE = 5;
|
|
|
|
private final String[] mProcessFullStatsStringData = new String[6];
|
|
private final long[] mProcessFullStatsData = new long[6];
|
|
|
|
private static final int[] SYSTEM_CPU_FORMAT = new int[] {
|
|
PROC_SPACE_TERM|PROC_COMBINE,
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 1: user time
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 2: nice time
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 3: sys time
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 4: idle time
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 5: iowait time
|
|
PROC_SPACE_TERM|PROC_OUT_LONG, // 6: irq time
|
|
PROC_SPACE_TERM|PROC_OUT_LONG // 7: softirq time
|
|
};
|
|
|
|
private final long[] mSystemCpuData = new long[7];
|
|
|
|
private static final int[] LOAD_AVERAGE_FORMAT = new int[] {
|
|
PROC_SPACE_TERM|PROC_OUT_FLOAT, // 0: 1 min
|
|
PROC_SPACE_TERM|PROC_OUT_FLOAT, // 1: 5 mins
|
|
PROC_SPACE_TERM|PROC_OUT_FLOAT // 2: 15 mins
|
|
};
|
|
|
|
private final float[] mLoadAverageData = new float[3];
|
|
|
|
private final boolean mIncludeThreads;
|
|
|
|
private float mLoad1 = 0;
|
|
private float mLoad5 = 0;
|
|
private float mLoad15 = 0;
|
|
|
|
private long mCurrentSampleTime;
|
|
private long mLastSampleTime;
|
|
|
|
private long mCurrentSampleRealTime;
|
|
private long mLastSampleRealTime;
|
|
|
|
private long mBaseUserTime;
|
|
private long mBaseSystemTime;
|
|
private long mBaseIoWaitTime;
|
|
private long mBaseIrqTime;
|
|
private long mBaseSoftIrqTime;
|
|
private long mBaseIdleTime;
|
|
private int mRelUserTime;
|
|
private int mRelSystemTime;
|
|
private int mRelIoWaitTime;
|
|
private int mRelIrqTime;
|
|
private int mRelSoftIrqTime;
|
|
private int mRelIdleTime;
|
|
|
|
private int[] mCurPids;
|
|
private int[] mCurThreadPids;
|
|
|
|
private final ArrayList<Stats> mProcStats = new ArrayList<Stats>();
|
|
private final ArrayList<Stats> mWorkingProcs = new ArrayList<Stats>();
|
|
private boolean mWorkingProcsSorted;
|
|
|
|
private boolean mFirst = true;
|
|
|
|
private byte[] mBuffer = new byte[256];
|
|
|
|
/**
|
|
* The time in microseconds that the CPU has been running at each speed.
|
|
*/
|
|
private long[] mCpuSpeedTimes;
|
|
|
|
/**
|
|
* The relative time in microseconds that the CPU has been running at each speed.
|
|
*/
|
|
private long[] mRelCpuSpeedTimes;
|
|
|
|
/**
|
|
* The different speeds that the CPU can be running at.
|
|
*/
|
|
private long[] mCpuSpeeds;
|
|
|
|
public static class Stats {
|
|
public final int pid;
|
|
final String statFile;
|
|
final String cmdlineFile;
|
|
final String threadsDir;
|
|
final ArrayList<Stats> threadStats;
|
|
final ArrayList<Stats> workingThreads;
|
|
|
|
public boolean interesting;
|
|
|
|
public String baseName;
|
|
public String name;
|
|
int nameWidth;
|
|
|
|
public long base_uptime;
|
|
public long rel_uptime;
|
|
|
|
public long base_utime;
|
|
public long base_stime;
|
|
public int rel_utime;
|
|
public int rel_stime;
|
|
|
|
public long base_minfaults;
|
|
public long base_majfaults;
|
|
public int rel_minfaults;
|
|
public int rel_majfaults;
|
|
|
|
public boolean active;
|
|
public boolean working;
|
|
public boolean added;
|
|
public boolean removed;
|
|
|
|
Stats(int _pid, int parentPid, boolean includeThreads) {
|
|
pid = _pid;
|
|
if (parentPid < 0) {
|
|
final File procDir = new File("/proc", Integer.toString(pid));
|
|
statFile = new File(procDir, "stat").toString();
|
|
cmdlineFile = new File(procDir, "cmdline").toString();
|
|
threadsDir = (new File(procDir, "task")).toString();
|
|
if (includeThreads) {
|
|
threadStats = new ArrayList<Stats>();
|
|
workingThreads = new ArrayList<Stats>();
|
|
} else {
|
|
threadStats = null;
|
|
workingThreads = null;
|
|
}
|
|
} else {
|
|
final File procDir = new File("/proc", Integer.toString(
|
|
parentPid));
|
|
final File taskDir = new File(
|
|
new File(procDir, "task"), Integer.toString(pid));
|
|
statFile = new File(taskDir, "stat").toString();
|
|
cmdlineFile = null;
|
|
threadsDir = null;
|
|
threadStats = null;
|
|
workingThreads = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private final static Comparator<Stats> sLoadComparator = new Comparator<Stats>() {
|
|
public final int
|
|
compare(Stats sta, Stats stb) {
|
|
int ta = sta.rel_utime + sta.rel_stime;
|
|
int tb = stb.rel_utime + stb.rel_stime;
|
|
if (ta != tb) {
|
|
return ta > tb ? -1 : 1;
|
|
}
|
|
if (sta.added != stb.added) {
|
|
return sta.added ? -1 : 1;
|
|
}
|
|
if (sta.removed != stb.removed) {
|
|
return sta.added ? -1 : 1;
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
|
|
public ProcessStats(boolean includeThreads) {
|
|
mIncludeThreads = includeThreads;
|
|
}
|
|
|
|
public void onLoadChanged(float load1, float load5, float load15) {
|
|
}
|
|
|
|
public int onMeasureProcessName(String name) {
|
|
return 0;
|
|
}
|
|
|
|
public void init() {
|
|
if (DEBUG) Slog.v(TAG, "Init: " + this);
|
|
mFirst = true;
|
|
update();
|
|
}
|
|
|
|
public void update() {
|
|
if (DEBUG) Slog.v(TAG, "Update: " + this);
|
|
mLastSampleTime = mCurrentSampleTime;
|
|
mCurrentSampleTime = SystemClock.uptimeMillis();
|
|
mLastSampleRealTime = mCurrentSampleRealTime;
|
|
mCurrentSampleRealTime = SystemClock.elapsedRealtime();
|
|
|
|
final long[] sysCpu = mSystemCpuData;
|
|
if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT,
|
|
null, sysCpu, null)) {
|
|
// Total user time is user + nice time.
|
|
final long usertime = sysCpu[0]+sysCpu[1];
|
|
// Total system time is simply system time.
|
|
final long systemtime = sysCpu[2];
|
|
// Total idle time is simply idle time.
|
|
final long idletime = sysCpu[3];
|
|
// Total irq time is iowait + irq + softirq time.
|
|
final long iowaittime = sysCpu[4];
|
|
final long irqtime = sysCpu[5];
|
|
final long softirqtime = sysCpu[6];
|
|
|
|
mRelUserTime = (int)(usertime - mBaseUserTime);
|
|
mRelSystemTime = (int)(systemtime - mBaseSystemTime);
|
|
mRelIoWaitTime = (int)(iowaittime - mBaseIoWaitTime);
|
|
mRelIrqTime = (int)(irqtime - mBaseIrqTime);
|
|
mRelSoftIrqTime = (int)(softirqtime - mBaseSoftIrqTime);
|
|
mRelIdleTime = (int)(idletime - mBaseIdleTime);
|
|
|
|
if (DEBUG) {
|
|
Slog.i("Load", "Total U:" + sysCpu[0] + " N:" + sysCpu[1]
|
|
+ " S:" + sysCpu[2] + " I:" + sysCpu[3]
|
|
+ " W:" + sysCpu[4] + " Q:" + sysCpu[5]
|
|
+ " O:" + sysCpu[6]);
|
|
Slog.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime
|
|
+ " I:" + mRelIdleTime + " Q:" + mRelIrqTime);
|
|
}
|
|
|
|
mBaseUserTime = usertime;
|
|
mBaseSystemTime = systemtime;
|
|
mBaseIoWaitTime = iowaittime;
|
|
mBaseIrqTime = irqtime;
|
|
mBaseSoftIrqTime = softirqtime;
|
|
mBaseIdleTime = idletime;
|
|
}
|
|
|
|
mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats);
|
|
|
|
final float[] loadAverages = mLoadAverageData;
|
|
if (Process.readProcFile("/proc/loadavg", LOAD_AVERAGE_FORMAT,
|
|
null, null, loadAverages)) {
|
|
float load1 = loadAverages[0];
|
|
float load5 = loadAverages[1];
|
|
float load15 = loadAverages[2];
|
|
if (load1 != mLoad1 || load5 != mLoad5 || load15 != mLoad15) {
|
|
mLoad1 = load1;
|
|
mLoad5 = load5;
|
|
mLoad15 = load15;
|
|
onLoadChanged(load1, load5, load15);
|
|
}
|
|
}
|
|
|
|
if (DEBUG) Slog.i(TAG, "*** TIME TO COLLECT STATS: "
|
|
+ (SystemClock.uptimeMillis()-mCurrentSampleTime));
|
|
|
|
mWorkingProcsSorted = false;
|
|
mFirst = false;
|
|
}
|
|
|
|
private int[] collectStats(String statsFile, int parentPid, boolean first,
|
|
int[] curPids, ArrayList<Stats> allProcs) {
|
|
|
|
int[] pids = Process.getPids(statsFile, curPids);
|
|
int NP = (pids == null) ? 0 : pids.length;
|
|
int NS = allProcs.size();
|
|
int curStatsIndex = 0;
|
|
for (int i=0; i<NP; i++) {
|
|
int pid = pids[i];
|
|
if (pid < 0) {
|
|
NP = pid;
|
|
break;
|
|
}
|
|
Stats st = curStatsIndex < NS ? allProcs.get(curStatsIndex) : null;
|
|
|
|
if (st != null && st.pid == pid) {
|
|
// Update an existing process...
|
|
st.added = false;
|
|
st.working = false;
|
|
curStatsIndex++;
|
|
if (DEBUG) Slog.v(TAG, "Existing "
|
|
+ (parentPid < 0 ? "process" : "thread")
|
|
+ " pid " + pid + ": " + st);
|
|
|
|
if (st.interesting) {
|
|
final long uptime = SystemClock.uptimeMillis();
|
|
|
|
final long[] procStats = mProcessStatsData;
|
|
if (!Process.readProcFile(st.statFile.toString(),
|
|
PROCESS_STATS_FORMAT, null, procStats, null)) {
|
|
continue;
|
|
}
|
|
|
|
final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS];
|
|
final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS];
|
|
final long utime = procStats[PROCESS_STAT_UTIME];
|
|
final long stime = procStats[PROCESS_STAT_STIME];
|
|
|
|
if (utime == st.base_utime && stime == st.base_stime) {
|
|
st.rel_utime = 0;
|
|
st.rel_stime = 0;
|
|
st.rel_minfaults = 0;
|
|
st.rel_majfaults = 0;
|
|
if (st.active) {
|
|
st.active = false;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!st.active) {
|
|
st.active = true;
|
|
}
|
|
|
|
if (parentPid < 0) {
|
|
getName(st, st.cmdlineFile);
|
|
if (st.threadStats != null) {
|
|
mCurThreadPids = collectStats(st.threadsDir, pid, false,
|
|
mCurThreadPids, st.threadStats);
|
|
}
|
|
}
|
|
|
|
if (DEBUG) Slog.v("Load", "Stats changed " + st.name + " pid=" + st.pid
|
|
+ " utime=" + utime + "-" + st.base_utime
|
|
+ " stime=" + stime + "-" + st.base_stime
|
|
+ " minfaults=" + minfaults + "-" + st.base_minfaults
|
|
+ " majfaults=" + majfaults + "-" + st.base_majfaults);
|
|
|
|
st.rel_uptime = uptime - st.base_uptime;
|
|
st.base_uptime = uptime;
|
|
st.rel_utime = (int)(utime - st.base_utime);
|
|
st.rel_stime = (int)(stime - st.base_stime);
|
|
st.base_utime = utime;
|
|
st.base_stime = stime;
|
|
st.rel_minfaults = (int)(minfaults - st.base_minfaults);
|
|
st.rel_majfaults = (int)(majfaults - st.base_majfaults);
|
|
st.base_minfaults = minfaults;
|
|
st.base_majfaults = majfaults;
|
|
st.working = true;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (st == null || st.pid > pid) {
|
|
// We have a new process!
|
|
st = new Stats(pid, parentPid, mIncludeThreads);
|
|
allProcs.add(curStatsIndex, st);
|
|
curStatsIndex++;
|
|
NS++;
|
|
if (DEBUG) Slog.v(TAG, "New "
|
|
+ (parentPid < 0 ? "process" : "thread")
|
|
+ " pid " + pid + ": " + st);
|
|
|
|
final String[] procStatsString = mProcessFullStatsStringData;
|
|
final long[] procStats = mProcessFullStatsData;
|
|
st.base_uptime = SystemClock.uptimeMillis();
|
|
if (Process.readProcFile(st.statFile.toString(),
|
|
PROCESS_FULL_STATS_FORMAT, procStatsString,
|
|
procStats, null)) {
|
|
// This is a possible way to filter out processes that
|
|
// are actually kernel threads... do we want to? Some
|
|
// of them do use CPU, but there can be a *lot* that are
|
|
// not doing anything.
|
|
if (true || procStats[PROCESS_FULL_STAT_VSIZE] != 0) {
|
|
st.interesting = true;
|
|
st.baseName = procStatsString[0];
|
|
st.base_minfaults = procStats[PROCESS_FULL_STAT_MINOR_FAULTS];
|
|
st.base_majfaults = procStats[PROCESS_FULL_STAT_MAJOR_FAULTS];
|
|
st.base_utime = procStats[PROCESS_FULL_STAT_UTIME];
|
|
st.base_stime = procStats[PROCESS_FULL_STAT_STIME];
|
|
} else {
|
|
Slog.i(TAG, "Skipping kernel process pid " + pid
|
|
+ " name " + procStatsString[0]);
|
|
st.baseName = procStatsString[0];
|
|
}
|
|
} else {
|
|
Slog.w(TAG, "Skipping unknown process pid " + pid);
|
|
st.baseName = "<unknown>";
|
|
st.base_utime = st.base_stime = 0;
|
|
st.base_minfaults = st.base_majfaults = 0;
|
|
}
|
|
|
|
if (parentPid < 0) {
|
|
getName(st, st.cmdlineFile);
|
|
if (st.threadStats != null) {
|
|
mCurThreadPids = collectStats(st.threadsDir, pid, true,
|
|
mCurThreadPids, st.threadStats);
|
|
}
|
|
} else if (st.interesting) {
|
|
st.name = st.baseName;
|
|
st.nameWidth = onMeasureProcessName(st.name);
|
|
}
|
|
|
|
if (DEBUG) Slog.v("Load", "Stats added " + st.name + " pid=" + st.pid
|
|
+ " utime=" + st.base_utime + " stime=" + st.base_stime
|
|
+ " minfaults=" + st.base_minfaults + " majfaults=" + st.base_majfaults);
|
|
|
|
st.rel_utime = 0;
|
|
st.rel_stime = 0;
|
|
st.rel_minfaults = 0;
|
|
st.rel_majfaults = 0;
|
|
st.added = true;
|
|
if (!first && st.interesting) {
|
|
st.working = true;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// This process has gone away!
|
|
st.rel_utime = 0;
|
|
st.rel_stime = 0;
|
|
st.rel_minfaults = 0;
|
|
st.rel_majfaults = 0;
|
|
st.removed = true;
|
|
st.working = true;
|
|
allProcs.remove(curStatsIndex);
|
|
NS--;
|
|
if (DEBUG) Slog.v(TAG, "Removed "
|
|
+ (parentPid < 0 ? "process" : "thread")
|
|
+ " pid " + pid + ": " + st);
|
|
// Decrement the loop counter so that we process the current pid
|
|
// again the next time through the loop.
|
|
i--;
|
|
continue;
|
|
}
|
|
|
|
while (curStatsIndex < NS) {
|
|
// This process has gone away!
|
|
final Stats st = allProcs.get(curStatsIndex);
|
|
st.rel_utime = 0;
|
|
st.rel_stime = 0;
|
|
st.rel_minfaults = 0;
|
|
st.rel_majfaults = 0;
|
|
st.removed = true;
|
|
st.working = true;
|
|
allProcs.remove(curStatsIndex);
|
|
NS--;
|
|
if (localLOGV) Slog.v(TAG, "Removed pid " + st.pid + ": " + st);
|
|
}
|
|
|
|
return pids;
|
|
}
|
|
|
|
public long getCpuTimeForPid(int pid) {
|
|
final String statFile = "/proc/" + pid + "/stat";
|
|
final long[] statsData = mSinglePidStatsData;
|
|
if (Process.readProcFile(statFile, PROCESS_STATS_FORMAT,
|
|
null, statsData, null)) {
|
|
long time = statsData[PROCESS_STAT_UTIME]
|
|
+ statsData[PROCESS_STAT_STIME];
|
|
return time;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the times spent at each CPU speed, since the last call to this method. If this
|
|
* is the first time, it will return 1 for each value.
|
|
* @return relative times spent at different speed steps.
|
|
*/
|
|
public long[] getLastCpuSpeedTimes() {
|
|
if (mCpuSpeedTimes == null) {
|
|
mCpuSpeedTimes = getCpuSpeedTimes(null);
|
|
mRelCpuSpeedTimes = new long[mCpuSpeedTimes.length];
|
|
for (int i = 0; i < mCpuSpeedTimes.length; i++) {
|
|
mRelCpuSpeedTimes[i] = 1; // Initialize
|
|
}
|
|
} else {
|
|
getCpuSpeedTimes(mRelCpuSpeedTimes);
|
|
for (int i = 0; i < mCpuSpeedTimes.length; i++) {
|
|
long temp = mRelCpuSpeedTimes[i];
|
|
mRelCpuSpeedTimes[i] -= mCpuSpeedTimes[i];
|
|
mCpuSpeedTimes[i] = temp;
|
|
}
|
|
}
|
|
return mRelCpuSpeedTimes;
|
|
}
|
|
|
|
private long[] getCpuSpeedTimes(long[] out) {
|
|
long[] tempTimes = out;
|
|
long[] tempSpeeds = mCpuSpeeds;
|
|
final int MAX_SPEEDS = 20;
|
|
if (out == null) {
|
|
tempTimes = new long[MAX_SPEEDS]; // Hopefully no more than that
|
|
tempSpeeds = new long[MAX_SPEEDS];
|
|
}
|
|
int speed = 0;
|
|
String file = readFile("/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state", '\0');
|
|
// Note: file may be null on kernels without cpufreq (i.e. the emulator's)
|
|
if (file != null) {
|
|
StringTokenizer st = new StringTokenizer(file, "\n ");
|
|
while (st.hasMoreElements()) {
|
|
String token = st.nextToken();
|
|
try {
|
|
long val = Long.parseLong(token);
|
|
tempSpeeds[speed] = val;
|
|
token = st.nextToken();
|
|
val = Long.parseLong(token);
|
|
tempTimes[speed] = val;
|
|
speed++;
|
|
if (speed == MAX_SPEEDS) break; // No more
|
|
if (localLOGV && out == null) {
|
|
Slog.v(TAG, "First time : Speed/Time = " + tempSpeeds[speed - 1]
|
|
+ "\t" + tempTimes[speed - 1]);
|
|
}
|
|
} catch (NumberFormatException nfe) {
|
|
Slog.i(TAG, "Unable to parse time_in_state");
|
|
}
|
|
}
|
|
}
|
|
if (out == null) {
|
|
out = new long[speed];
|
|
mCpuSpeeds = new long[speed];
|
|
System.arraycopy(tempSpeeds, 0, mCpuSpeeds, 0, speed);
|
|
System.arraycopy(tempTimes, 0, out, 0, speed);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
final public int getLastUserTime() {
|
|
return mRelUserTime;
|
|
}
|
|
|
|
final public int getLastSystemTime() {
|
|
return mRelSystemTime;
|
|
}
|
|
|
|
final public int getLastIoWaitTime() {
|
|
return mRelIoWaitTime;
|
|
}
|
|
|
|
final public int getLastIrqTime() {
|
|
return mRelIrqTime;
|
|
}
|
|
|
|
final public int getLastSoftIrqTime() {
|
|
return mRelSoftIrqTime;
|
|
}
|
|
|
|
final public int getLastIdleTime() {
|
|
return mRelIdleTime;
|
|
}
|
|
|
|
final public float getTotalCpuPercent() {
|
|
return ((float)(mRelUserTime+mRelSystemTime+mRelIrqTime)*100)
|
|
/ (mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime);
|
|
}
|
|
|
|
final void buildWorkingProcs() {
|
|
if (!mWorkingProcsSorted) {
|
|
mWorkingProcs.clear();
|
|
final int N = mProcStats.size();
|
|
for (int i=0; i<N; i++) {
|
|
Stats stats = mProcStats.get(i);
|
|
if (stats.working) {
|
|
mWorkingProcs.add(stats);
|
|
if (stats.threadStats != null && stats.threadStats.size() > 1) {
|
|
stats.workingThreads.clear();
|
|
final int M = stats.threadStats.size();
|
|
for (int j=0; j<M; j++) {
|
|
Stats tstats = stats.threadStats.get(j);
|
|
if (tstats.working) {
|
|
stats.workingThreads.add(tstats);
|
|
}
|
|
}
|
|
Collections.sort(stats.workingThreads, sLoadComparator);
|
|
}
|
|
}
|
|
}
|
|
Collections.sort(mWorkingProcs, sLoadComparator);
|
|
mWorkingProcsSorted = true;
|
|
}
|
|
}
|
|
|
|
final public int countStats() {
|
|
return mProcStats.size();
|
|
}
|
|
|
|
final public Stats getStats(int index) {
|
|
return mProcStats.get(index);
|
|
}
|
|
|
|
final public int countWorkingStats() {
|
|
buildWorkingProcs();
|
|
return mWorkingProcs.size();
|
|
}
|
|
|
|
final public Stats getWorkingStats(int index) {
|
|
return mWorkingProcs.get(index);
|
|
}
|
|
|
|
final public String printCurrentLoad() {
|
|
StringWriter sw = new StringWriter();
|
|
PrintWriter pw = new PrintWriter(sw);
|
|
pw.print("Load: ");
|
|
pw.print(mLoad1);
|
|
pw.print(" / ");
|
|
pw.print(mLoad5);
|
|
pw.print(" / ");
|
|
pw.println(mLoad15);
|
|
return sw.toString();
|
|
}
|
|
|
|
final public String printCurrentState(long now) {
|
|
buildWorkingProcs();
|
|
|
|
StringWriter sw = new StringWriter();
|
|
PrintWriter pw = new PrintWriter(sw);
|
|
|
|
pw.print("CPU usage from ");
|
|
if (now > mLastSampleTime) {
|
|
pw.print(now-mLastSampleTime);
|
|
pw.print("ms to ");
|
|
pw.print(now-mCurrentSampleTime);
|
|
pw.print("ms ago");
|
|
} else {
|
|
pw.print(mLastSampleTime-now);
|
|
pw.print("ms to ");
|
|
pw.print(mCurrentSampleTime-now);
|
|
pw.print("ms later");
|
|
}
|
|
|
|
long sampleTime = mCurrentSampleTime - mLastSampleTime;
|
|
long sampleRealTime = mCurrentSampleRealTime - mLastSampleRealTime;
|
|
long percAwake = (sampleTime*100) / sampleRealTime;
|
|
if (percAwake != 100) {
|
|
pw.print(" with ");
|
|
pw.print(percAwake);
|
|
pw.print("% awake");
|
|
}
|
|
pw.println(":");
|
|
|
|
final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime
|
|
+ mRelIrqTime + mRelSoftIrqTime + mRelIdleTime;
|
|
|
|
if (DEBUG) Slog.i(TAG, "totalTime " + totalTime + " over sample time "
|
|
+ (mCurrentSampleTime-mLastSampleTime));
|
|
|
|
int N = mWorkingProcs.size();
|
|
for (int i=0; i<N; i++) {
|
|
Stats st = mWorkingProcs.get(i);
|
|
printProcessCPU(pw, st.added ? " +" : (st.removed ? " -": " "),
|
|
st.pid, st.name, (int)(st.rel_uptime+5)/10,
|
|
st.rel_utime, st.rel_stime, 0, 0, 0, st.rel_minfaults, st.rel_majfaults);
|
|
if (!st.removed && st.workingThreads != null) {
|
|
int M = st.workingThreads.size();
|
|
for (int j=0; j<M; j++) {
|
|
Stats tst = st.workingThreads.get(j);
|
|
printProcessCPU(pw,
|
|
tst.added ? " +" : (tst.removed ? " -": " "),
|
|
tst.pid, tst.name, (int)(st.rel_uptime+5)/10,
|
|
tst.rel_utime, tst.rel_stime, 0, 0, 0, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
printProcessCPU(pw, "", -1, "TOTAL", totalTime, mRelUserTime, mRelSystemTime,
|
|
mRelIoWaitTime, mRelIrqTime, mRelSoftIrqTime, 0, 0);
|
|
|
|
return sw.toString();
|
|
}
|
|
|
|
private void printRatio(PrintWriter pw, long numerator, long denominator) {
|
|
long thousands = (numerator*1000)/denominator;
|
|
long hundreds = thousands/10;
|
|
pw.print(hundreds);
|
|
if (hundreds < 10) {
|
|
long remainder = thousands - (hundreds*10);
|
|
if (remainder != 0) {
|
|
pw.print('.');
|
|
pw.print(remainder);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void printProcessCPU(PrintWriter pw, String prefix, int pid, String label,
|
|
int totalTime, int user, int system, int iowait, int irq, int softIrq,
|
|
int minFaults, int majFaults) {
|
|
pw.print(prefix);
|
|
if (totalTime == 0) totalTime = 1;
|
|
printRatio(pw, user+system+iowait+irq+softIrq, totalTime);
|
|
pw.print("% ");
|
|
if (pid >= 0) {
|
|
pw.print(pid);
|
|
pw.print("/");
|
|
}
|
|
pw.print(label);
|
|
pw.print(": ");
|
|
printRatio(pw, user, totalTime);
|
|
pw.print("% user + ");
|
|
printRatio(pw, system, totalTime);
|
|
pw.print("% kernel");
|
|
if (iowait > 0) {
|
|
pw.print(" + ");
|
|
printRatio(pw, iowait, totalTime);
|
|
pw.print("% iowait");
|
|
}
|
|
if (irq > 0) {
|
|
pw.print(" + ");
|
|
printRatio(pw, irq, totalTime);
|
|
pw.print("% irq");
|
|
}
|
|
if (softIrq > 0) {
|
|
pw.print(" + ");
|
|
printRatio(pw, softIrq, totalTime);
|
|
pw.print("% softirq");
|
|
}
|
|
if (minFaults > 0 || majFaults > 0) {
|
|
pw.print(" / faults:");
|
|
if (minFaults > 0) {
|
|
pw.print(" ");
|
|
pw.print(minFaults);
|
|
pw.print(" minor");
|
|
}
|
|
if (majFaults > 0) {
|
|
pw.print(" ");
|
|
pw.print(majFaults);
|
|
pw.print(" major");
|
|
}
|
|
}
|
|
pw.println();
|
|
}
|
|
|
|
private String readFile(String file, char endChar) {
|
|
try {
|
|
FileInputStream is = new FileInputStream(file);
|
|
int len = is.read(mBuffer);
|
|
is.close();
|
|
|
|
if (len > 0) {
|
|
int i;
|
|
for (i=0; i<len; i++) {
|
|
if (mBuffer[i] == endChar) {
|
|
break;
|
|
}
|
|
}
|
|
return new String(mBuffer, 0, 0, i);
|
|
}
|
|
} catch (java.io.FileNotFoundException e) {
|
|
} catch (java.io.IOException e) {
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void getName(Stats st, String cmdlineFile) {
|
|
String newName = st.name;
|
|
if (st.name == null || st.name.equals("app_process")
|
|
|| st.name.equals("<pre-initialized>")) {
|
|
String cmdName = readFile(cmdlineFile, '\0');
|
|
if (cmdName != null && cmdName.length() > 1) {
|
|
newName = cmdName;
|
|
int i = newName.lastIndexOf("/");
|
|
if (i > 0 && i < newName.length()-1) {
|
|
newName = newName.substring(i+1);
|
|
}
|
|
}
|
|
if (newName == null) {
|
|
newName = st.baseName;
|
|
}
|
|
}
|
|
if (st.name == null || !newName.equals(st.name)) {
|
|
st.name = newName;
|
|
st.nameWidth = onMeasureProcessName(st.name);
|
|
}
|
|
}
|
|
}
|
|
|