• 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.server;
18 
19 import static android.os.Process.*;
20 
21 import android.os.Process;
22 import android.os.SystemClock;
23 import android.util.Config;
24 import android.util.Log;
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 || Config.LOGV;
39 
40     private static final int[] PROCESS_STATS_FORMAT = new int[] {
41         PROC_SPACE_TERM,
42         PROC_SPACE_TERM,
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,
79         PROC_SPACE_TERM,
80         PROC_SPACE_TERM,
81         PROC_SPACE_TERM,
82         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 13: utime
83         PROC_SPACE_TERM|PROC_OUT_LONG                   // 14: stime
84     };
85 
86     private final String[] mProcessFullStatsStringData = new String[3];
87     private final long[] mProcessFullStatsData = new long[3];
88 
89     private static final int[] SYSTEM_CPU_FORMAT = new int[] {
90         PROC_SPACE_TERM|PROC_COMBINE,
91         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 1: user time
92         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 2: nice time
93         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 3: sys time
94         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 4: idle time
95         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 5: iowait time
96         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 6: irq time
97         PROC_SPACE_TERM|PROC_OUT_LONG                   // 7: softirq time
98     };
99 
100     private final long[] mSystemCpuData = new long[7];
101 
102     private static final int[] LOAD_AVERAGE_FORMAT = new int[] {
103         PROC_SPACE_TERM|PROC_OUT_FLOAT,                 // 0: 1 min
104         PROC_SPACE_TERM|PROC_OUT_FLOAT,                 // 1: 5 mins
105         PROC_SPACE_TERM|PROC_OUT_FLOAT                  // 2: 15 mins
106     };
107 
108     private final float[] mLoadAverageData = new float[3];
109 
110     private final boolean mIncludeThreads;
111 
112     private float mLoad1 = 0;
113     private float mLoad5 = 0;
114     private float mLoad15 = 0;
115 
116     private long mCurrentSampleTime;
117     private long mLastSampleTime;
118 
119     private long mBaseUserTime;
120     private long mBaseSystemTime;
121     private long mBaseIoWaitTime;
122     private long mBaseIrqTime;
123     private long mBaseSoftIrqTime;
124     private long mBaseIdleTime;
125     private int mRelUserTime;
126     private int mRelSystemTime;
127     private int mRelIoWaitTime;
128     private int mRelIrqTime;
129     private int mRelSoftIrqTime;
130     private int mRelIdleTime;
131 
132     private int[] mCurPids;
133     private int[] mCurThreadPids;
134 
135     private final ArrayList<Stats> mProcStats = new ArrayList<Stats>();
136     private final ArrayList<Stats> mWorkingProcs = new ArrayList<Stats>();
137     private boolean mWorkingProcsSorted;
138 
139     private boolean mFirst = true;
140 
141     private byte[] mBuffer = new byte[256];
142 
143     /**
144      * The time in microseconds that the CPU has been running at each speed.
145      */
146     private long[] mCpuSpeedTimes;
147 
148     /**
149      * The relative time in microseconds that the CPU has been running at each speed.
150      */
151     private long[] mRelCpuSpeedTimes;
152 
153     /**
154      * The different speeds that the CPU can be running at.
155      */
156     private long[] mCpuSpeeds;
157 
158     public static class Stats {
159         public final int pid;
160         final String statFile;
161         final String cmdlineFile;
162         final String threadsDir;
163         final ArrayList<Stats> threadStats;
164         final ArrayList<Stats> workingThreads;
165 
166         public String baseName;
167         public String name;
168         int nameWidth;
169 
170         public long base_utime;
171         public long base_stime;
172         public int rel_utime;
173         public int rel_stime;
174 
175         public long base_minfaults;
176         public long base_majfaults;
177         public int rel_minfaults;
178         public int rel_majfaults;
179 
180         public boolean active;
181         public boolean added;
182         public boolean removed;
183 
Stats(int _pid, int parentPid, boolean includeThreads)184         Stats(int _pid, int parentPid, boolean includeThreads) {
185             pid = _pid;
186             if (parentPid < 0) {
187                 final File procDir = new File("/proc", Integer.toString(pid));
188                 statFile = new File(procDir, "stat").toString();
189                 cmdlineFile = new File(procDir, "cmdline").toString();
190                 threadsDir = (new File(procDir, "task")).toString();
191                 if (includeThreads) {
192                     threadStats = new ArrayList<Stats>();
193                     workingThreads = new ArrayList<Stats>();
194                 } else {
195                     threadStats = null;
196                     workingThreads = null;
197                 }
198             } else {
199                 final File procDir = new File("/proc", Integer.toString(
200                         parentPid));
201                 final File taskDir = new File(
202                         new File(procDir, "task"), Integer.toString(pid));
203                 statFile = new File(taskDir, "stat").toString();
204                 cmdlineFile = null;
205                 threadsDir = null;
206                 threadStats = null;
207                 workingThreads = null;
208             }
209         }
210     }
211 
212     private final static Comparator<Stats> sLoadComparator = new Comparator<Stats>() {
213         public final int
214         compare(Stats sta, Stats stb)
215         {
216             int ta = sta.rel_utime + sta.rel_stime;
217             int tb = stb.rel_utime + stb.rel_stime;
218             if (ta != tb) {
219                 return ta > tb ? -1 : 1;
220             }
221             if (sta.added != stb.added) {
222                 return sta.added ? -1 : 1;
223             }
224             if (sta.removed != stb.removed) {
225                 return sta.added ? -1 : 1;
226             }
227             return 0;
228         }
229     };
230 
231 
ProcessStats(boolean includeThreads)232     public ProcessStats(boolean includeThreads) {
233         mIncludeThreads = includeThreads;
234     }
235 
onLoadChanged(float load1, float load5, float load15)236     public void onLoadChanged(float load1, float load5, float load15) {
237     }
238 
onMeasureProcessName(String name)239     public int onMeasureProcessName(String name) {
240         return 0;
241     }
242 
init()243     public void init() {
244         mFirst = true;
245         update();
246     }
247 
update()248     public void update() {
249         mLastSampleTime = mCurrentSampleTime;
250         mCurrentSampleTime = SystemClock.uptimeMillis();
251 
252         final float[] loadAverages = mLoadAverageData;
253         if (Process.readProcFile("/proc/loadavg", LOAD_AVERAGE_FORMAT,
254                 null, null, loadAverages)) {
255             float load1 = loadAverages[0];
256             float load5 = loadAverages[1];
257             float load15 = loadAverages[2];
258             if (load1 != mLoad1 || load5 != mLoad5 || load15 != mLoad15) {
259                 mLoad1 = load1;
260                 mLoad5 = load5;
261                 mLoad15 = load15;
262                 onLoadChanged(load1, load5, load15);
263             }
264         }
265 
266         mCurPids = collectStats("/proc", -1, mFirst, mCurPids,
267                 mProcStats, mWorkingProcs);
268         mFirst = false;
269 
270         final long[] sysCpu = mSystemCpuData;
271         if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT,
272                 null, sysCpu, null)) {
273             // Total user time is user + nice time.
274             final long usertime = sysCpu[0]+sysCpu[1];
275             // Total system time is simply system time.
276             final long systemtime = sysCpu[2];
277             // Total idle time is simply idle time.
278             final long idletime = sysCpu[3];
279             // Total irq time is iowait + irq + softirq time.
280             final long iowaittime = sysCpu[4];
281             final long irqtime = sysCpu[5];
282             final long softirqtime = sysCpu[6];
283 
284             mRelUserTime = (int)(usertime - mBaseUserTime);
285             mRelSystemTime = (int)(systemtime - mBaseSystemTime);
286             mRelIoWaitTime = (int)(iowaittime - mBaseIoWaitTime);
287             mRelIrqTime = (int)(irqtime - mBaseIrqTime);
288             mRelSoftIrqTime = (int)(softirqtime - mBaseSoftIrqTime);
289             mRelIdleTime = (int)(idletime - mBaseIdleTime);
290 
291             if (false) {
292                 Log.i("Load", "Total U:" + sysCpu[0] + " N:" + sysCpu[1]
293                       + " S:" + sysCpu[2] + " I:" + sysCpu[3]
294                       + " W:" + sysCpu[4] + " Q:" + sysCpu[5]
295                       + " O:" + sysCpu[6]);
296                 Log.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime
297                       + " I:" + mRelIdleTime + " Q:" + mRelIrqTime);
298             }
299 
300             mBaseUserTime = usertime;
301             mBaseSystemTime = systemtime;
302             mBaseIoWaitTime = iowaittime;
303             mBaseIrqTime = irqtime;
304             mBaseSoftIrqTime = softirqtime;
305             mBaseIdleTime = idletime;
306         }
307 
308         mWorkingProcsSorted = false;
309         mFirst = false;
310     }
311 
collectStats(String statsFile, int parentPid, boolean first, int[] curPids, ArrayList<Stats> allProcs, ArrayList<Stats> workingProcs)312     private int[] collectStats(String statsFile, int parentPid, boolean first,
313             int[] curPids, ArrayList<Stats> allProcs,
314             ArrayList<Stats> workingProcs) {
315 
316         workingProcs.clear();
317 
318         int[] pids = Process.getPids(statsFile, curPids);
319         int NP = (pids == null) ? 0 : pids.length;
320         int NS = allProcs.size();
321         int curStatsIndex = 0;
322         for (int i=0; i<NP; i++) {
323             int pid = pids[i];
324             if (pid < 0) {
325                 NP = pid;
326                 break;
327             }
328             Stats st = curStatsIndex < NS ? allProcs.get(curStatsIndex) : null;
329 
330             if (st != null && st.pid == pid) {
331                 // Update an existing process...
332                 st.added = false;
333                 curStatsIndex++;
334                 if (localLOGV) Log.v(TAG, "Existing pid " + pid + ": " + st);
335 
336                 final long[] procStats = mProcessStatsData;
337                 if (!Process.readProcFile(st.statFile.toString(),
338                         PROCESS_STATS_FORMAT, null, procStats, null)) {
339                     continue;
340                 }
341 
342                 final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS];
343                 final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS];
344                 final long utime = procStats[PROCESS_STAT_UTIME];
345                 final long stime = procStats[PROCESS_STAT_STIME];
346 
347                 if (utime == st.base_utime && stime == st.base_stime) {
348                     st.rel_utime = 0;
349                     st.rel_stime = 0;
350                     st.rel_minfaults = 0;
351                     st.rel_majfaults = 0;
352                     if (st.active) {
353                         st.active = false;
354                     }
355                     continue;
356                 }
357 
358                 if (!st.active) {
359                     st.active = true;
360                 }
361 
362                 if (parentPid < 0) {
363                     getName(st, st.cmdlineFile);
364                     if (st.threadStats != null) {
365                         mCurThreadPids = collectStats(st.threadsDir, pid, false,
366                                 mCurThreadPids, st.threadStats,
367                                 st.workingThreads);
368                     }
369                 }
370 
371                 st.rel_utime = (int)(utime - st.base_utime);
372                 st.rel_stime = (int)(stime - st.base_stime);
373                 st.base_utime = utime;
374                 st.base_stime = stime;
375                 st.rel_minfaults = (int)(minfaults - st.base_minfaults);
376                 st.rel_majfaults = (int)(majfaults - st.base_majfaults);
377                 st.base_minfaults = minfaults;
378                 st.base_majfaults = majfaults;
379                 //Log.i("Load", "Stats changed " + name + " pid=" + st.pid
380                 //      + " name=" + st.name + " utime=" + utime
381                 //      + " stime=" + stime);
382                 workingProcs.add(st);
383                 continue;
384             }
385 
386             if (st == null || st.pid > pid) {
387                 // We have a new process!
388                 st = new Stats(pid, parentPid, mIncludeThreads);
389                 allProcs.add(curStatsIndex, st);
390                 curStatsIndex++;
391                 NS++;
392                 if (localLOGV) Log.v(TAG, "New pid " + pid + ": " + st);
393 
394                 final String[] procStatsString = mProcessFullStatsStringData;
395                 final long[] procStats = mProcessFullStatsData;
396                 if (Process.readProcFile(st.statFile.toString(),
397                         PROCESS_FULL_STATS_FORMAT, procStatsString,
398                         procStats, null)) {
399                     st.baseName = parentPid < 0
400                             ? procStatsString[0] : Integer.toString(pid);
401                     st.base_utime = 0; //procStats[1];
402                     st.base_stime = 0; //procStats[2];
403                     st.base_minfaults = st.base_majfaults = 0;
404                 } else {
405                     st.baseName = "<unknown>";
406                     st.base_utime = st.base_stime = 0;
407                     st.base_minfaults = st.base_majfaults = 0;
408                 }
409 
410                 if (parentPid < 0) {
411                     getName(st, st.cmdlineFile);
412                 } else {
413                     st.name = st.baseName;
414                     st.nameWidth = onMeasureProcessName(st.name);
415                     if (st.threadStats != null) {
416                         mCurThreadPids = collectStats(st.threadsDir, pid, true,
417                                 mCurThreadPids, st.threadStats,
418                                 st.workingThreads);
419                     }
420                 }
421 
422                 //Log.i("Load", "New process: " + st.pid + " " + st.name);
423                 st.rel_utime = 0;
424                 st.rel_stime = 0;
425                 st.rel_minfaults = 0;
426                 st.rel_majfaults = 0;
427                 st.added = true;
428                 if (!first) {
429                     workingProcs.add(st);
430                 }
431                 continue;
432             }
433 
434             // This process has gone away!
435             st.rel_utime = 0;
436             st.rel_stime = 0;
437             st.rel_minfaults = 0;
438             st.rel_majfaults = 0;
439             st.removed = true;
440             workingProcs.add(st);
441             allProcs.remove(curStatsIndex);
442             NS--;
443             if (localLOGV) Log.v(TAG, "Removed pid " + st.pid + ": " + st);
444             // Decrement the loop counter so that we process the current pid
445             // again the next time through the loop.
446             i--;
447             continue;
448         }
449 
450         while (curStatsIndex < NS) {
451             // This process has gone away!
452             final Stats st = allProcs.get(curStatsIndex);
453             st.rel_utime = 0;
454             st.rel_stime = 0;
455             st.rel_minfaults = 0;
456             st.rel_majfaults = 0;
457             st.removed = true;
458             workingProcs.add(st);
459             allProcs.remove(curStatsIndex);
460             NS--;
461             if (localLOGV) Log.v(TAG, "Removed pid " + st.pid + ": " + st);
462         }
463 
464         return pids;
465     }
466 
467     public long getCpuTimeForPid(int pid) {
468         final String statFile = "/proc/" + pid + "/stat";
469         final long[] statsData = mSinglePidStatsData;
470         if (Process.readProcFile(statFile, PROCESS_STATS_FORMAT,
471                 null, statsData, null)) {
472             long time = statsData[PROCESS_STAT_UTIME]
473                     + statsData[PROCESS_STAT_STIME];
474             return time;
475         }
476         return 0;
477     }
478 
479     /**
480      * Returns the times spent at each CPU speed, since the last call to this method. If this
481      * is the first time, it will return 1 for each value.
482      * @return relative times spent at different speed steps.
483      */
484     public long[] getLastCpuSpeedTimes() {
485         if (mCpuSpeedTimes == null) {
486             mCpuSpeedTimes = getCpuSpeedTimes(null);
487             mRelCpuSpeedTimes = new long[mCpuSpeedTimes.length];
488             for (int i = 0; i < mCpuSpeedTimes.length; i++) {
489                 mRelCpuSpeedTimes[i] = 1; // Initialize
490             }
491         } else {
492             getCpuSpeedTimes(mRelCpuSpeedTimes);
493             for (int i = 0; i < mCpuSpeedTimes.length; i++) {
494                 long temp = mRelCpuSpeedTimes[i];
495                 mRelCpuSpeedTimes[i] -= mCpuSpeedTimes[i];
496                 mCpuSpeedTimes[i] = temp;
497             }
498         }
499         return mRelCpuSpeedTimes;
500     }
501 
502     private long[] getCpuSpeedTimes(long[] out) {
503         long[] tempTimes = out;
504         long[] tempSpeeds = mCpuSpeeds;
505         final int MAX_SPEEDS = 20;
506         if (out == null) {
507             tempTimes = new long[MAX_SPEEDS]; // Hopefully no more than that
508             tempSpeeds = new long[MAX_SPEEDS];
509         }
510         int speed = 0;
511         String file = readFile("/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state", '\0');
512         // Note: file may be null on kernels without cpufreq (i.e. the emulator's)
513         if (file != null) {
514             StringTokenizer st = new StringTokenizer(file, "\n ");
515             while (st.hasMoreElements()) {
516                 String token = st.nextToken();
517                 try {
518                     long val = Long.parseLong(token);
519                     tempSpeeds[speed] = val;
520                     token = st.nextToken();
521                     val = Long.parseLong(token);
522                     tempTimes[speed] = val;
523                     speed++;
524                     if (speed == MAX_SPEEDS) break; // No more
525                     if (localLOGV && out == null) {
526                         Log.v(TAG, "First time : Speed/Time = " + tempSpeeds[speed - 1]
527                               + "\t" + tempTimes[speed - 1]);
528                     }
529                 } catch (NumberFormatException nfe) {
530                     Log.i(TAG, "Unable to parse time_in_state");
531                 }
532             }
533         }
534         if (out == null) {
535             out = new long[speed];
536             mCpuSpeeds = new long[speed];
537             System.arraycopy(tempSpeeds, 0, mCpuSpeeds, 0, speed);
538             System.arraycopy(tempTimes, 0, out, 0, speed);
539         }
540         return out;
541     }
542 
543     final public int getLastUserTime() {
544         return mRelUserTime;
545     }
546 
547     final public int getLastSystemTime() {
548         return mRelSystemTime;
549     }
550 
551     final public int getLastIoWaitTime() {
552         return mRelIoWaitTime;
553     }
554 
555     final public int getLastIrqTime() {
556         return mRelIrqTime;
557     }
558 
559     final public int getLastSoftIrqTime() {
560         return mRelSoftIrqTime;
561     }
562 
563     final public int getLastIdleTime() {
564         return mRelIdleTime;
565     }
566 
567     final public float getTotalCpuPercent() {
568         return ((float)(mRelUserTime+mRelSystemTime+mRelIrqTime)*100)
569                 / (mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime);
570     }
571 
572     final public int countWorkingStats() {
573         if (!mWorkingProcsSorted) {
574             Collections.sort(mWorkingProcs, sLoadComparator);
575             mWorkingProcsSorted = true;
576         }
577         return mWorkingProcs.size();
578     }
579 
580     final public Stats getWorkingStats(int index) {
581         return mWorkingProcs.get(index);
582     }
583 
584     final public String printCurrentState() {
585         if (!mWorkingProcsSorted) {
586             Collections.sort(mWorkingProcs, sLoadComparator);
587             mWorkingProcsSorted = true;
588         }
589 
590         StringWriter sw = new StringWriter();
591         PrintWriter pw = new PrintWriter(sw);
592         pw.print("Load: ");
593         pw.print(mLoad1);
594         pw.print(" / ");
595         pw.print(mLoad5);
596         pw.print(" / ");
597         pw.println(mLoad15);
598 
599         long now = SystemClock.uptimeMillis();
600 
601         pw.print("CPU usage from ");
602         pw.print(now-mLastSampleTime);
603         pw.print("ms to ");
604         pw.print(now-mCurrentSampleTime);
605         pw.println("ms ago:");
606 
607         final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime
608                 + mRelIrqTime + mRelSoftIrqTime + mRelIdleTime;
609 
610         int N = mWorkingProcs.size();
611         for (int i=0; i<N; i++) {
612             Stats st = mWorkingProcs.get(i);
613             printProcessCPU(pw, st.added ? " +" : (st.removed ? " -": "  "),
614                     st.name, totalTime, st.rel_utime, st.rel_stime, 0, 0, 0,
615                     st.rel_minfaults, st.rel_majfaults);
616             if (!st.removed && st.workingThreads != null) {
617                 int M = st.workingThreads.size();
618                 for (int j=0; j<M; j++) {
619                     Stats tst = st.workingThreads.get(j);
620                     printProcessCPU(pw,
621                             tst.added ? "   +" : (tst.removed ? "   -": "    "),
622                             tst.name, totalTime, tst.rel_utime, tst.rel_stime,
623                             0, 0, 0, 0, 0);
624                 }
625             }
626         }
627 
628         printProcessCPU(pw, "", "TOTAL", totalTime, mRelUserTime, mRelSystemTime,
629                 mRelIoWaitTime, mRelIrqTime, mRelSoftIrqTime, 0, 0);
630 
631         return sw.toString();
632     }
633 
634     private void printProcessCPU(PrintWriter pw, String prefix, String label, int totalTime,
635             int user, int system, int iowait, int irq, int softIrq, int minFaults, int majFaults) {
636         pw.print(prefix);
637         pw.print(label);
638         pw.print(": ");
639         if (totalTime == 0) totalTime = 1;
640         pw.print(((user+system+iowait+irq+softIrq)*100)/totalTime);
641         pw.print("% = ");
642         pw.print((user*100)/totalTime);
643         pw.print("% user + ");
644         pw.print((system*100)/totalTime);
645         pw.print("% kernel");
646         if (iowait > 0) {
647             pw.print(" + ");
648             pw.print((iowait*100)/totalTime);
649             pw.print("% iowait");
650         }
651         if (irq > 0) {
652             pw.print(" + ");
653             pw.print((irq*100)/totalTime);
654             pw.print("% irq");
655         }
656         if (softIrq > 0) {
657             pw.print(" + ");
658             pw.print((softIrq*100)/totalTime);
659             pw.print("% softirq");
660         }
661         if (minFaults > 0 || majFaults > 0) {
662             pw.print(" / faults:");
663             if (minFaults > 0) {
664                 pw.print(" ");
665                 pw.print(minFaults);
666                 pw.print(" minor");
667             }
668             if (majFaults > 0) {
669                 pw.print(" ");
670                 pw.print(majFaults);
671                 pw.print(" major");
672             }
673         }
674         pw.println();
675     }
676 
677     private String readFile(String file, char endChar) {
678         try {
679             FileInputStream is = new FileInputStream(file);
680             int len = is.read(mBuffer);
681             is.close();
682 
683             if (len > 0) {
684                 int i;
685                 for (i=0; i<len; i++) {
686                     if (mBuffer[i] == endChar) {
687                         break;
688                     }
689                 }
690                 return new String(mBuffer, 0, 0, i);
691             }
692         } catch (java.io.FileNotFoundException e) {
693         } catch (java.io.IOException e) {
694         }
695         return null;
696     }
697 
getName(Stats st, String cmdlineFile)698     private void getName(Stats st, String cmdlineFile) {
699         String newName = st.baseName;
700         if (st.baseName == null || st.baseName.equals("app_process")) {
701             String cmdName = readFile(cmdlineFile, '\0');
702             if (cmdName != null && cmdName.length() > 1) {
703                 newName = cmdName;
704                 int i = newName.lastIndexOf("/");
705                 if (i > 0 && i < newName.length()-1) {
706                     newName = newName.substring(i+1);
707                 }
708             }
709         }
710         if (st.name == null || !newName.equals(st.name)) {
711             st.name = newName;
712             st.nameWidth = onMeasureProcessName(st.name);
713         }
714     }
715 }
716 
717