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