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