• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.internal.os;
18 
19 import static android.os.Process.*;
20 
21 import android.os.Process;
22 import android.os.StrictMode;
23 import android.os.SystemClock;
24 import android.util.Slog;
25 
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.PrintWriter;
29 import java.io.StringWriter;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.Comparator;
33 import java.util.StringTokenizer;
34 
35 public 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[4096];
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 
Stats(int _pid, int parentPid, boolean includeThreads)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 
ProcessStats(boolean includeThreads)253     public ProcessStats(boolean includeThreads) {
254         mIncludeThreads = includeThreads;
255     }
256 
onLoadChanged(float load1, float load5, float load15)257     public void onLoadChanged(float load1, float load5, float load15) {
258     }
259 
onMeasureProcessName(String name)260     public int onMeasureProcessName(String name) {
261         return 0;
262     }
263 
init()264     public void init() {
265         if (DEBUG) Slog.v(TAG, "Init: " + this);
266         mFirst = true;
267         update();
268     }
269 
update()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 
collectStats(String statsFile, int parentPid, boolean first, int[] curPids, ArrayList<Stats> allProcs)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 = 60;
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         int denom = mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime;
623         if (denom <= 0) {
624             return 0;
625         }
626         return ((float)(mRelUserTime+mRelSystemTime+mRelIrqTime)*100) / denom;
627     }
628 
629     final void buildWorkingProcs() {
630         if (!mWorkingProcsSorted) {
631             mWorkingProcs.clear();
632             final int N = mProcStats.size();
633             for (int i=0; i<N; i++) {
634                 Stats stats = mProcStats.get(i);
635                 if (stats.working) {
636                     mWorkingProcs.add(stats);
637                     if (stats.threadStats != null && stats.threadStats.size() > 1) {
638                         stats.workingThreads.clear();
639                         final int M = stats.threadStats.size();
640                         for (int j=0; j<M; j++) {
641                             Stats tstats = stats.threadStats.get(j);
642                             if (tstats.working) {
643                                 stats.workingThreads.add(tstats);
644                             }
645                         }
646                         Collections.sort(stats.workingThreads, sLoadComparator);
647                     }
648                 }
649             }
650             Collections.sort(mWorkingProcs, sLoadComparator);
651             mWorkingProcsSorted = true;
652         }
653     }
654 
655     final public int countStats() {
656         return mProcStats.size();
657     }
658 
659     final public Stats getStats(int index) {
660         return mProcStats.get(index);
661     }
662 
663     final public int countWorkingStats() {
664         buildWorkingProcs();
665         return mWorkingProcs.size();
666     }
667 
668     final public Stats getWorkingStats(int index) {
669         return mWorkingProcs.get(index);
670     }
671 
672     final public String printCurrentLoad() {
673         StringWriter sw = new StringWriter();
674         PrintWriter pw = new PrintWriter(sw);
675         pw.print("Load: ");
676         pw.print(mLoad1);
677         pw.print(" / ");
678         pw.print(mLoad5);
679         pw.print(" / ");
680         pw.println(mLoad15);
681         return sw.toString();
682     }
683 
684     final public String printCurrentState(long now) {
685         buildWorkingProcs();
686 
687         StringWriter sw = new StringWriter();
688         PrintWriter pw = new PrintWriter(sw);
689 
690         pw.print("CPU usage from ");
691         if (now > mLastSampleTime) {
692             pw.print(now-mLastSampleTime);
693             pw.print("ms to ");
694             pw.print(now-mCurrentSampleTime);
695             pw.print("ms ago");
696         } else {
697             pw.print(mLastSampleTime-now);
698             pw.print("ms to ");
699             pw.print(mCurrentSampleTime-now);
700             pw.print("ms later");
701         }
702 
703         long sampleTime = mCurrentSampleTime - mLastSampleTime;
704         long sampleRealTime = mCurrentSampleRealTime - mLastSampleRealTime;
705         long percAwake = sampleRealTime > 0 ? ((sampleTime*100) / sampleRealTime) : 0;
706         if (percAwake != 100) {
707             pw.print(" with ");
708             pw.print(percAwake);
709             pw.print("% awake");
710         }
711         pw.println(":");
712 
713         final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime
714                 + mRelIrqTime + mRelSoftIrqTime + mRelIdleTime;
715 
716         if (DEBUG) Slog.i(TAG, "totalTime " + totalTime + " over sample time "
717                 + (mCurrentSampleTime-mLastSampleTime));
718 
719         int N = mWorkingProcs.size();
720         for (int i=0; i<N; i++) {
721             Stats st = mWorkingProcs.get(i);
722             printProcessCPU(pw, st.added ? " +" : (st.removed ? " -": "  "),
723                     st.pid, st.name, (int)(st.rel_uptime+5)/10,
724                     st.rel_utime, st.rel_stime, 0, 0, 0, st.rel_minfaults, st.rel_majfaults);
725             if (!st.removed && st.workingThreads != null) {
726                 int M = st.workingThreads.size();
727                 for (int j=0; j<M; j++) {
728                     Stats tst = st.workingThreads.get(j);
729                     printProcessCPU(pw,
730                             tst.added ? "   +" : (tst.removed ? "   -": "    "),
731                             tst.pid, tst.name, (int)(st.rel_uptime+5)/10,
732                             tst.rel_utime, tst.rel_stime, 0, 0, 0, 0, 0);
733                 }
734             }
735         }
736 
737         printProcessCPU(pw, "", -1, "TOTAL", totalTime, mRelUserTime, mRelSystemTime,
738                 mRelIoWaitTime, mRelIrqTime, mRelSoftIrqTime, 0, 0);
739 
740         return sw.toString();
741     }
742 
743     private void printRatio(PrintWriter pw, long numerator, long denominator) {
744         long thousands = (numerator*1000)/denominator;
745         long hundreds = thousands/10;
746         pw.print(hundreds);
747         if (hundreds < 10) {
748             long remainder = thousands - (hundreds*10);
749             if (remainder != 0) {
750                 pw.print('.');
751                 pw.print(remainder);
752             }
753         }
754     }
755 
756     private void printProcessCPU(PrintWriter pw, String prefix, int pid, String label,
757             int totalTime, int user, int system, int iowait, int irq, int softIrq,
758             int minFaults, int majFaults) {
759         pw.print(prefix);
760         if (totalTime == 0) totalTime = 1;
761         printRatio(pw, user+system+iowait+irq+softIrq, totalTime);
762         pw.print("% ");
763         if (pid >= 0) {
764             pw.print(pid);
765             pw.print("/");
766         }
767         pw.print(label);
768         pw.print(": ");
769         printRatio(pw, user, totalTime);
770         pw.print("% user + ");
771         printRatio(pw, system, totalTime);
772         pw.print("% kernel");
773         if (iowait > 0) {
774             pw.print(" + ");
775             printRatio(pw, iowait, totalTime);
776             pw.print("% iowait");
777         }
778         if (irq > 0) {
779             pw.print(" + ");
780             printRatio(pw, irq, totalTime);
781             pw.print("% irq");
782         }
783         if (softIrq > 0) {
784             pw.print(" + ");
785             printRatio(pw, softIrq, totalTime);
786             pw.print("% softirq");
787         }
788         if (minFaults > 0 || majFaults > 0) {
789             pw.print(" / faults:");
790             if (minFaults > 0) {
791                 pw.print(" ");
792                 pw.print(minFaults);
793                 pw.print(" minor");
794             }
795             if (majFaults > 0) {
796                 pw.print(" ");
797                 pw.print(majFaults);
798                 pw.print(" major");
799             }
800         }
801         pw.println();
802     }
803 
804     private String readFile(String file, char endChar) {
805         // Permit disk reads here, as /proc/meminfo isn't really "on
806         // disk" and should be fast.  TODO: make BlockGuard ignore
807         // /proc/ and /sys/ files perhaps?
808         StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
809         FileInputStream is = null;
810         try {
811             is = new FileInputStream(file);
812             int len = is.read(mBuffer);
813             is.close();
814 
815             if (len > 0) {
816                 int i;
817                 for (i=0; i<len; i++) {
818                     if (mBuffer[i] == endChar) {
819                         break;
820                     }
821                 }
822                 return new String(mBuffer, 0, i);
823             }
824         } catch (java.io.FileNotFoundException e) {
825         } catch (java.io.IOException e) {
826         } finally {
827             if (is != null) {
828                 try {
829                     is.close();
830                 } catch (java.io.IOException e) {
831                 }
832             }
833             StrictMode.setThreadPolicy(savedPolicy);
834         }
835         return null;
836     }
837 
838     private void getName(Stats st, String cmdlineFile) {
839         String newName = st.name;
840         if (st.name == null || st.name.equals("app_process")
841                 || st.name.equals("<pre-initialized>")) {
842             String cmdName = readFile(cmdlineFile, '\0');
843             if (cmdName != null && cmdName.length() > 1) {
844                 newName = cmdName;
845                 int i = newName.lastIndexOf("/");
846                 if (i > 0 && i < newName.length()-1) {
847                     newName = newName.substring(i+1);
848                 }
849             }
850             if (newName == null) {
851                 newName = st.baseName;
852             }
853         }
854         if (st.name == null || !newName.equals(st.name)) {
855             st.name = newName;
856             st.nameWidth = onMeasureProcessName(st.name);
857         }
858     }
859 }
860