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