diff options
author | Vishal Bhoj <vishal.bhoj@linaro.org> | 2013-06-19 13:33:16 +0100 |
---|---|---|
committer | Bernhard Rosenkränzer <Bernhard.Rosenkranzer@linaro.org> | 2014-07-03 02:38:35 +0200 |
commit | 1fd69731d399b0cc16179c11d033bc8ecd575d77 (patch) | |
tree | 5ed11d5b702c64ac61d0e7f955ec59d6a806e2d4 | |
parent | 60de6a82dda3852883d830433411aa80e223a48d (diff) | |
download | base-1fd69731d399b0cc16179c11d033bc8ecd575d77.tar.gz |
ProcessStats: Handle changes in the scaling frequnency
Switching from MP to IKS mode results increase in number of operating points
and results in bug #1183781 . The patch makes sure that whenever the available
scaling frequency changes it resets the existing values and sets them as per the
new values.
Change-Id: Ic74cdc2015f3c4ddd61cb116d1f4b4ed1204a3bf
Signed-off-by: Vishal Bhoj <vishal.bhoj@linaro.org>
-rw-r--r-- | core/java/com/android/internal/os/ProcessStats.java | 887 |
1 files changed, 887 insertions, 0 deletions
diff --git a/core/java/com/android/internal/os/ProcessStats.java b/core/java/com/android/internal/os/ProcessStats.java new file mode 100644 index 000000000000..b1ea5c897f30 --- /dev/null +++ b/core/java/com/android/internal/os/ProcessStats.java @@ -0,0 +1,887 @@ +/* + * 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.internal.os; + +import static android.os.Process.*; + +import android.os.Process; +import android.os.StrictMode; +import android.os.SystemClock; +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.Date; +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 || false; + + 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[4096]; + + /** + * 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; + + /** + * The lastmodified date of sysfs entry to detect change in available scaling frequency + */ + private Date mLastModified = null; + + 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; + public 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 = 60; + Date currentModified = null; + + try { + File file = new File("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies"); + if (file.exists()) { + currentModified = new Date(file.lastModified()); + } + } catch (Exception e) { + Slog.i(TAG, "Exception = " + e); + } + if (mLastModified == null) { + mLastModified = currentModified; + } + if (out == null || out.length == 0 || mLastModified != currentModified) { + tempTimes = new long[MAX_SPEEDS]; // Hopefully no more than that + tempSpeeds = new long[MAX_SPEEDS]; + mLastModified = currentModified; + // We need to clean up previous scaling frequency since they need to be updated + out = null; + } + 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 (Exception e) { + if (e instanceof NumberFormatException) { + Slog.i(TAG, "Unable to parse time_in_state"); + } + else if (e instanceof ArrayIndexOutOfBoundsException) { + Slog.i(TAG, "Scaling frequency changed while accessing" + + " the time_in_state, It will be handled when the function is called again"); + } + } + } + } + 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() { + int denom = mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime; + if (denom <= 0) { + return 0; + } + return ((float)(mRelUserTime+mRelSystemTime+mRelIrqTime)*100) / denom; + } + + 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 = sampleRealTime > 0 ? ((sampleTime*100) / sampleRealTime) : 0; + 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) { + // Permit disk reads here, as /proc/meminfo isn't really "on + // disk" and should be fast. TODO: make BlockGuard ignore + // /proc/ and /sys/ files perhaps? + StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); + FileInputStream is = null; + try { + 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, i); + } + } catch (java.io.FileNotFoundException e) { + } catch (java.io.IOException e) { + } finally { + if (is != null) { + try { + is.close(); + } catch (java.io.IOException e) { + } + } + StrictMode.setThreadPolicy(savedPolicy); + } + 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); + } + } +} |