[go: nahoru, domu]

1/*
2 * Copyright (C) 2013 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.internal.app.procstats;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.os.SystemClock;
22import android.os.SystemProperties;
23import android.os.UserHandle;
24import android.text.format.DateFormat;
25import android.util.ArrayMap;
26import android.util.ArraySet;
27import android.util.DebugUtils;
28import android.util.Log;
29import android.util.Slog;
30import android.util.SparseArray;
31import android.util.TimeUtils;
32
33import com.android.internal.app.procstats.ProcessStats;
34import com.android.internal.app.procstats.ProcessStats.PackageState;
35import com.android.internal.app.procstats.ProcessStats.ProcessStateHolder;
36import com.android.internal.app.procstats.ProcessStats.TotalMemoryUseCollection;
37import static com.android.internal.app.procstats.ProcessStats.PSS_SAMPLE_COUNT;
38import static com.android.internal.app.procstats.ProcessStats.PSS_MINIMUM;
39import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE;
40import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM;
41import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM;
42import static com.android.internal.app.procstats.ProcessStats.PSS_USS_AVERAGE;
43import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM;
44import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
45import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
46import static com.android.internal.app.procstats.ProcessStats.STATE_PERSISTENT;
47import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
48import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_FOREGROUND;
49import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND;
50import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP;
51import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT;
52import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE;
53import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING;
54import static com.android.internal.app.procstats.ProcessStats.STATE_RECEIVER;
55import static com.android.internal.app.procstats.ProcessStats.STATE_HOME;
56import static com.android.internal.app.procstats.ProcessStats.STATE_LAST_ACTIVITY;
57import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY;
58import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT;
59import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY;
60import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
61
62import dalvik.system.VMRuntime;
63import libcore.util.EmptyArray;
64
65import java.io.IOException;
66import java.io.InputStream;
67import java.io.PrintWriter;
68import java.util.ArrayList;
69import java.util.Arrays;
70import java.util.Collections;
71import java.util.Comparator;
72import java.util.Objects;
73
74public final class ProcessState {
75    private static final String TAG = "ProcessStats";
76    private static final boolean DEBUG = false;
77    private static final boolean DEBUG_PARCEL = false;
78
79    // Map from process states to the states we track.
80    private static final int[] PROCESS_STATE_TO_STATE = new int[] {
81        STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT
82        STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT_UI
83        STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP
84        STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
85        STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
86        STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP_SLEEPING
87        STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
88        STATE_IMPORTANT_BACKGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
89        STATE_BACKUP,                   // ActivityManager.PROCESS_STATE_BACKUP
90        STATE_HEAVY_WEIGHT,             // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
91        STATE_SERVICE,                  // ActivityManager.PROCESS_STATE_SERVICE
92        STATE_RECEIVER,                 // ActivityManager.PROCESS_STATE_RECEIVER
93        STATE_HOME,                     // ActivityManager.PROCESS_STATE_HOME
94        STATE_LAST_ACTIVITY,            // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
95        STATE_CACHED_ACTIVITY,          // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
96        STATE_CACHED_ACTIVITY_CLIENT,   // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
97        STATE_CACHED_EMPTY,             // ActivityManager.PROCESS_STATE_CACHED_EMPTY
98    };
99
100    public static final Comparator<ProcessState> COMPARATOR = new Comparator<ProcessState>() {
101            @Override
102            public int compare(ProcessState lhs, ProcessState rhs) {
103                if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) {
104                    return -1;
105                } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) {
106                    return 1;
107                }
108                return 0;
109            }
110        };
111
112    static class PssAggr {
113        long pss = 0;
114        long samples = 0;
115
116        void add(long newPss, long newSamples) {
117            pss = (long)( (pss*(double)samples) + (newPss*(double)newSamples) )
118                    / (samples+newSamples);
119            samples += newSamples;
120        }
121    }
122
123    // Used by reset to count rather than storing extra maps. Be careful.
124    public int tmpNumInUse;
125    public ProcessState tmpFoundSubProc;
126
127    private final ProcessStats mStats;
128    private final String mName;
129    private final String mPackage;
130    private final int mUid;
131    private final int mVersion;
132    private final DurationsTable mDurations;
133    private final PssTable mPssTable;
134
135    private ProcessState mCommonProcess;
136    private int mCurState = STATE_NOTHING;
137    private long mStartTime;
138
139    private int mLastPssState = STATE_NOTHING;
140    private long mLastPssTime;
141
142    private boolean mActive;
143    private int mNumActiveServices;
144    private int mNumStartedServices;
145
146    private int mNumExcessiveWake;
147    private int mNumExcessiveCpu;
148
149    private int mNumCachedKill;
150    private long mMinCachedKillPss;
151    private long mAvgCachedKillPss;
152    private long mMaxCachedKillPss;
153
154    private boolean mMultiPackage;
155    private boolean mDead;
156
157    // Set in computeProcessTimeLocked and used by COMPARATOR to sort. Be careful.
158    private long mTmpTotalTime;
159
160    /**
161     * Create a new top-level process state, for the initial case where there is only
162     * a single package running in a process.  The initial state is not running.
163     */
164    public ProcessState(ProcessStats processStats, String pkg, int uid, int vers, String name) {
165        mStats = processStats;
166        mName = name;
167        mCommonProcess = this;
168        mPackage = pkg;
169        mUid = uid;
170        mVersion = vers;
171        mDurations = new DurationsTable(processStats.mTableData);
172        mPssTable = new PssTable(processStats.mTableData);
173    }
174
175    /**
176     * Create a new per-package process state for an existing top-level process
177     * state.  The current running state of the top-level process is also copied,
178     * marked as started running at 'now'.
179     */
180    public ProcessState(ProcessState commonProcess, String pkg, int uid, int vers, String name,
181            long now) {
182        mStats = commonProcess.mStats;
183        mName = name;
184        mCommonProcess = commonProcess;
185        mPackage = pkg;
186        mUid = uid;
187        mVersion = vers;
188        mCurState = commonProcess.mCurState;
189        mStartTime = now;
190        mDurations = new DurationsTable(commonProcess.mStats.mTableData);
191        mPssTable = new PssTable(commonProcess.mStats.mTableData);
192    }
193
194    public ProcessState clone(long now) {
195        ProcessState pnew = new ProcessState(this, mPackage, mUid, mVersion, mName, now);
196        pnew.mDurations.addDurations(mDurations);
197        pnew.mPssTable.copyFrom(mPssTable, PSS_COUNT);
198        pnew.mNumExcessiveWake = mNumExcessiveWake;
199        pnew.mNumExcessiveCpu = mNumExcessiveCpu;
200        pnew.mNumCachedKill = mNumCachedKill;
201        pnew.mMinCachedKillPss = mMinCachedKillPss;
202        pnew.mAvgCachedKillPss = mAvgCachedKillPss;
203        pnew.mMaxCachedKillPss = mMaxCachedKillPss;
204        pnew.mActive = mActive;
205        pnew.mNumActiveServices = mNumActiveServices;
206        pnew.mNumStartedServices = mNumStartedServices;
207        return pnew;
208    }
209
210    public String getName() {
211        return mName;
212    }
213
214    public ProcessState getCommonProcess() {
215        return mCommonProcess;
216    }
217
218    /**
219     * Say that we are not part of a shared process, so mCommonProcess = this.
220     */
221    public void makeStandalone() {
222        mCommonProcess = this;
223    }
224
225    public String getPackage() {
226        return mPackage;
227    }
228
229    public int getUid() {
230        return mUid;
231    }
232
233    public int getVersion() {
234        return mVersion;
235    }
236
237    public boolean isMultiPackage() {
238        return mMultiPackage;
239    }
240
241    public void setMultiPackage(boolean val) {
242        mMultiPackage = val;
243    }
244
245    public int getDurationsBucketCount() {
246        return mDurations.getKeyCount();
247    }
248
249    public void add(ProcessState other) {
250        mDurations.addDurations(other.mDurations);
251        mPssTable.mergeStats(other.mPssTable);
252        mNumExcessiveWake += other.mNumExcessiveWake;
253        mNumExcessiveCpu += other.mNumExcessiveCpu;
254        if (other.mNumCachedKill > 0) {
255            addCachedKill(other.mNumCachedKill, other.mMinCachedKillPss,
256                    other.mAvgCachedKillPss, other.mMaxCachedKillPss);
257        }
258    }
259
260    public void resetSafely(long now) {
261        mDurations.resetTable();
262        mPssTable.resetTable();
263        mStartTime = now;
264        mLastPssState = STATE_NOTHING;
265        mLastPssTime = 0;
266        mNumExcessiveWake = 0;
267        mNumExcessiveCpu = 0;
268        mNumCachedKill = 0;
269        mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0;
270    }
271
272    public void makeDead() {
273        mDead = true;
274    }
275
276    private void ensureNotDead() {
277        if (!mDead) {
278            return;
279        }
280        Slog.w(TAG, "ProcessState dead: name=" + mName
281                + " pkg=" + mPackage + " uid=" + mUid + " common.name=" + mCommonProcess.mName);
282    }
283
284    public void writeToParcel(Parcel out, long now) {
285        out.writeInt(mMultiPackage ? 1 : 0);
286        mDurations.writeToParcel(out);
287        mPssTable.writeToParcel(out);
288        out.writeInt(mNumExcessiveWake);
289        out.writeInt(mNumExcessiveCpu);
290        out.writeInt(mNumCachedKill);
291        if (mNumCachedKill > 0) {
292            out.writeLong(mMinCachedKillPss);
293            out.writeLong(mAvgCachedKillPss);
294            out.writeLong(mMaxCachedKillPss);
295        }
296    }
297
298    public boolean readFromParcel(Parcel in, boolean fully) {
299        boolean multiPackage = in.readInt() != 0;
300        if (fully) {
301            mMultiPackage = multiPackage;
302        }
303        if (DEBUG_PARCEL) Slog.d(TAG, "Reading durations table...");
304        if (!mDurations.readFromParcel(in)) {
305            return false;
306        }
307        if (DEBUG_PARCEL) Slog.d(TAG, "Reading pss table...");
308        if (!mPssTable.readFromParcel(in)) {
309            return false;
310        }
311        mNumExcessiveWake = in.readInt();
312        mNumExcessiveCpu = in.readInt();
313        mNumCachedKill = in.readInt();
314        if (mNumCachedKill > 0) {
315            mMinCachedKillPss = in.readLong();
316            mAvgCachedKillPss = in.readLong();
317            mMaxCachedKillPss = in.readLong();
318        } else {
319            mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0;
320        }
321        return true;
322    }
323
324    public void makeActive() {
325        ensureNotDead();
326        mActive = true;
327    }
328
329    public void makeInactive() {
330        mActive = false;
331    }
332
333    public boolean isInUse() {
334        return mActive || mNumActiveServices > 0 || mNumStartedServices > 0
335                || mCurState != STATE_NOTHING;
336    }
337
338    public boolean isActive() {
339        return mActive;
340    }
341
342    public boolean hasAnyData() {
343        return !(mDurations.getKeyCount() == 0
344                && mCurState == STATE_NOTHING
345                && mPssTable.getKeyCount() == 0);
346    }
347
348    /**
349     * Update the current state of the given list of processes.
350     *
351     * @param state Current ActivityManager.PROCESS_STATE_*
352     * @param memFactor Current mem factor constant.
353     * @param now Current time.
354     * @param pkgList Processes to update.
355     */
356    public void setState(int state, int memFactor, long now,
357            ArrayMap<String, ProcessStateHolder> pkgList) {
358        if (state < 0) {
359            state = mNumStartedServices > 0
360                    ? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING;
361        } else {
362            state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT);
363        }
364
365        // First update the common process.
366        mCommonProcess.setState(state, now);
367
368        // If the common process is not multi-package, there is nothing else to do.
369        if (!mCommonProcess.mMultiPackage) {
370            return;
371        }
372
373        if (pkgList != null) {
374            for (int ip=pkgList.size()-1; ip>=0; ip--) {
375                pullFixedProc(pkgList, ip).setState(state, now);
376            }
377        }
378    }
379
380    public void setState(int state, long now) {
381        ensureNotDead();
382        if (mCurState != state) {
383            //Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state);
384            commitStateTime(now);
385            mCurState = state;
386        }
387    }
388
389    public void commitStateTime(long now) {
390        if (mCurState != STATE_NOTHING) {
391            long dur = now - mStartTime;
392            if (dur > 0) {
393                mDurations.addDuration(mCurState, dur);
394            }
395        }
396        mStartTime = now;
397    }
398
399    public void incActiveServices(String serviceName) {
400        if (DEBUG && "".equals(mName)) {
401            RuntimeException here = new RuntimeException("here");
402            here.fillInStackTrace();
403            Slog.d(TAG, "incActiveServices: " + this + " service=" + serviceName
404                    + " to " + (mNumActiveServices+1), here);
405        }
406        if (mCommonProcess != this) {
407            mCommonProcess.incActiveServices(serviceName);
408        }
409        mNumActiveServices++;
410    }
411
412    public void decActiveServices(String serviceName) {
413        if (DEBUG && "".equals(mName)) {
414            RuntimeException here = new RuntimeException("here");
415            here.fillInStackTrace();
416            Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName
417                    + " to " + (mNumActiveServices-1), here);
418        }
419        if (mCommonProcess != this) {
420            mCommonProcess.decActiveServices(serviceName);
421        }
422        mNumActiveServices--;
423        if (mNumActiveServices < 0) {
424            Slog.wtfStack(TAG, "Proc active services underrun: pkg=" + mPackage
425                    + " uid=" + mUid + " proc=" + mName + " service=" + serviceName);
426            mNumActiveServices = 0;
427        }
428    }
429
430    public void incStartedServices(int memFactor, long now, String serviceName) {
431        if (false) {
432            RuntimeException here = new RuntimeException("here");
433            here.fillInStackTrace();
434            Slog.d(TAG, "incStartedServices: " + this + " service=" + serviceName
435                    + " to " + (mNumStartedServices+1), here);
436        }
437        if (mCommonProcess != this) {
438            mCommonProcess.incStartedServices(memFactor, now, serviceName);
439        }
440        mNumStartedServices++;
441        if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) {
442            setState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now);
443        }
444    }
445
446    public void decStartedServices(int memFactor, long now, String serviceName) {
447        if (false) {
448            RuntimeException here = new RuntimeException("here");
449            here.fillInStackTrace();
450            Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName
451                    + " to " + (mNumStartedServices-1), here);
452        }
453        if (mCommonProcess != this) {
454            mCommonProcess.decStartedServices(memFactor, now, serviceName);
455        }
456        mNumStartedServices--;
457        if (mNumStartedServices == 0 && (mCurState%STATE_COUNT) == STATE_SERVICE_RESTARTING) {
458            setState(STATE_NOTHING, now);
459        } else if (mNumStartedServices < 0) {
460            Slog.wtfStack(TAG, "Proc started services underrun: pkg="
461                    + mPackage + " uid=" + mUid + " name=" + mName);
462            mNumStartedServices = 0;
463        }
464    }
465
466    public void addPss(long pss, long uss, boolean always,
467            ArrayMap<String, ProcessStateHolder> pkgList) {
468        ensureNotDead();
469        if (!always) {
470            if (mLastPssState == mCurState && SystemClock.uptimeMillis()
471                    < (mLastPssTime+(30*1000))) {
472                return;
473            }
474        }
475        mLastPssState = mCurState;
476        mLastPssTime = SystemClock.uptimeMillis();
477        if (mCurState != STATE_NOTHING) {
478            // First update the common process.
479            mCommonProcess.mPssTable.mergeStats(mCurState, 1, pss, pss, pss, uss, uss, uss);
480
481            // If the common process is not multi-package, there is nothing else to do.
482            if (!mCommonProcess.mMultiPackage) {
483                return;
484            }
485
486            if (pkgList != null) {
487                for (int ip=pkgList.size()-1; ip>=0; ip--) {
488                    pullFixedProc(pkgList, ip).mPssTable.mergeStats(mCurState, 1,
489                            pss, pss, pss, uss, uss, uss);
490                }
491            }
492        }
493    }
494
495    public void reportExcessiveWake(ArrayMap<String, ProcessStateHolder> pkgList) {
496        ensureNotDead();
497        mCommonProcess.mNumExcessiveWake++;
498        if (!mCommonProcess.mMultiPackage) {
499            return;
500        }
501
502        for (int ip=pkgList.size()-1; ip>=0; ip--) {
503            pullFixedProc(pkgList, ip).mNumExcessiveWake++;
504        }
505    }
506
507    public void reportExcessiveCpu(ArrayMap<String, ProcessStateHolder> pkgList) {
508        ensureNotDead();
509        mCommonProcess.mNumExcessiveCpu++;
510        if (!mCommonProcess.mMultiPackage) {
511            return;
512        }
513
514        for (int ip=pkgList.size()-1; ip>=0; ip--) {
515            pullFixedProc(pkgList, ip).mNumExcessiveCpu++;
516        }
517    }
518
519    private void addCachedKill(int num, long minPss, long avgPss, long maxPss) {
520        if (mNumCachedKill <= 0) {
521            mNumCachedKill = num;
522            mMinCachedKillPss = minPss;
523            mAvgCachedKillPss = avgPss;
524            mMaxCachedKillPss = maxPss;
525        } else {
526            if (minPss < mMinCachedKillPss) {
527                mMinCachedKillPss = minPss;
528            }
529            if (maxPss > mMaxCachedKillPss) {
530                mMaxCachedKillPss = maxPss;
531            }
532            mAvgCachedKillPss = (long)( ((mAvgCachedKillPss*(double)mNumCachedKill) + avgPss)
533                    / (mNumCachedKill+num) );
534            mNumCachedKill += num;
535        }
536    }
537
538    public void reportCachedKill(ArrayMap<String, ProcessStateHolder> pkgList, long pss) {
539        ensureNotDead();
540        mCommonProcess.addCachedKill(1, pss, pss, pss);
541        if (!mCommonProcess.mMultiPackage) {
542            return;
543        }
544
545        for (int ip=pkgList.size()-1; ip>=0; ip--) {
546            pullFixedProc(pkgList, ip).addCachedKill(1, pss, pss, pss);
547        }
548    }
549
550    public ProcessState pullFixedProc(String pkgName) {
551        if (mMultiPackage) {
552            // The array map is still pointing to a common process state
553            // that is now shared across packages.  Update it to point to
554            // the new per-package state.
555            SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid);
556            if (vpkg == null) {
557                throw new IllegalStateException("Didn't find package " + pkgName
558                        + " / " + mUid);
559            }
560            PackageState pkg = vpkg.get(mVersion);
561            if (pkg == null) {
562                throw new IllegalStateException("Didn't find package " + pkgName
563                        + " / " + mUid + " vers " + mVersion);
564            }
565            ProcessState proc = pkg.mProcesses.get(mName);
566            if (proc == null) {
567                throw new IllegalStateException("Didn't create per-package process "
568                        + mName + " in pkg " + pkgName + " / " + mUid + " vers " + mVersion);
569            }
570            return proc;
571        }
572        return this;
573    }
574
575    private ProcessState pullFixedProc(ArrayMap<String, ProcessStateHolder> pkgList,
576            int index) {
577        ProcessStateHolder holder = pkgList.valueAt(index);
578        ProcessState proc = holder.state;
579        if (mDead && proc.mCommonProcess != proc) {
580            // Somehow we are contining to use a process state that is dead, because
581            // it was not being told it was active during the last commit.  We can recover
582            // from this by generating a fresh new state, but this is bad because we
583            // are losing whatever data we had in the old process state.
584            Log.wtf(TAG, "Pulling dead proc: name=" + mName + " pkg=" + mPackage
585                    + " uid=" + mUid + " common.name=" + mCommonProcess.mName);
586            proc = mStats.getProcessStateLocked(proc.mPackage, proc.mUid, proc.mVersion,
587                    proc.mName);
588        }
589        if (proc.mMultiPackage) {
590            // The array map is still pointing to a common process state
591            // that is now shared across packages.  Update it to point to
592            // the new per-package state.
593            SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index),
594                    proc.mUid);
595            if (vpkg == null) {
596                throw new IllegalStateException("No existing package "
597                        + pkgList.keyAt(index) + "/" + proc.mUid
598                        + " for multi-proc " + proc.mName);
599            }
600            PackageState pkg = vpkg.get(proc.mVersion);
601            if (pkg == null) {
602                throw new IllegalStateException("No existing package "
603                        + pkgList.keyAt(index) + "/" + proc.mUid
604                        + " for multi-proc " + proc.mName + " version " + proc.mVersion);
605            }
606            String savedName = proc.mName;
607            proc = pkg.mProcesses.get(proc.mName);
608            if (proc == null) {
609                throw new IllegalStateException("Didn't create per-package process "
610                        + savedName + " in pkg " + pkg.mPackageName + "/" + pkg.mUid);
611            }
612            holder.state = proc;
613        }
614        return proc;
615    }
616
617    public long getDuration(int state, long now) {
618        long time = mDurations.getValueForId((byte)state);
619        if (mCurState == state) {
620            time += now - mStartTime;
621        }
622        return time;
623    }
624
625    public long getPssSampleCount(int state) {
626        return mPssTable.getValueForId((byte)state, PSS_SAMPLE_COUNT);
627    }
628
629    public long getPssMinimum(int state) {
630        return mPssTable.getValueForId((byte)state, PSS_MINIMUM);
631    }
632
633    public long getPssAverage(int state) {
634        return mPssTable.getValueForId((byte)state, PSS_AVERAGE);
635    }
636
637    public long getPssMaximum(int state) {
638        return mPssTable.getValueForId((byte)state, PSS_MAXIMUM);
639    }
640
641    public long getPssUssMinimum(int state) {
642        return mPssTable.getValueForId((byte)state, PSS_USS_MINIMUM);
643    }
644
645    public long getPssUssAverage(int state) {
646        return mPssTable.getValueForId((byte)state, PSS_USS_AVERAGE);
647    }
648
649    public long getPssUssMaximum(int state) {
650        return mPssTable.getValueForId((byte)state, PSS_USS_MAXIMUM);
651    }
652
653    /**
654     * Sums up the PSS data and adds it to 'data'.
655     *
656     * @param data The aggregate data is added here.
657     * @param now SystemClock.uptimeMillis()
658     */
659    public void aggregatePss(TotalMemoryUseCollection data, long now) {
660        final PssAggr fgPss = new PssAggr();
661        final PssAggr bgPss = new PssAggr();
662        final PssAggr cachedPss = new PssAggr();
663        boolean havePss = false;
664        for (int i=0; i<mDurations.getKeyCount(); i++) {
665            final int key = mDurations.getKeyAt(i);
666            int type = SparseMappingTable.getIdFromKey(key);
667            int procState = type % STATE_COUNT;
668            long samples = getPssSampleCount(type);
669            if (samples > 0) {
670                long avg = getPssAverage(type);
671                havePss = true;
672                if (procState <= STATE_IMPORTANT_FOREGROUND) {
673                    fgPss.add(avg, samples);
674                } else if (procState <= STATE_RECEIVER) {
675                    bgPss.add(avg, samples);
676                } else {
677                    cachedPss.add(avg, samples);
678                }
679            }
680        }
681        if (!havePss) {
682            return;
683        }
684        boolean fgHasBg = false;
685        boolean fgHasCached = false;
686        boolean bgHasCached = false;
687        if (fgPss.samples < 3 && bgPss.samples > 0) {
688            fgHasBg = true;
689            fgPss.add(bgPss.pss, bgPss.samples);
690        }
691        if (fgPss.samples < 3 && cachedPss.samples > 0) {
692            fgHasCached = true;
693            fgPss.add(cachedPss.pss, cachedPss.samples);
694        }
695        if (bgPss.samples < 3 && cachedPss.samples > 0) {
696            bgHasCached = true;
697            bgPss.add(cachedPss.pss, cachedPss.samples);
698        }
699        if (bgPss.samples < 3 && !fgHasBg && fgPss.samples > 0) {
700            bgPss.add(fgPss.pss, fgPss.samples);
701        }
702        if (cachedPss.samples < 3 && !bgHasCached && bgPss.samples > 0) {
703            cachedPss.add(bgPss.pss, bgPss.samples);
704        }
705        if (cachedPss.samples < 3 && !fgHasCached && fgPss.samples > 0) {
706            cachedPss.add(fgPss.pss, fgPss.samples);
707        }
708        for (int i=0; i<mDurations.getKeyCount(); i++) {
709            final int key = mDurations.getKeyAt(i);
710            final int type = SparseMappingTable.getIdFromKey(key);
711            long time = mDurations.getValue(key);
712            if (mCurState == type) {
713                time += now - mStartTime;
714            }
715            final int procState = type % STATE_COUNT;
716            data.processStateTime[procState] += time;
717            long samples = getPssSampleCount(type);
718            long avg;
719            if (samples > 0) {
720                avg = getPssAverage(type);
721            } else if (procState <= STATE_IMPORTANT_FOREGROUND) {
722                samples = fgPss.samples;
723                avg = fgPss.pss;
724            } else if (procState <= STATE_RECEIVER) {
725                samples = bgPss.samples;
726                avg = bgPss.pss;
727            } else {
728                samples = cachedPss.samples;
729                avg = cachedPss.pss;
730            }
731            double newAvg = ( (data.processStatePss[procState]
732                    * (double)data.processStateSamples[procState])
733                        + (avg*(double)samples)
734                    ) / (data.processStateSamples[procState]+samples);
735            data.processStatePss[procState] = (long)newAvg;
736            data.processStateSamples[procState] += samples;
737            data.processStateWeight[procState] += avg * (double)time;
738        }
739    }
740
741    public long computeProcessTimeLocked(int[] screenStates, int[] memStates,
742                int[] procStates, long now) {
743        long totalTime = 0;
744        for (int is=0; is<screenStates.length; is++) {
745            for (int im=0; im<memStates.length; im++) {
746                for (int ip=0; ip<procStates.length; ip++) {
747                    int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT)
748                            + procStates[ip];
749                    totalTime += getDuration(bucket, now);
750                }
751            }
752        }
753        mTmpTotalTime = totalTime;
754        return totalTime;
755    }
756
757    public void dumpSummary(PrintWriter pw, String prefix,
758            int[] screenStates, int[] memStates, int[] procStates,
759            long now, long totalTime) {
760        pw.print(prefix);
761        pw.print("* ");
762        pw.print(mName);
763        pw.print(" / ");
764        UserHandle.formatUid(pw, mUid);
765        pw.print(" / v");
766        pw.print(mVersion);
767        pw.println(":");
768        dumpProcessSummaryDetails(pw, prefix, "         TOTAL: ", screenStates, memStates,
769                procStates, now, totalTime, true);
770        dumpProcessSummaryDetails(pw, prefix, "    Persistent: ", screenStates, memStates,
771                new int[] { STATE_PERSISTENT }, now, totalTime, true);
772        dumpProcessSummaryDetails(pw, prefix, "           Top: ", screenStates, memStates,
773                new int[] {STATE_TOP}, now, totalTime, true);
774        dumpProcessSummaryDetails(pw, prefix, "        Imp Fg: ", screenStates, memStates,
775                new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true);
776        dumpProcessSummaryDetails(pw, prefix, "        Imp Bg: ", screenStates, memStates,
777                new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true);
778        dumpProcessSummaryDetails(pw, prefix, "        Backup: ", screenStates, memStates,
779                new int[] {STATE_BACKUP}, now, totalTime, true);
780        dumpProcessSummaryDetails(pw, prefix, "     Heavy Wgt: ", screenStates, memStates,
781                new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true);
782        dumpProcessSummaryDetails(pw, prefix, "       Service: ", screenStates, memStates,
783                new int[] {STATE_SERVICE}, now, totalTime, true);
784        dumpProcessSummaryDetails(pw, prefix, "    Service Rs: ", screenStates, memStates,
785                new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true);
786        dumpProcessSummaryDetails(pw, prefix, "      Receiver: ", screenStates, memStates,
787                new int[] {STATE_RECEIVER}, now, totalTime, true);
788        dumpProcessSummaryDetails(pw, prefix, "        (Home): ", screenStates, memStates,
789                new int[] {STATE_HOME}, now, totalTime, true);
790        dumpProcessSummaryDetails(pw, prefix, "    (Last Act): ", screenStates, memStates,
791                new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
792        dumpProcessSummaryDetails(pw, prefix, "      (Cached): ", screenStates, memStates,
793                new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT,
794                        STATE_CACHED_EMPTY}, now, totalTime, true);
795    }
796
797    public void dumpProcessState(PrintWriter pw, String prefix,
798            int[] screenStates, int[] memStates, int[] procStates, long now) {
799        long totalTime = 0;
800        int printedScreen = -1;
801        for (int is=0; is<screenStates.length; is++) {
802            int printedMem = -1;
803            for (int im=0; im<memStates.length; im++) {
804                for (int ip=0; ip<procStates.length; ip++) {
805                    final int iscreen = screenStates[is];
806                    final int imem = memStates[im];
807                    final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
808                    long time = mDurations.getValueForId((byte)bucket);
809                    String running = "";
810                    if (mCurState == bucket) {
811                        running = " (running)";
812                    }
813                    if (time != 0) {
814                        pw.print(prefix);
815                        if (screenStates.length > 1) {
816                            DumpUtils.printScreenLabel(pw, printedScreen != iscreen
817                                    ? iscreen : STATE_NOTHING);
818                            printedScreen = iscreen;
819                        }
820                        if (memStates.length > 1) {
821                            DumpUtils.printMemLabel(pw,
822                                    printedMem != imem ? imem : STATE_NOTHING, '/');
823                            printedMem = imem;
824                        }
825                        pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": ");
826                        TimeUtils.formatDuration(time, pw); pw.println(running);
827                        totalTime += time;
828                    }
829                }
830            }
831        }
832        if (totalTime != 0) {
833            pw.print(prefix);
834            if (screenStates.length > 1) {
835                DumpUtils.printScreenLabel(pw, STATE_NOTHING);
836            }
837            if (memStates.length > 1) {
838                DumpUtils.printMemLabel(pw, STATE_NOTHING, '/');
839            }
840            pw.print("TOTAL  : ");
841            TimeUtils.formatDuration(totalTime, pw);
842            pw.println();
843        }
844    }
845
846    public void dumpPss(PrintWriter pw, String prefix,
847            int[] screenStates, int[] memStates, int[] procStates) {
848        boolean printedHeader = false;
849        int printedScreen = -1;
850        for (int is=0; is<screenStates.length; is++) {
851            int printedMem = -1;
852            for (int im=0; im<memStates.length; im++) {
853                for (int ip=0; ip<procStates.length; ip++) {
854                    final int iscreen = screenStates[is];
855                    final int imem = memStates[im];
856                    final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
857                    long count = getPssSampleCount(bucket);
858                    if (count > 0) {
859                        if (!printedHeader) {
860                            pw.print(prefix);
861                            pw.print("PSS/USS (");
862                            pw.print(mPssTable.getKeyCount());
863                            pw.println(" entries):");
864                            printedHeader = true;
865                        }
866                        pw.print(prefix);
867                        pw.print("  ");
868                        if (screenStates.length > 1) {
869                            DumpUtils.printScreenLabel(pw,
870                                    printedScreen != iscreen ? iscreen : STATE_NOTHING);
871                            printedScreen = iscreen;
872                        }
873                        if (memStates.length > 1) {
874                            DumpUtils.printMemLabel(pw,
875                                    printedMem != imem ? imem : STATE_NOTHING, '/');
876                            printedMem = imem;
877                        }
878                        pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": ");
879                        pw.print(count);
880                        pw.print(" samples ");
881                        DebugUtils.printSizeValue(pw, getPssMinimum(bucket) * 1024);
882                        pw.print(" ");
883                        DebugUtils.printSizeValue(pw, getPssAverage(bucket) * 1024);
884                        pw.print(" ");
885                        DebugUtils.printSizeValue(pw, getPssMaximum(bucket) * 1024);
886                        pw.print(" / ");
887                        DebugUtils.printSizeValue(pw, getPssUssMinimum(bucket) * 1024);
888                        pw.print(" ");
889                        DebugUtils.printSizeValue(pw, getPssUssAverage(bucket) * 1024);
890                        pw.print(" ");
891                        DebugUtils.printSizeValue(pw, getPssUssMaximum(bucket) * 1024);
892                        pw.println();
893                    }
894                }
895            }
896        }
897        if (mNumExcessiveWake != 0) {
898            pw.print(prefix); pw.print("Killed for excessive wake locks: ");
899                    pw.print(mNumExcessiveWake); pw.println(" times");
900        }
901        if (mNumExcessiveCpu != 0) {
902            pw.print(prefix); pw.print("Killed for excessive CPU use: ");
903                    pw.print(mNumExcessiveCpu); pw.println(" times");
904        }
905        if (mNumCachedKill != 0) {
906            pw.print(prefix); pw.print("Killed from cached state: ");
907                    pw.print(mNumCachedKill); pw.print(" times from pss ");
908                    DebugUtils.printSizeValue(pw, mMinCachedKillPss * 1024); pw.print("-");
909                    DebugUtils.printSizeValue(pw, mAvgCachedKillPss * 1024); pw.print("-");
910                    DebugUtils.printSizeValue(pw, mMaxCachedKillPss * 1024); pw.println();
911        }
912    }
913
914    private void dumpProcessSummaryDetails(PrintWriter pw, String prefix,
915            String label, int[] screenStates, int[] memStates, int[] procStates,
916            long now, long totalTime, boolean full) {
917        ProcessStats.ProcessDataCollection totals = new ProcessStats.ProcessDataCollection(
918                screenStates, memStates, procStates);
919        computeProcessData(totals, now);
920        final double percentage = (double) totals.totalTime / (double) totalTime * 100;
921        // We don't print percentages < .01, so just drop those.
922        if (percentage >= 0.005 || totals.numPss != 0) {
923            if (prefix != null) {
924                pw.print(prefix);
925            }
926            if (label != null) {
927                pw.print(label);
928            }
929            totals.print(pw, totalTime, full);
930            if (prefix != null) {
931                pw.println();
932            }
933        }
934    }
935
936    public void dumpInternalLocked(PrintWriter pw, String prefix, boolean dumpAll) {
937        if (dumpAll) {
938            pw.print(prefix); pw.print("myID=");
939                    pw.print(Integer.toHexString(System.identityHashCode(this)));
940                    pw.print(" mCommonProcess=");
941                    pw.print(Integer.toHexString(System.identityHashCode(mCommonProcess)));
942                    pw.print(" mPackage="); pw.println(mPackage);
943            if (mMultiPackage) {
944                pw.print(prefix); pw.print("mMultiPackage="); pw.println(mMultiPackage);
945            }
946            if (this != mCommonProcess) {
947                pw.print(prefix); pw.print("Common Proc: "); pw.print(mCommonProcess.mName);
948                        pw.print("/"); pw.print(mCommonProcess.mUid);
949                        pw.print(" pkg="); pw.println(mCommonProcess.mPackage);
950            }
951        }
952        if (mActive) {
953            pw.print(prefix); pw.print("mActive="); pw.println(mActive);
954        }
955        if (mDead) {
956            pw.print(prefix); pw.print("mDead="); pw.println(mDead);
957        }
958        if (mNumActiveServices != 0 || mNumStartedServices != 0) {
959            pw.print(prefix); pw.print("mNumActiveServices="); pw.print(mNumActiveServices);
960                    pw.print(" mNumStartedServices=");
961                    pw.println(mNumStartedServices);
962        }
963    }
964
965    public void computeProcessData(ProcessStats.ProcessDataCollection data, long now) {
966        data.totalTime = 0;
967        data.numPss = data.minPss = data.avgPss = data.maxPss =
968                data.minUss = data.avgUss = data.maxUss = 0;
969        for (int is=0; is<data.screenStates.length; is++) {
970            for (int im=0; im<data.memStates.length; im++) {
971                for (int ip=0; ip<data.procStates.length; ip++) {
972                    int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT)
973                            + data.procStates[ip];
974                    data.totalTime += getDuration(bucket, now);
975                    long samples = getPssSampleCount(bucket);
976                    if (samples > 0) {
977                        long minPss = getPssMinimum(bucket);
978                        long avgPss = getPssAverage(bucket);
979                        long maxPss = getPssMaximum(bucket);
980                        long minUss = getPssUssMinimum(bucket);
981                        long avgUss = getPssUssAverage(bucket);
982                        long maxUss = getPssUssMaximum(bucket);
983                        if (data.numPss == 0) {
984                            data.minPss = minPss;
985                            data.avgPss = avgPss;
986                            data.maxPss = maxPss;
987                            data.minUss = minUss;
988                            data.avgUss = avgUss;
989                            data.maxUss = maxUss;
990                        } else {
991                            if (minPss < data.minPss) {
992                                data.minPss = minPss;
993                            }
994                            data.avgPss = (long)( ((data.avgPss*(double)data.numPss)
995                                    + (avgPss*(double)samples)) / (data.numPss+samples) );
996                            if (maxPss > data.maxPss) {
997                                data.maxPss = maxPss;
998                            }
999                            if (minUss < data.minUss) {
1000                                data.minUss = minUss;
1001                            }
1002                            data.avgUss = (long)( ((data.avgUss*(double)data.numPss)
1003                                    + (avgUss*(double)samples)) / (data.numPss+samples) );
1004                            if (maxUss > data.maxUss) {
1005                                data.maxUss = maxUss;
1006                            }
1007                        }
1008                        data.numPss += samples;
1009                    }
1010                }
1011            }
1012        }
1013    }
1014
1015    public void dumpCsv(PrintWriter pw,
1016            boolean sepScreenStates, int[] screenStates, boolean sepMemStates,
1017            int[] memStates, boolean sepProcStates, int[] procStates, long now) {
1018        final int NSS = sepScreenStates ? screenStates.length : 1;
1019        final int NMS = sepMemStates ? memStates.length : 1;
1020        final int NPS = sepProcStates ? procStates.length : 1;
1021        for (int iss=0; iss<NSS; iss++) {
1022            for (int ims=0; ims<NMS; ims++) {
1023                for (int ips=0; ips<NPS; ips++) {
1024                    final int vsscreen = sepScreenStates ? screenStates[iss] : 0;
1025                    final int vsmem = sepMemStates ? memStates[ims] : 0;
1026                    final int vsproc = sepProcStates ? procStates[ips] : 0;
1027                    final int NSA = sepScreenStates ? 1 : screenStates.length;
1028                    final int NMA = sepMemStates ? 1 : memStates.length;
1029                    final int NPA = sepProcStates ? 1 : procStates.length;
1030                    long totalTime = 0;
1031                    for (int isa=0; isa<NSA; isa++) {
1032                        for (int ima=0; ima<NMA; ima++) {
1033                            for (int ipa=0; ipa<NPA; ipa++) {
1034                                final int vascreen = sepScreenStates ? 0 : screenStates[isa];
1035                                final int vamem = sepMemStates ? 0 : memStates[ima];
1036                                final int vaproc = sepProcStates ? 0 : procStates[ipa];
1037                                final int bucket = ((vsscreen + vascreen + vsmem + vamem)
1038                                        * STATE_COUNT) + vsproc + vaproc;
1039                                totalTime += getDuration(bucket, now);
1040                            }
1041                        }
1042                    }
1043                    pw.print(DumpUtils.CSV_SEP);
1044                    pw.print(totalTime);
1045                }
1046            }
1047        }
1048    }
1049
1050    public void dumpPackageProcCheckin(PrintWriter pw, String pkgName, int uid, int vers,
1051            String itemName, long now) {
1052        pw.print("pkgproc,");
1053        pw.print(pkgName);
1054        pw.print(",");
1055        pw.print(uid);
1056        pw.print(",");
1057        pw.print(vers);
1058        pw.print(",");
1059        pw.print(DumpUtils.collapseString(pkgName, itemName));
1060        dumpAllStateCheckin(pw, now);
1061        pw.println();
1062        if (mPssTable.getKeyCount() > 0) {
1063            pw.print("pkgpss,");
1064            pw.print(pkgName);
1065            pw.print(",");
1066            pw.print(uid);
1067            pw.print(",");
1068            pw.print(vers);
1069            pw.print(",");
1070            pw.print(DumpUtils.collapseString(pkgName, itemName));
1071            dumpAllPssCheckin(pw);
1072            pw.println();
1073        }
1074        if (mNumExcessiveWake > 0 || mNumExcessiveCpu > 0 || mNumCachedKill > 0) {
1075            pw.print("pkgkills,");
1076            pw.print(pkgName);
1077            pw.print(",");
1078            pw.print(uid);
1079            pw.print(",");
1080            pw.print(vers);
1081            pw.print(",");
1082            pw.print(DumpUtils.collapseString(pkgName, itemName));
1083            pw.print(",");
1084            pw.print(mNumExcessiveWake);
1085            pw.print(",");
1086            pw.print(mNumExcessiveCpu);
1087            pw.print(",");
1088            pw.print(mNumCachedKill);
1089            pw.print(",");
1090            pw.print(mMinCachedKillPss);
1091            pw.print(":");
1092            pw.print(mAvgCachedKillPss);
1093            pw.print(":");
1094            pw.print(mMaxCachedKillPss);
1095            pw.println();
1096        }
1097    }
1098
1099    public void dumpProcCheckin(PrintWriter pw, String procName, int uid, long now) {
1100        if (mDurations.getKeyCount() > 0) {
1101            pw.print("proc,");
1102            pw.print(procName);
1103            pw.print(",");
1104            pw.print(uid);
1105            dumpAllStateCheckin(pw, now);
1106            pw.println();
1107        }
1108        if (mPssTable.getKeyCount() > 0) {
1109            pw.print("pss,");
1110            pw.print(procName);
1111            pw.print(",");
1112            pw.print(uid);
1113            dumpAllPssCheckin(pw);
1114            pw.println();
1115        }
1116        if (mNumExcessiveWake > 0 || mNumExcessiveCpu > 0 || mNumCachedKill > 0) {
1117            pw.print("kills,");
1118            pw.print(procName);
1119            pw.print(",");
1120            pw.print(uid);
1121            pw.print(",");
1122            pw.print(mNumExcessiveWake);
1123            pw.print(",");
1124            pw.print(mNumExcessiveCpu);
1125            pw.print(",");
1126            pw.print(mNumCachedKill);
1127            pw.print(",");
1128            pw.print(mMinCachedKillPss);
1129            pw.print(":");
1130            pw.print(mAvgCachedKillPss);
1131            pw.print(":");
1132            pw.print(mMaxCachedKillPss);
1133            pw.println();
1134        }
1135    }
1136
1137    public void dumpAllStateCheckin(PrintWriter pw, long now) {
1138        boolean didCurState = false;
1139        for (int i=0; i<mDurations.getKeyCount(); i++) {
1140            final int key = mDurations.getKeyAt(i);
1141            final int type = SparseMappingTable.getIdFromKey(key);
1142            long time = mDurations.getValue(key);
1143            if (mCurState == type) {
1144                didCurState = true;
1145                time += now - mStartTime;
1146            }
1147            DumpUtils.printProcStateTagAndValue(pw, type, time);
1148        }
1149        if (!didCurState && mCurState != STATE_NOTHING) {
1150            DumpUtils.printProcStateTagAndValue(pw, mCurState, now - mStartTime);
1151        }
1152    }
1153
1154    public void dumpAllPssCheckin(PrintWriter pw) {
1155        final int N = mPssTable.getKeyCount();
1156        for (int i=0; i<N; i++) {
1157            final int key = mPssTable.getKeyAt(i);
1158            final int type = SparseMappingTable.getIdFromKey(key);
1159            pw.print(',');
1160            DumpUtils.printProcStateTag(pw, type);
1161            pw.print(':');
1162            pw.print(mPssTable.getValue(key, PSS_SAMPLE_COUNT));
1163            pw.print(':');
1164            pw.print(mPssTable.getValue(key, PSS_MINIMUM));
1165            pw.print(':');
1166            pw.print(mPssTable.getValue(key, PSS_AVERAGE));
1167            pw.print(':');
1168            pw.print(mPssTable.getValue(key, PSS_MAXIMUM));
1169            pw.print(':');
1170            pw.print(mPssTable.getValue(key, PSS_USS_MINIMUM));
1171            pw.print(':');
1172            pw.print(mPssTable.getValue(key, PSS_USS_AVERAGE));
1173            pw.print(':');
1174            pw.print(mPssTable.getValue(key, PSS_USS_MAXIMUM));
1175        }
1176    }
1177
1178    public String toString() {
1179        StringBuilder sb = new StringBuilder(128);
1180        sb.append("ProcessState{").append(Integer.toHexString(System.identityHashCode(this)))
1181                .append(" ").append(mName).append("/").append(mUid)
1182                .append(" pkg=").append(mPackage);
1183        if (mMultiPackage) sb.append(" (multi)");
1184        if (mCommonProcess != this) sb.append(" (sub)");
1185        sb.append("}");
1186        return sb.toString();
1187    }
1188}
1189