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