/* * 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_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_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 mProcStats = new ArrayList(); private final ArrayList mWorkingProcs = new ArrayList(); 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 threadStats; final ArrayList 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 long base_minfaults; public long base_majfaults; public int rel_minfaults; public int rel_majfaults; 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(); workingThreads = new ArrayList(); } 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 sLoadComparator = new Comparator() { 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) { 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; } mWorkingProcsSorted = false; mFirst = false; } private int[] collectStats(String statsFile, int parentPid, boolean first, int[] curPids, ArrayList allProcs, ArrayList 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 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"); } 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 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); } } }