summaryrefslogtreecommitdiff
path: root/services/java/com/android/server/ProcessStats.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/ProcessStats.java')
-rw-r--r--services/java/com/android/server/ProcessStats.java580
1 files changed, 580 insertions, 0 deletions
diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java
new file mode 100644
index 000000000000..55adabbfa7a0
--- /dev/null
+++ b/services/java/com/android/server/ProcessStats.java
@@ -0,0 +1,580 @@
+/*
+ * 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.Log;
+
+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;
+
+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_SPACE_TERM,
+ 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_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime
+ PROC_SPACE_TERM|PROC_OUT_LONG // 14: stime
+ };
+
+ private final long[] mProcessStatsData = new long[2];
+
+ 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_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime
+ PROC_SPACE_TERM|PROC_OUT_LONG // 14: stime
+ };
+
+ private final String[] mProcessFullStatsStringData = new String[3];
+ private final long[] mProcessFullStatsData = new long[3];
+
+ 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 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];
+
+ 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 String baseName;
+ public String name;
+ int nameWidth;
+
+ public long base_utime;
+ public long base_stime;
+ public int rel_utime;
+ public int rel_stime;
+
+ public boolean active;
+ 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() {
+ mFirst = true;
+ update();
+ }
+
+ public void update() {
+ mLastSampleTime = mCurrentSampleTime;
+ mCurrentSampleTime = SystemClock.uptimeMillis();
+
+ 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);
+ }
+ }
+
+ mCurPids = collectStats("/proc", -1, mFirst, mCurPids,
+ mProcStats, mWorkingProcs);
+ mFirst = false;
+
+ 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 (false) {
+ Log.i("Load", "Total U:" + sysCpu[0] + " N:" + sysCpu[1]
+ + " S:" + sysCpu[2] + " I:" + sysCpu[3]
+ + " W:" + sysCpu[4] + " Q:" + sysCpu[5]
+ + " O:" + sysCpu[6]);
+ Log.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime
+ + " I:" + mRelIdleTime + " Q:" + mRelIrqTime);
+ }
+
+ mBaseUserTime = usertime;
+ mBaseSystemTime = systemtime;
+ mBaseIoWaitTime = iowaittime;
+ mBaseIrqTime = irqtime;
+ mBaseSoftIrqTime = softirqtime;
+ mBaseIdleTime = idletime;
+ }
+
+ mWorkingProcsSorted = false;
+ mFirst = false;
+ }
+
+ private int[] collectStats(String statsFile, int parentPid, boolean first,
+ int[] curPids, ArrayList<Stats> allProcs,
+ ArrayList<Stats> workingProcs) {
+
+ workingProcs.clear();
+
+ 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;
+ curStatsIndex++;
+ if (localLOGV) Log.v(TAG, "Existing pid " + pid + ": " + st);
+
+ final long[] procStats = mProcessStatsData;
+ if (!Process.readProcFile(st.statFile.toString(),
+ PROCESS_STATS_FORMAT, null, procStats, null)) {
+ continue;
+ }
+
+ final long utime = procStats[0];
+ final long stime = procStats[1];
+
+ if (utime == st.base_utime && stime == st.base_stime) {
+ st.rel_utime = 0;
+ st.rel_stime = 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,
+ st.workingThreads);
+ }
+ }
+
+ st.rel_utime = (int)(utime - st.base_utime);
+ st.rel_stime = (int)(stime - st.base_stime);
+ st.base_utime = utime;
+ st.base_stime = stime;
+ //Log.i("Load", "Stats changed " + name + " pid=" + st.pid
+ // + " name=" + st.name + " utime=" + utime
+ // + " stime=" + stime);
+ workingProcs.add(st);
+ 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 (localLOGV) Log.v(TAG, "New pid " + pid + ": " + st);
+
+ final String[] procStatsString = mProcessFullStatsStringData;
+ final long[] procStats = mProcessFullStatsData;
+ if (Process.readProcFile(st.statFile.toString(),
+ PROCESS_FULL_STATS_FORMAT, procStatsString,
+ procStats, null)) {
+ st.baseName = parentPid < 0
+ ? procStatsString[0] : Integer.toString(pid);
+ st.base_utime = procStats[1];
+ st.base_stime = procStats[2];
+ } else {
+ st.baseName = "<unknown>";
+ st.base_utime = st.base_stime = 0;
+ }
+
+ if (parentPid < 0) {
+ getName(st, st.cmdlineFile);
+ } else {
+ st.name = st.baseName;
+ st.nameWidth = onMeasureProcessName(st.name);
+ if (st.threadStats != null) {
+ mCurThreadPids = collectStats(st.threadsDir, pid, true,
+ mCurThreadPids, st.threadStats,
+ st.workingThreads);
+ }
+ }
+
+ //Log.i("Load", "New process: " + st.pid + " " + st.name);
+ st.rel_utime = 0;
+ st.rel_stime = 0;
+ st.added = true;
+ if (!first) {
+ workingProcs.add(st);
+ }
+ continue;
+ }
+
+ // This process has gone away!
+ st.rel_utime = 0;
+ st.rel_stime = 0;
+ st.removed = true;
+ workingProcs.add(st);
+ allProcs.remove(curStatsIndex);
+ NS--;
+ if (localLOGV) Log.v(TAG, "Removed pid " + st.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.removed = true;
+ workingProcs.add(st);
+ allProcs.remove(curStatsIndex);
+ NS--;
+ if (localLOGV) Log.v(TAG, "Removed pid " + st.pid + ": " + st);
+ }
+
+ return pids;
+ }
+
+ 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 public int countWorkingStats() {
+ if (!mWorkingProcsSorted) {
+ Collections.sort(mWorkingProcs, sLoadComparator);
+ mWorkingProcsSorted = true;
+ }
+ return mWorkingProcs.size();
+ }
+
+ final public Stats getWorkingStats(int index) {
+ return mWorkingProcs.get(index);
+ }
+
+ final public String printCurrentState() {
+ if (!mWorkingProcsSorted) {
+ Collections.sort(mWorkingProcs, sLoadComparator);
+ mWorkingProcsSorted = true;
+ }
+
+ 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);
+
+ long now = SystemClock.uptimeMillis();
+
+ pw.print("CPU usage from ");
+ pw.print(now-mLastSampleTime);
+ pw.print("ms to ");
+ pw.print(now-mCurrentSampleTime);
+ pw.println("ms ago:");
+
+ final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime + mRelIrqTime +
+ mRelSoftIrqTime + mRelIdleTime;
+
+ int N = mWorkingProcs.size();
+ for (int i=0; i<N; i++) {
+ Stats st = mWorkingProcs.get(i);
+ printProcessCPU(pw, st.added ? " +" : (st.removed ? " -": " "),
+ st.name, totalTime, st.rel_utime, st.rel_stime, 0, 0, 0);
+ 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.name, totalTime, tst.rel_utime, tst.rel_stime, 0, 0, 0);
+ }
+ }
+ }
+
+ printProcessCPU(pw, "", "TOTAL", totalTime, mRelUserTime, mRelSystemTime, mRelIoWaitTime,
+ mRelIrqTime, mRelSoftIrqTime);
+
+ return sw.toString();
+ }
+
+ private void printProcessCPU(PrintWriter pw, String prefix, String label, int totalTime,
+ int user, int system, int iowait, int irq, int softIrq) {
+ pw.print(prefix);
+ pw.print(label);
+ pw.print(": ");
+ if (totalTime == 0) totalTime = 1;
+ pw.print(((user+system+iowait+irq+softIrq)*100)/totalTime);
+ pw.print("% = ");
+ pw.print((user*100)/totalTime);
+ pw.print("% user + ");
+ pw.print((system*100)/totalTime);
+ pw.print("% kernel");
+ if (iowait > 0) {
+ pw.print(" + ");
+ pw.print((iowait*100)/totalTime);
+ pw.print("% iowait");
+ }
+ if (irq > 0) {
+ pw.print(" + ");
+ pw.print((irq*100)/totalTime);
+ pw.print("% irq");
+ }
+ if (softIrq > 0) {
+ pw.print(" + ");
+ pw.print((softIrq*100)/totalTime);
+ pw.print("% softirq");
+ }
+ 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.baseName;
+ if (st.baseName == null || st.baseName.equals("app_process")) {
+ 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 (st.name == null || !newName.equals(st.name)) {
+ st.name = newName;
+ st.nameWidth = onMeasureProcessName(st.name);
+ }
+ }
+}
+