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