ProcessCpuTracker.java revision ccce016fb18b7c42d69edf9b1ade9a05e749eb4e
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.os;
18
19import static android.os.Process.*;
20
21import android.os.FileUtils;
22import android.os.Process;
23import android.os.StrictMode;
24import android.os.SystemClock;
25import android.util.Slog;
26
27import com.android.internal.util.FastPrintWriter;
28
29import libcore.io.IoUtils;
30
31import java.io.File;
32import java.io.FileInputStream;
33import java.io.PrintWriter;
34import java.io.StringWriter;
35import java.util.ArrayList;
36import java.util.Collections;
37import java.util.Comparator;
38import java.util.StringTokenizer;
39
40public class ProcessCpuTracker {
41    private static final String TAG = "ProcessCpuTracker";
42    private static final boolean DEBUG = false;
43    private static final boolean localLOGV = DEBUG || false;
44
45    private static final int[] PROCESS_STATS_FORMAT = new int[] {
46        PROC_SPACE_TERM,
47        PROC_SPACE_TERM|PROC_PARENS,
48        PROC_SPACE_TERM,
49        PROC_SPACE_TERM,
50        PROC_SPACE_TERM,
51        PROC_SPACE_TERM,
52        PROC_SPACE_TERM,
53        PROC_SPACE_TERM,
54        PROC_SPACE_TERM,
55        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 10: minor faults
56        PROC_SPACE_TERM,
57        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 12: major faults
58        PROC_SPACE_TERM,
59        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 14: utime
60        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 15: stime
61    };
62
63    static final int PROCESS_STAT_MINOR_FAULTS = 0;
64    static final int PROCESS_STAT_MAJOR_FAULTS = 1;
65    static final int PROCESS_STAT_UTIME = 2;
66    static final int PROCESS_STAT_STIME = 3;
67
68    /** Stores user time and system time in 100ths of a second. */
69    private final long[] mProcessStatsData = new long[4];
70
71    /** Stores user time and system time in 100ths of a second.  Used for
72     * public API to retrieve CPU use for a process.  Must lock while in use. */
73    private final long[] mSinglePidStatsData = new long[4];
74
75    private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
76        PROC_SPACE_TERM,
77        PROC_SPACE_TERM|PROC_PARENS|PROC_OUT_STRING,    // 2: name
78        PROC_SPACE_TERM,
79        PROC_SPACE_TERM,
80        PROC_SPACE_TERM,
81        PROC_SPACE_TERM,
82        PROC_SPACE_TERM,
83        PROC_SPACE_TERM,
84        PROC_SPACE_TERM,
85        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 10: minor faults
86        PROC_SPACE_TERM,
87        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 12: major faults
88        PROC_SPACE_TERM,
89        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 14: utime
90        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 15: stime
91        PROC_SPACE_TERM,
92        PROC_SPACE_TERM,
93        PROC_SPACE_TERM,
94        PROC_SPACE_TERM,
95        PROC_SPACE_TERM,
96        PROC_SPACE_TERM,
97        PROC_SPACE_TERM,
98        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 23: vsize
99    };
100
101    static final int PROCESS_FULL_STAT_MINOR_FAULTS = 1;
102    static final int PROCESS_FULL_STAT_MAJOR_FAULTS = 2;
103    static final int PROCESS_FULL_STAT_UTIME = 3;
104    static final int PROCESS_FULL_STAT_STIME = 4;
105    static final int PROCESS_FULL_STAT_VSIZE = 5;
106
107    private final String[] mProcessFullStatsStringData = new String[6];
108    private final long[] mProcessFullStatsData = new long[6];
109
110    private static final int[] SYSTEM_CPU_FORMAT = new int[] {
111        PROC_SPACE_TERM|PROC_COMBINE,
112        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 1: user time
113        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 2: nice time
114        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 3: sys time
115        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 4: idle time
116        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 5: iowait time
117        PROC_SPACE_TERM|PROC_OUT_LONG,                  // 6: irq time
118        PROC_SPACE_TERM|PROC_OUT_LONG                   // 7: softirq time
119    };
120
121    private final long[] mSystemCpuData = new long[7];
122
123    private static final int[] LOAD_AVERAGE_FORMAT = new int[] {
124        PROC_SPACE_TERM|PROC_OUT_FLOAT,                 // 0: 1 min
125        PROC_SPACE_TERM|PROC_OUT_FLOAT,                 // 1: 5 mins
126        PROC_SPACE_TERM|PROC_OUT_FLOAT                  // 2: 15 mins
127    };
128
129    private final float[] mLoadAverageData = new float[3];
130
131    private final boolean mIncludeThreads;
132
133    private float mLoad1 = 0;
134    private float mLoad5 = 0;
135    private float mLoad15 = 0;
136
137    private long mCurrentSampleTime;
138    private long mLastSampleTime;
139
140    private long mCurrentSampleRealTime;
141    private long mLastSampleRealTime;
142
143    private long mBaseUserTime;
144    private long mBaseSystemTime;
145    private long mBaseIoWaitTime;
146    private long mBaseIrqTime;
147    private long mBaseSoftIrqTime;
148    private long mBaseIdleTime;
149    private int mRelUserTime;
150    private int mRelSystemTime;
151    private int mRelIoWaitTime;
152    private int mRelIrqTime;
153    private int mRelSoftIrqTime;
154    private int mRelIdleTime;
155
156    private int[] mCurPids;
157    private int[] mCurThreadPids;
158
159    private final ArrayList<Stats> mProcStats = new ArrayList<Stats>();
160    private final ArrayList<Stats> mWorkingProcs = new ArrayList<Stats>();
161    private boolean mWorkingProcsSorted;
162
163    private boolean mFirst = true;
164
165    private byte[] mBuffer = new byte[4096];
166
167    /**
168     * The time in microseconds that the CPU has been running at each speed.
169     */
170    private long[] mCpuSpeedTimes;
171
172    /**
173     * The relative time in microseconds that the CPU has been running at each speed.
174     */
175    private long[] mRelCpuSpeedTimes;
176
177    /**
178     * The different speeds that the CPU can be running at.
179     */
180    private long[] mCpuSpeeds;
181
182    public static class Stats {
183        public final int pid;
184        public final int uid;
185        final String statFile;
186        final String cmdlineFile;
187        final String threadsDir;
188        final ArrayList<Stats> threadStats;
189        final ArrayList<Stats> workingThreads;
190
191        public BatteryStatsImpl.Uid.Proc batteryStats;
192
193        public boolean interesting;
194
195        public String baseName;
196        public String name;
197        public int nameWidth;
198
199        // vsize capture when process first detected; can be used to
200        // filter out kernel processes.
201        public long vsize;
202
203        public long base_uptime;
204        public long rel_uptime;
205
206        public long base_utime;
207        public long base_stime;
208        public int rel_utime;
209        public int rel_stime;
210
211        public long base_minfaults;
212        public long base_majfaults;
213        public int rel_minfaults;
214        public int rel_majfaults;
215
216        public boolean active;
217        public boolean working;
218        public boolean added;
219        public boolean removed;
220
221        Stats(int _pid, int parentPid, boolean includeThreads) {
222            pid = _pid;
223            if (parentPid < 0) {
224                final File procDir = new File("/proc", Integer.toString(pid));
225                statFile = new File(procDir, "stat").toString();
226                cmdlineFile = new File(procDir, "cmdline").toString();
227                threadsDir = (new File(procDir, "task")).toString();
228                if (includeThreads) {
229                    threadStats = new ArrayList<Stats>();
230                    workingThreads = new ArrayList<Stats>();
231                } else {
232                    threadStats = null;
233                    workingThreads = null;
234                }
235            } else {
236                final File procDir = new File("/proc", Integer.toString(
237                        parentPid));
238                final File taskDir = new File(
239                        new File(procDir, "task"), Integer.toString(pid));
240                statFile = new File(taskDir, "stat").toString();
241                cmdlineFile = null;
242                threadsDir = null;
243                threadStats = null;
244                workingThreads = null;
245            }
246            uid = FileUtils.getUid(statFile.toString());
247        }
248    }
249
250    private final static Comparator<Stats> sLoadComparator = new Comparator<Stats>() {
251        public final int
252        compare(Stats sta, Stats stb) {
253            int ta = sta.rel_utime + sta.rel_stime;
254            int tb = stb.rel_utime + stb.rel_stime;
255            if (ta != tb) {
256                return ta > tb ? -1 : 1;
257            }
258            if (sta.added != stb.added) {
259                return sta.added ? -1 : 1;
260            }
261            if (sta.removed != stb.removed) {
262                return sta.added ? -1 : 1;
263            }
264            return 0;
265        }
266    };
267
268
269    public ProcessCpuTracker(boolean includeThreads) {
270        mIncludeThreads = includeThreads;
271    }
272
273    public void onLoadChanged(float load1, float load5, float load15) {
274    }
275
276    public int onMeasureProcessName(String name) {
277        return 0;
278    }
279
280    public void init() {
281        if (DEBUG) Slog.v(TAG, "Init: " + this);
282        mFirst = true;
283        update();
284    }
285
286    public void update() {
287        if (DEBUG) Slog.v(TAG, "Update: " + this);
288        mLastSampleTime = mCurrentSampleTime;
289        mCurrentSampleTime = SystemClock.uptimeMillis();
290        mLastSampleRealTime = mCurrentSampleRealTime;
291        mCurrentSampleRealTime = SystemClock.elapsedRealtime();
292
293        final long[] sysCpu = mSystemCpuData;
294        if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT,
295                null, sysCpu, null)) {
296            // Total user time is user + nice time.
297            final long usertime = sysCpu[0]+sysCpu[1];
298            // Total system time is simply system time.
299            final long systemtime = sysCpu[2];
300            // Total idle time is simply idle time.
301            final long idletime = sysCpu[3];
302            // Total irq time is iowait + irq + softirq time.
303            final long iowaittime = sysCpu[4];
304            final long irqtime = sysCpu[5];
305            final long softirqtime = sysCpu[6];
306
307            mRelUserTime = (int)(usertime - mBaseUserTime);
308            mRelSystemTime = (int)(systemtime - mBaseSystemTime);
309            mRelIoWaitTime = (int)(iowaittime - mBaseIoWaitTime);
310            mRelIrqTime = (int)(irqtime - mBaseIrqTime);
311            mRelSoftIrqTime = (int)(softirqtime - mBaseSoftIrqTime);
312            mRelIdleTime = (int)(idletime - mBaseIdleTime);
313
314            if (DEBUG) {
315                Slog.i("Load", "Total U:" + sysCpu[0] + " N:" + sysCpu[1]
316                      + " S:" + sysCpu[2] + " I:" + sysCpu[3]
317                      + " W:" + sysCpu[4] + " Q:" + sysCpu[5]
318                      + " O:" + sysCpu[6]);
319                Slog.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime
320                      + " I:" + mRelIdleTime + " Q:" + mRelIrqTime);
321            }
322
323            mBaseUserTime = usertime;
324            mBaseSystemTime = systemtime;
325            mBaseIoWaitTime = iowaittime;
326            mBaseIrqTime = irqtime;
327            mBaseSoftIrqTime = softirqtime;
328            mBaseIdleTime = idletime;
329        }
330
331        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
332        try {
333            mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats);
334        } finally {
335            StrictMode.setThreadPolicy(savedPolicy);
336        }
337
338        final float[] loadAverages = mLoadAverageData;
339        if (Process.readProcFile("/proc/loadavg", LOAD_AVERAGE_FORMAT,
340                null, null, loadAverages)) {
341            float load1 = loadAverages[0];
342            float load5 = loadAverages[1];
343            float load15 = loadAverages[2];
344            if (load1 != mLoad1 || load5 != mLoad5 || load15 != mLoad15) {
345                mLoad1 = load1;
346                mLoad5 = load5;
347                mLoad15 = load15;
348                onLoadChanged(load1, load5, load15);
349            }
350        }
351
352        if (DEBUG) Slog.i(TAG, "*** TIME TO COLLECT STATS: "
353                + (SystemClock.uptimeMillis()-mCurrentSampleTime));
354
355        mWorkingProcsSorted = false;
356        mFirst = false;
357    }
358
359    private int[] collectStats(String statsFile, int parentPid, boolean first,
360            int[] curPids, ArrayList<Stats> allProcs) {
361
362        int[] pids = Process.getPids(statsFile, curPids);
363        int NP = (pids == null) ? 0 : pids.length;
364        int NS = allProcs.size();
365        int curStatsIndex = 0;
366        for (int i=0; i<NP; i++) {
367            int pid = pids[i];
368            if (pid < 0) {
369                NP = pid;
370                break;
371            }
372            Stats st = curStatsIndex < NS ? allProcs.get(curStatsIndex) : null;
373
374            if (st != null && st.pid == pid) {
375                // Update an existing process...
376                st.added = false;
377                st.working = false;
378                curStatsIndex++;
379                if (DEBUG) Slog.v(TAG, "Existing "
380                        + (parentPid < 0 ? "process" : "thread")
381                        + " pid " + pid + ": " + st);
382
383                if (st.interesting) {
384                    final long uptime = SystemClock.uptimeMillis();
385
386                    final long[] procStats = mProcessStatsData;
387                    if (!Process.readProcFile(st.statFile.toString(),
388                            PROCESS_STATS_FORMAT, null, procStats, null)) {
389                        continue;
390                    }
391
392                    final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS];
393                    final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS];
394                    final long utime = procStats[PROCESS_STAT_UTIME];
395                    final long stime = procStats[PROCESS_STAT_STIME];
396
397                    if (utime == st.base_utime && stime == st.base_stime) {
398                        st.rel_utime = 0;
399                        st.rel_stime = 0;
400                        st.rel_minfaults = 0;
401                        st.rel_majfaults = 0;
402                        if (st.active) {
403                            st.active = false;
404                        }
405                        continue;
406                    }
407
408                    if (!st.active) {
409                        st.active = true;
410                    }
411
412                    if (parentPid < 0) {
413                        getName(st, st.cmdlineFile);
414                        if (st.threadStats != null) {
415                            mCurThreadPids = collectStats(st.threadsDir, pid, false,
416                                    mCurThreadPids, st.threadStats);
417                        }
418                    }
419
420                    if (DEBUG) Slog.v("Load", "Stats changed " + st.name + " pid=" + st.pid
421                            + " utime=" + utime + "-" + st.base_utime
422                            + " stime=" + stime + "-" + st.base_stime
423                            + " minfaults=" + minfaults + "-" + st.base_minfaults
424                            + " majfaults=" + majfaults + "-" + st.base_majfaults);
425
426                    st.rel_uptime = uptime - st.base_uptime;
427                    st.base_uptime = uptime;
428                    st.rel_utime = (int)(utime - st.base_utime);
429                    st.rel_stime = (int)(stime - st.base_stime);
430                    st.base_utime = utime;
431                    st.base_stime = stime;
432                    st.rel_minfaults = (int)(minfaults - st.base_minfaults);
433                    st.rel_majfaults = (int)(majfaults - st.base_majfaults);
434                    st.base_minfaults = minfaults;
435                    st.base_majfaults = majfaults;
436                    st.working = true;
437                }
438
439                continue;
440            }
441
442            if (st == null || st.pid > pid) {
443                // We have a new process!
444                st = new Stats(pid, parentPid, mIncludeThreads);
445                allProcs.add(curStatsIndex, st);
446                curStatsIndex++;
447                NS++;
448                if (DEBUG) Slog.v(TAG, "New "
449                        + (parentPid < 0 ? "process" : "thread")
450                        + " pid " + pid + ": " + st);
451
452                final String[] procStatsString = mProcessFullStatsStringData;
453                final long[] procStats = mProcessFullStatsData;
454                st.base_uptime = SystemClock.uptimeMillis();
455                String path = st.statFile.toString();
456                //Slog.d(TAG, "Reading proc file: " + path);
457                if (Process.readProcFile(path, PROCESS_FULL_STATS_FORMAT, procStatsString,
458                        procStats, null)) {
459                    // This is a possible way to filter out processes that
460                    // are actually kernel threads...  do we want to?  Some
461                    // of them do use CPU, but there can be a *lot* that are
462                    // not doing anything.
463                    st.vsize = procStats[PROCESS_FULL_STAT_VSIZE];
464                    if (true || procStats[PROCESS_FULL_STAT_VSIZE] != 0) {
465                        st.interesting = true;
466                        st.baseName = procStatsString[0];
467                        st.base_minfaults = procStats[PROCESS_FULL_STAT_MINOR_FAULTS];
468                        st.base_majfaults = procStats[PROCESS_FULL_STAT_MAJOR_FAULTS];
469                        st.base_utime = procStats[PROCESS_FULL_STAT_UTIME];
470                        st.base_stime = procStats[PROCESS_FULL_STAT_STIME];
471                    } else {
472                        Slog.i(TAG, "Skipping kernel process pid " + pid
473                                + " name " + procStatsString[0]);
474                        st.baseName = procStatsString[0];
475                    }
476                } else {
477                    Slog.w(TAG, "Skipping unknown process pid " + pid);
478                    st.baseName = "<unknown>";
479                    st.base_utime = st.base_stime = 0;
480                    st.base_minfaults = st.base_majfaults = 0;
481                }
482
483                if (parentPid < 0) {
484                    getName(st, st.cmdlineFile);
485                    if (st.threadStats != null) {
486                        mCurThreadPids = collectStats(st.threadsDir, pid, true,
487                                mCurThreadPids, st.threadStats);
488                    }
489                } else if (st.interesting) {
490                    st.name = st.baseName;
491                    st.nameWidth = onMeasureProcessName(st.name);
492                }
493
494                if (DEBUG) Slog.v("Load", "Stats added " + st.name + " pid=" + st.pid
495                        + " utime=" + st.base_utime + " stime=" + st.base_stime
496                        + " minfaults=" + st.base_minfaults + " majfaults=" + st.base_majfaults);
497
498                st.rel_utime = 0;
499                st.rel_stime = 0;
500                st.rel_minfaults = 0;
501                st.rel_majfaults = 0;
502                st.added = true;
503                if (!first && st.interesting) {
504                    st.working = true;
505                }
506                continue;
507            }
508
509            // This process has gone away!
510            st.rel_utime = 0;
511            st.rel_stime = 0;
512            st.rel_minfaults = 0;
513            st.rel_majfaults = 0;
514            st.removed = true;
515            st.working = true;
516            allProcs.remove(curStatsIndex);
517            NS--;
518            if (DEBUG) Slog.v(TAG, "Removed "
519                    + (parentPid < 0 ? "process" : "thread")
520                    + " pid " + pid + ": " + st);
521            // Decrement the loop counter so that we process the current pid
522            // again the next time through the loop.
523            i--;
524            continue;
525        }
526
527        while (curStatsIndex < NS) {
528            // This process has gone away!
529            final Stats st = allProcs.get(curStatsIndex);
530            st.rel_utime = 0;
531            st.rel_stime = 0;
532            st.rel_minfaults = 0;
533            st.rel_majfaults = 0;
534            st.removed = true;
535            st.working = true;
536            allProcs.remove(curStatsIndex);
537            NS--;
538            if (localLOGV) Slog.v(TAG, "Removed pid " + st.pid + ": " + st);
539        }
540
541        return pids;
542    }
543
544    /**
545     * Returns the total time (in clock ticks, or 1/100 sec) spent executing in
546     * both user and system code.  Safe to call without lock held.
547     */
548    public long getCpuTimeForPid(int pid) {
549        synchronized (mSinglePidStatsData) {
550            final String statFile = "/proc/" + pid + "/stat";
551            final long[] statsData = mSinglePidStatsData;
552            if (Process.readProcFile(statFile, PROCESS_STATS_FORMAT,
553                    null, statsData, null)) {
554                long time = statsData[PROCESS_STAT_UTIME]
555                        + statsData[PROCESS_STAT_STIME];
556                return time;
557            }
558            return 0;
559        }
560    }
561
562    /**
563     * Returns the delta time (in clock ticks, or 1/100 sec) spent at each CPU
564     * speed, since the last call to this method. If this is the first call, it
565     * will return 1 for each value.
566     */
567    public long[] getLastCpuSpeedTimes() {
568        if (mCpuSpeedTimes == null) {
569            mCpuSpeedTimes = getCpuSpeedTimes(null);
570            mRelCpuSpeedTimes = new long[mCpuSpeedTimes.length];
571            for (int i = 0; i < mCpuSpeedTimes.length; i++) {
572                mRelCpuSpeedTimes[i] = 1; // Initialize
573            }
574        } else {
575            getCpuSpeedTimes(mRelCpuSpeedTimes);
576            for (int i = 0; i < mCpuSpeedTimes.length; i++) {
577                long temp = mRelCpuSpeedTimes[i];
578                mRelCpuSpeedTimes[i] -= mCpuSpeedTimes[i];
579                mCpuSpeedTimes[i] = temp;
580            }
581        }
582        return mRelCpuSpeedTimes;
583    }
584
585    private long[] getCpuSpeedTimes(long[] out) {
586        long[] tempTimes = out;
587        long[] tempSpeeds = mCpuSpeeds;
588        final int MAX_SPEEDS = 60;
589        if (out == null) {
590            tempTimes = new long[MAX_SPEEDS]; // Hopefully no more than that
591            tempSpeeds = new long[MAX_SPEEDS];
592        }
593        int speed = 0;
594        String file = readFile("/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state", '\0');
595        // Note: file may be null on kernels without cpufreq (i.e. the emulator's)
596        if (file != null) {
597            StringTokenizer st = new StringTokenizer(file, "\n ");
598            while (st.hasMoreElements()) {
599                String token = st.nextToken();
600                try {
601                    long val = Long.parseLong(token);
602                    tempSpeeds[speed] = val;
603                    token = st.nextToken();
604                    val = Long.parseLong(token);
605                    tempTimes[speed] = val;
606                    speed++;
607                    if (speed == MAX_SPEEDS) break; // No more
608                    if (localLOGV && out == null) {
609                        Slog.v(TAG, "First time : Speed/Time = " + tempSpeeds[speed - 1]
610                              + "\t" + tempTimes[speed - 1]);
611                    }
612                } catch (NumberFormatException nfe) {
613                    Slog.i(TAG, "Unable to parse time_in_state");
614                }
615            }
616        }
617        if (out == null) {
618            out = new long[speed];
619            mCpuSpeeds = new long[speed];
620            System.arraycopy(tempSpeeds, 0, mCpuSpeeds, 0, speed);
621            System.arraycopy(tempTimes, 0, out, 0, speed);
622        }
623        return out;
624    }
625
626    final public int getLastUserTime() {
627        return mRelUserTime;
628    }
629
630    final public int getLastSystemTime() {
631        return mRelSystemTime;
632    }
633
634    final public int getLastIoWaitTime() {
635        return mRelIoWaitTime;
636    }
637
638    final public int getLastIrqTime() {
639        return mRelIrqTime;
640    }
641
642    final public int getLastSoftIrqTime() {
643        return mRelSoftIrqTime;
644    }
645
646    final public int getLastIdleTime() {
647        return mRelIdleTime;
648    }
649
650    final public float getTotalCpuPercent() {
651        int denom = mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime;
652        if (denom <= 0) {
653            return 0;
654        }
655        return ((float)(mRelUserTime+mRelSystemTime+mRelIrqTime)*100) / denom;
656    }
657
658    final void buildWorkingProcs() {
659        if (!mWorkingProcsSorted) {
660            mWorkingProcs.clear();
661            final int N = mProcStats.size();
662            for (int i=0; i<N; i++) {
663                Stats stats = mProcStats.get(i);
664                if (stats.working) {
665                    mWorkingProcs.add(stats);
666                    if (stats.threadStats != null && stats.threadStats.size() > 1) {
667                        stats.workingThreads.clear();
668                        final int M = stats.threadStats.size();
669                        for (int j=0; j<M; j++) {
670                            Stats tstats = stats.threadStats.get(j);
671                            if (tstats.working) {
672                                stats.workingThreads.add(tstats);
673                            }
674                        }
675                        Collections.sort(stats.workingThreads, sLoadComparator);
676                    }
677                }
678            }
679            Collections.sort(mWorkingProcs, sLoadComparator);
680            mWorkingProcsSorted = true;
681        }
682    }
683
684    final public int countStats() {
685        return mProcStats.size();
686    }
687
688    final public Stats getStats(int index) {
689        return mProcStats.get(index);
690    }
691
692    final public int countWorkingStats() {
693        buildWorkingProcs();
694        return mWorkingProcs.size();
695    }
696
697    final public Stats getWorkingStats(int index) {
698        return mWorkingProcs.get(index);
699    }
700
701    final public String printCurrentLoad() {
702        StringWriter sw = new StringWriter();
703        PrintWriter pw = new FastPrintWriter(sw, false, 128);
704        pw.print("Load: ");
705        pw.print(mLoad1);
706        pw.print(" / ");
707        pw.print(mLoad5);
708        pw.print(" / ");
709        pw.println(mLoad15);
710        pw.flush();
711        return sw.toString();
712    }
713
714    final public String printCurrentState(long now) {
715        buildWorkingProcs();
716
717        StringWriter sw = new StringWriter();
718        PrintWriter pw = new FastPrintWriter(sw, false, 1024);
719
720        pw.print("CPU usage from ");
721        if (now > mLastSampleTime) {
722            pw.print(now-mLastSampleTime);
723            pw.print("ms to ");
724            pw.print(now-mCurrentSampleTime);
725            pw.print("ms ago");
726        } else {
727            pw.print(mLastSampleTime-now);
728            pw.print("ms to ");
729            pw.print(mCurrentSampleTime-now);
730            pw.print("ms later");
731        }
732
733        long sampleTime = mCurrentSampleTime - mLastSampleTime;
734        long sampleRealTime = mCurrentSampleRealTime - mLastSampleRealTime;
735        long percAwake = sampleRealTime > 0 ? ((sampleTime*100) / sampleRealTime) : 0;
736        if (percAwake != 100) {
737            pw.print(" with ");
738            pw.print(percAwake);
739            pw.print("% awake");
740        }
741        pw.println(":");
742
743        final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime
744                + mRelIrqTime + mRelSoftIrqTime + mRelIdleTime;
745
746        if (DEBUG) Slog.i(TAG, "totalTime " + totalTime + " over sample time "
747                + (mCurrentSampleTime-mLastSampleTime));
748
749        int N = mWorkingProcs.size();
750        for (int i=0; i<N; i++) {
751            Stats st = mWorkingProcs.get(i);
752            printProcessCPU(pw, st.added ? " +" : (st.removed ? " -": "  "),
753                    st.pid, st.name, (int)(st.rel_uptime+5)/10,
754                    st.rel_utime, st.rel_stime, 0, 0, 0, st.rel_minfaults, st.rel_majfaults);
755            if (!st.removed && st.workingThreads != null) {
756                int M = st.workingThreads.size();
757                for (int j=0; j<M; j++) {
758                    Stats tst = st.workingThreads.get(j);
759                    printProcessCPU(pw,
760                            tst.added ? "   +" : (tst.removed ? "   -": "    "),
761                            tst.pid, tst.name, (int)(st.rel_uptime+5)/10,
762                            tst.rel_utime, tst.rel_stime, 0, 0, 0, 0, 0);
763                }
764            }
765        }
766
767        printProcessCPU(pw, "", -1, "TOTAL", totalTime, mRelUserTime, mRelSystemTime,
768                mRelIoWaitTime, mRelIrqTime, mRelSoftIrqTime, 0, 0);
769
770        pw.flush();
771        return sw.toString();
772    }
773
774    private void printRatio(PrintWriter pw, long numerator, long denominator) {
775        long thousands = (numerator*1000)/denominator;
776        long hundreds = thousands/10;
777        pw.print(hundreds);
778        if (hundreds < 10) {
779            long remainder = thousands - (hundreds*10);
780            if (remainder != 0) {
781                pw.print('.');
782                pw.print(remainder);
783            }
784        }
785    }
786
787    private void printProcessCPU(PrintWriter pw, String prefix, int pid, String label,
788            int totalTime, int user, int system, int iowait, int irq, int softIrq,
789            int minFaults, int majFaults) {
790        pw.print(prefix);
791        if (totalTime == 0) totalTime = 1;
792        printRatio(pw, user+system+iowait+irq+softIrq, totalTime);
793        pw.print("% ");
794        if (pid >= 0) {
795            pw.print(pid);
796            pw.print("/");
797        }
798        pw.print(label);
799        pw.print(": ");
800        printRatio(pw, user, totalTime);
801        pw.print("% user + ");
802        printRatio(pw, system, totalTime);
803        pw.print("% kernel");
804        if (iowait > 0) {
805            pw.print(" + ");
806            printRatio(pw, iowait, totalTime);
807            pw.print("% iowait");
808        }
809        if (irq > 0) {
810            pw.print(" + ");
811            printRatio(pw, irq, totalTime);
812            pw.print("% irq");
813        }
814        if (softIrq > 0) {
815            pw.print(" + ");
816            printRatio(pw, softIrq, totalTime);
817            pw.print("% softirq");
818        }
819        if (minFaults > 0 || majFaults > 0) {
820            pw.print(" / faults:");
821            if (minFaults > 0) {
822                pw.print(" ");
823                pw.print(minFaults);
824                pw.print(" minor");
825            }
826            if (majFaults > 0) {
827                pw.print(" ");
828                pw.print(majFaults);
829                pw.print(" major");
830            }
831        }
832        pw.println();
833    }
834
835    private String readFile(String file, char endChar) {
836        // Permit disk reads here, as /proc/meminfo isn't really "on
837        // disk" and should be fast.  TODO: make BlockGuard ignore
838        // /proc/ and /sys/ files perhaps?
839        StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
840        FileInputStream is = null;
841        try {
842            is = new FileInputStream(file);
843            int len = is.read(mBuffer);
844            is.close();
845
846            if (len > 0) {
847                int i;
848                for (i=0; i<len; i++) {
849                    if (mBuffer[i] == endChar) {
850                        break;
851                    }
852                }
853                return new String(mBuffer, 0, i);
854            }
855        } catch (java.io.FileNotFoundException e) {
856        } catch (java.io.IOException e) {
857        } finally {
858            IoUtils.closeQuietly(is);
859            StrictMode.setThreadPolicy(savedPolicy);
860        }
861        return null;
862    }
863
864    private void getName(Stats st, String cmdlineFile) {
865        String newName = st.name;
866        if (st.name == null || st.name.equals("app_process")
867                || st.name.equals("<pre-initialized>")) {
868            String cmdName = readFile(cmdlineFile, '\0');
869            if (cmdName != null && cmdName.length() > 1) {
870                newName = cmdName;
871                int i = newName.lastIndexOf("/");
872                if (i > 0 && i < newName.length()-1) {
873                    newName = newName.substring(i+1);
874                }
875            }
876            if (newName == null) {
877                newName = st.baseName;
878            }
879        }
880        if (st.name == null || !newName.equals(st.name)) {
881            st.name = newName;
882            st.nameWidth = onMeasureProcessName(st.name);
883        }
884    }
885}
886