1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import static android.os.Process.*;
20
21import android.os.Process;
22import android.os.SystemClock;
23import android.util.Config;
24import android.util.Log;
25
26import java.io.File;
27import java.io.FileInputStream;
28import java.io.PrintWriter;
29import java.io.StringWriter;
30import java.util.ArrayList;
31import java.util.Collections;
32import java.util.Comparator;
33import java.util.StringTokenizer;
34
35public class ProcessStats {
36    private static final String TAG = "ProcessStats";
37    private static final boolean DEBUG = false;
38    private static final boolean localLOGV = DEBUG || 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
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
232    public ProcessStats(boolean includeThreads) {
233        mIncludeThreads = includeThreads;
234    }
235
236    public void onLoadChanged(float load1, float load5, float load15) {
237    }
238
239    public int onMeasureProcessName(String name) {
240        return 0;
241    }
242
243    public void init() {
244        mFirst = true;
245        update();
246    }
247
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
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
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