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