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