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