diff options
-rw-r--r-- | core/java/android/os/Binder.java | 17 | ||||
-rw-r--r-- | core/java/com/android/internal/os/ProcessCpuTracker.java (renamed from core/java/com/android/internal/os/ProcessStats.java) | 78 | ||||
-rw-r--r-- | packages/SystemUI/src/com/android/systemui/LoadAverageService.java | 18 | ||||
-rw-r--r-- | services/java/com/android/internal/app/ProcessStats.java | 2437 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActiveServices.java | 31 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 141 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityStack.java | 10 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityStackSupervisor.java | 2 | ||||
-rw-r--r-- | services/java/com/android/server/am/BroadcastQueue.java | 2 | ||||
-rw-r--r-- | services/java/com/android/server/am/ProcessRecord.java | 15 | ||||
-rw-r--r-- | services/java/com/android/server/am/ProcessStatsService.java | 714 | ||||
-rw-r--r-- | services/java/com/android/server/am/ProcessTracker.java | 3126 | ||||
-rw-r--r-- | services/java/com/android/server/am/ServiceRecord.java | 7 |
13 files changed, 3320 insertions, 3278 deletions
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 7b91418..4627c88 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -296,7 +296,21 @@ public class Binder implements IBinder { disabled = sDumpDisabled; } if (disabled == null) { - dump(fd, pw, args); + try { + dump(fd, pw, args); + } catch (SecurityException e) { + pw.println(); + pw.println("Security exception: " + e.getMessage()); + throw e; + } catch (Throwable e) { + // Unlike usual calls, in this case if an exception gets thrown + // back to us we want to print it back in to the dump data, since + // that is where the caller expects all interesting information to + // go. + pw.println(); + pw.println("Exception occurred while dumping:"); + e.printStackTrace(pw); + } } else { pw.println(sDumpDisabled); } @@ -443,7 +457,6 @@ final class BinderProxy implements IBinder { data.writeStringArray(args); try { transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY); - reply.readException(); } finally { data.recycle(); reply.recycle(); diff --git a/core/java/com/android/internal/os/ProcessStats.java b/core/java/com/android/internal/os/ProcessCpuTracker.java index 874bc0e..c092807 100644 --- a/core/java/com/android/internal/os/ProcessStats.java +++ b/core/java/com/android/internal/os/ProcessCpuTracker.java @@ -34,11 +34,11 @@ import java.util.Collections; import java.util.Comparator; import java.util.StringTokenizer; -public class ProcessStats { +public class ProcessCpuTracker { private static final String TAG = "ProcessStats"; private static final boolean DEBUG = false; private static final boolean localLOGV = DEBUG || false; - + private static final int[] PROCESS_STATS_FORMAT = new int[] { PROC_SPACE_TERM, PROC_SPACE_TERM|PROC_PARENS, @@ -61,7 +61,7 @@ public class ProcessStats { static final int PROCESS_STAT_MAJOR_FAULTS = 1; static final int PROCESS_STAT_UTIME = 2; static final int PROCESS_STAT_STIME = 3; - + /** Stores user time and system time in 100ths of a second. */ private final long[] mProcessStatsData = new long[4]; /** Stores user time and system time in 100ths of a second. */ @@ -123,14 +123,14 @@ public class ProcessStats { private final float[] mLoadAverageData = new float[3]; private final boolean mIncludeThreads; - + private float mLoad1 = 0; private float mLoad5 = 0; private float mLoad15 = 0; - + private long mCurrentSampleTime; private long mLastSampleTime; - + private long mCurrentSampleRealTime; private long mLastSampleRealTime; @@ -149,7 +149,7 @@ public class ProcessStats { private int[] mCurPids; private int[] mCurThreadPids; - + private final ArrayList<Stats> mProcStats = new ArrayList<Stats>(); private final ArrayList<Stats> mWorkingProcs = new ArrayList<Stats>(); private boolean mWorkingProcsSorted; @@ -202,12 +202,12 @@ public class ProcessStats { public long base_majfaults; public int rel_minfaults; public int rel_majfaults; - + public boolean active; public boolean working; public boolean added; public boolean removed; - + Stats(int _pid, int parentPid, boolean includeThreads) { pid = _pid; if (parentPid < 0) { @@ -256,30 +256,30 @@ public class ProcessStats { }; - public ProcessStats(boolean includeThreads) { + public ProcessCpuTracker(boolean includeThreads) { mIncludeThreads = includeThreads; } - + public void onLoadChanged(float load1, float load5, float load15) { } - + public int onMeasureProcessName(String name) { return 0; } - + public void init() { if (DEBUG) Slog.v(TAG, "Init: " + this); mFirst = true; update(); } - + public void update() { if (DEBUG) Slog.v(TAG, "Update: " + this); mLastSampleTime = mCurrentSampleTime; mCurrentSampleTime = SystemClock.uptimeMillis(); mLastSampleRealTime = mCurrentSampleRealTime; mCurrentSampleRealTime = SystemClock.elapsedRealtime(); - + final long[] sysCpu = mSystemCpuData; if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null, sysCpu, null)) { @@ -339,11 +339,11 @@ public class ProcessStats { mWorkingProcsSorted = false; mFirst = false; - } - + } + private int[] collectStats(String statsFile, int parentPid, boolean first, int[] curPids, ArrayList<Stats> allProcs) { - + int[] pids = Process.getPids(statsFile, curPids); int NP = (pids == null) ? 0 : pids.length; int NS = allProcs.size(); @@ -355,7 +355,7 @@ public class ProcessStats { break; } Stats st = curStatsIndex < NS ? allProcs.get(curStatsIndex) : null; - + if (st != null && st.pid == pid) { // Update an existing process... st.added = false; @@ -373,7 +373,7 @@ public class ProcessStats { PROCESS_STATS_FORMAT, null, procStats, null)) { continue; } - + final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS]; final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS]; final long utime = procStats[PROCESS_STAT_UTIME]; @@ -423,7 +423,7 @@ public class ProcessStats { continue; } - + if (st == null || st.pid > pid) { // We have a new process! st = new Stats(pid, parentPid, mIncludeThreads); @@ -477,7 +477,7 @@ public class ProcessStats { if (DEBUG) Slog.v("Load", "Stats added " + st.name + " pid=" + st.pid + " utime=" + st.base_utime + " stime=" + st.base_stime + " minfaults=" + st.base_minfaults + " majfaults=" + st.base_majfaults); - + st.rel_utime = 0; st.rel_stime = 0; st.rel_minfaults = 0; @@ -488,7 +488,7 @@ public class ProcessStats { } continue; } - + // This process has gone away! st.rel_utime = 0; st.rel_stime = 0; @@ -520,7 +520,7 @@ public class ProcessStats { NS--; if (localLOGV) Slog.v(TAG, "Removed pid " + st.pid + ": " + st); } - + return pids; } @@ -607,27 +607,27 @@ public class ProcessStats { final public int getLastUserTime() { return mRelUserTime; } - + final public int getLastSystemTime() { return mRelSystemTime; } - + final public int getLastIoWaitTime() { return mRelIoWaitTime; } - + final public int getLastIrqTime() { return mRelIrqTime; } - + final public int getLastSoftIrqTime() { return mRelSoftIrqTime; } - + final public int getLastIdleTime() { return mRelIdleTime; } - + final public float getTotalCpuPercent() { int denom = mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime; if (denom <= 0) { @@ -635,7 +635,7 @@ public class ProcessStats { } return ((float)(mRelUserTime+mRelSystemTime+mRelIrqTime)*100) / denom; } - + final void buildWorkingProcs() { if (!mWorkingProcsSorted) { mWorkingProcs.clear(); @@ -678,7 +678,7 @@ public class ProcessStats { final public Stats getWorkingStats(int index) { return mWorkingProcs.get(index); } - + final public String printCurrentLoad() { StringWriter sw = new StringWriter(); PrintWriter pw = new FastPrintWriter(sw, false, 128); @@ -694,10 +694,10 @@ public class ProcessStats { final public String printCurrentState(long now) { buildWorkingProcs(); - + StringWriter sw = new StringWriter(); PrintWriter pw = new FastPrintWriter(sw, false, 1024); - + pw.print("CPU usage from "); if (now > mLastSampleTime) { pw.print(now-mLastSampleTime); @@ -720,10 +720,10 @@ public class ProcessStats { pw.print("% awake"); } pw.println(":"); - + final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime + mRelIrqTime + mRelSoftIrqTime + mRelIdleTime; - + if (DEBUG) Slog.i(TAG, "totalTime " + totalTime + " over sample time " + (mCurrentSampleTime-mLastSampleTime)); @@ -744,14 +744,14 @@ public class ProcessStats { } } } - + printProcessCPU(pw, "", -1, "TOTAL", totalTime, mRelUserTime, mRelSystemTime, mRelIoWaitTime, mRelIrqTime, mRelSoftIrqTime, 0, 0); pw.flush(); return sw.toString(); } - + private void printRatio(PrintWriter pw, long numerator, long denominator) { long thousands = (numerator*1000)/denominator; long hundreds = thousands/10; @@ -812,7 +812,7 @@ public class ProcessStats { } pw.println(); } - + private String readFile(String file, char endChar) { // Permit disk reads here, as /proc/meminfo isn't really "on // disk" and should be fast. TODO: make BlockGuard ignore diff --git a/packages/SystemUI/src/com/android/systemui/LoadAverageService.java b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java index efbee5b..610e42b 100644 --- a/packages/SystemUI/src/com/android/systemui/LoadAverageService.java +++ b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java @@ -29,18 +29,18 @@ import android.view.Gravity; import android.view.View; import android.view.WindowManager; -import com.android.internal.os.ProcessStats; +import com.android.internal.os.ProcessCpuTracker; public class LoadAverageService extends Service { private View mView; - private static final class Stats extends ProcessStats { + private static final class CpuTracker extends ProcessCpuTracker { String mLoadText; int mLoadWidth; private final Paint mPaint; - Stats(Paint paint) { + CpuTracker(Paint paint) { super(false); mPaint = paint; } @@ -70,7 +70,7 @@ public class LoadAverageService extends Service { } }; - private final Stats mStats; + private final CpuTracker mStats; private Paint mLoadPaint; private Paint mAddedPaint; @@ -150,7 +150,7 @@ public class LoadAverageService extends Service { float descent = mLoadPaint.descent(); mFH = (int)(descent - mAscent + .5f); - mStats = new Stats(mLoadPaint); + mStats = new CpuTracker(mLoadPaint); mStats.init(); updateDisplay(); } @@ -179,7 +179,7 @@ public class LoadAverageService extends Service { final int W = mNeededWidth; final int RIGHT = getWidth()-1; - final Stats stats = mStats; + final CpuTracker stats = mStats; final int userTime = stats.getLastUserTime(); final int systemTime = stats.getLastSystemTime(); final int iowaitTime = stats.getLastIoWaitTime(); @@ -226,7 +226,7 @@ public class LoadAverageService extends Service { int N = stats.countWorkingStats(); for (int i=0; i<N; i++) { - Stats.Stats st = stats.getWorkingStats(i); + CpuTracker.Stats st = stats.getWorkingStats(i); y += mFH; top += mFH; bottom += mFH; @@ -259,12 +259,12 @@ public class LoadAverageService extends Service { } void updateDisplay() { - final Stats stats = mStats; + final CpuTracker stats = mStats; final int NW = stats.countWorkingStats(); int maxWidth = stats.mLoadWidth; for (int i=0; i<NW; i++) { - Stats.Stats st = stats.getWorkingStats(i); + CpuTracker.Stats st = stats.getWorkingStats(i); if (st.nameWidth > maxWidth) { maxWidth = st.nameWidth; } diff --git a/services/java/com/android/internal/app/ProcessStats.java b/services/java/com/android/internal/app/ProcessStats.java new file mode 100644 index 0000000..e916b56 --- /dev/null +++ b/services/java/com/android/internal/app/ProcessStats.java @@ -0,0 +1,2437 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.os.Parcel; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.text.format.DateFormat; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; +import android.webkit.WebViewFactory; +import com.android.internal.util.ArrayUtils; +import com.android.server.ProcessMap; +import dalvik.system.VMRuntime; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; + +final public class ProcessStats { + static final String TAG = "ProcessStats"; + static final boolean DEBUG = false; + + public static final int STATE_NOTHING = -1; + public static final int STATE_PERSISTENT = 0; + public static final int STATE_TOP = 1; + public static final int STATE_IMPORTANT_FOREGROUND = 2; + public static final int STATE_IMPORTANT_BACKGROUND = 3; + public static final int STATE_BACKUP = 4; + public static final int STATE_HEAVY_WEIGHT = 5; + public static final int STATE_SERVICE = 6; + public static final int STATE_SERVICE_RESTARTING = 7; + public static final int STATE_RECEIVER = 8; + public static final int STATE_HOME = 9; + public static final int STATE_LAST_ACTIVITY = 10; + public static final int STATE_CACHED_ACTIVITY = 11; + public static final int STATE_CACHED_ACTIVITY_CLIENT = 12; + public static final int STATE_CACHED_EMPTY = 13; + public static final int STATE_COUNT = STATE_CACHED_EMPTY+1; + + public static final int PSS_SAMPLE_COUNT = 0; + public static final int PSS_MINIMUM = 1; + public static final int PSS_AVERAGE = 2; + public static final int PSS_MAXIMUM = 3; + public static final int PSS_USS_MINIMUM = 4; + public static final int PSS_USS_AVERAGE = 5; + public static final int PSS_USS_MAXIMUM = 6; + public static final int PSS_COUNT = PSS_USS_MAXIMUM+1; + + public static final int ADJ_NOTHING = -1; + public static final int ADJ_MEM_FACTOR_NORMAL = 0; + public static final int ADJ_MEM_FACTOR_MODERATE = 1; + public static final int ADJ_MEM_FACTOR_LOW = 2; + public static final int ADJ_MEM_FACTOR_CRITICAL = 3; + public static final int ADJ_MEM_FACTOR_COUNT = ADJ_MEM_FACTOR_CRITICAL+1; + public static final int ADJ_SCREEN_MOD = ADJ_MEM_FACTOR_COUNT; + public static final int ADJ_SCREEN_OFF = 0; + public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD; + public static final int ADJ_COUNT = ADJ_SCREEN_ON*2; + + public static final int FLAG_COMPLETE = 1<<0; + public static final int FLAG_SHUTDOWN = 1<<1; + public static final int FLAG_SYSPROPS = 1<<2; + + static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, ADJ_MEM_FACTOR_MODERATE, + ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL }; + static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON }; + static final int[] NON_CACHED_PROC_STATES = new int[] { STATE_PERSISTENT, + STATE_TOP, STATE_IMPORTANT_FOREGROUND, + STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT, + STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, STATE_HOME + }; + + // Map from process states to the states we track. + static final int[] PROCESS_STATE_TO_STATE = new int[] { + STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT + STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI + STATE_TOP, // ActivityManager.PROCESS_STATE_TOP + STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND + STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND + STATE_BACKUP, // ActivityManager.PROCESS_STATE_BACKUP + STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT + STATE_SERVICE, // ActivityManager.PROCESS_STATE_SERVICE + STATE_RECEIVER, // ActivityManager.PROCESS_STATE_RECEIVER + STATE_HOME, // ActivityManager.PROCESS_STATE_HOME + STATE_LAST_ACTIVITY, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY + STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY + STATE_CACHED_ACTIVITY_CLIENT, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT + STATE_CACHED_EMPTY, // ActivityManager.PROCESS_STATE_CACHED_EMPTY + }; + + public static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, + STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, + STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, + STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY, + STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY + }; + + static final String[] STATE_NAMES = new String[] { + "Persistent", "Top ", "Imp Fg ", "Imp Bg ", + "Backup ", "Heavy Wght", "Service ", "Service Rs", + "Receiver ", "Home ", + "Last Act ", "Cch Act ", "Cch CliAct", "Cch Empty " + }; + + public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] { + "off", "on" + }; + + public static final String[] ADJ_MEM_NAMES_CSV = new String[] { + "norm", "mod", "low", "crit" + }; + + public static final String[] STATE_NAMES_CSV = new String[] { + "pers", "top", "impfg", "impbg", "backup", "heavy", + "service", "service-rs", "receiver", "home", "lastact", + "cch-activity", "cch-aclient", "cch-empty" + }; + + static final String[] ADJ_SCREEN_TAGS = new String[] { + "0", "1" + }; + + static final String[] ADJ_MEM_TAGS = new String[] { + "n", "m", "l", "c" + }; + + static final String[] STATE_TAGS = new String[] { + "p", "t", "f", "b", "u", "w", + "s", "x", "r", "h", "l", "a", "c", "e" + }; + + static final String CSV_SEP = "\t"; + + // Current version of the parcel format. + private static final int PARCEL_VERSION = 9; + // In-memory Parcel magic number, used to detect attempts to unmarshall bad data + private static final int MAGIC = 0x50535453; + + // Where the "type"/"state" part of the data appears in an offset integer. + static int OFFSET_TYPE_SHIFT = 0; + static int OFFSET_TYPE_MASK = 0xff; + // Where the "which array" part of the data appears in an offset integer. + static int OFFSET_ARRAY_SHIFT = 8; + static int OFFSET_ARRAY_MASK = 0xff; + // Where the "index into array" part of the data appears in an offset integer. + static int OFFSET_INDEX_SHIFT = 16; + static int OFFSET_INDEX_MASK = 0xffff; + + public String mReadError; + public String mTimePeriodStartClockStr; + public int mFlags; + + public final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>(); + public final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>(); + + public final long[] mMemFactorDurations = new long[ADJ_COUNT]; + public int mMemFactor = STATE_NOTHING; + public long mStartTime; + + public long mTimePeriodStartClock; + public long mTimePeriodStartRealtime; + public long mTimePeriodEndRealtime; + String mRuntime; + String mWebView; + boolean mRunning; + + static final int LONGS_SIZE = 4096; + final ArrayList<long[]> mLongs = new ArrayList<long[]>(); + int mNextLong; + + int[] mAddLongTable; + int mAddLongTableSize; + + public ProcessStats(boolean running) { + mRunning = running; + reset(); + } + + static private void printScreenLabel(PrintWriter pw, int offset) { + switch (offset) { + case ADJ_NOTHING: + pw.print(" "); + break; + case ADJ_SCREEN_OFF: + pw.print("Screen Off / "); + break; + case ADJ_SCREEN_ON: + pw.print("Screen On / "); + break; + default: + pw.print("?????????? / "); + break; + } + } + + static public void printScreenLabelCsv(PrintWriter pw, int offset) { + switch (offset) { + case ADJ_NOTHING: + break; + case ADJ_SCREEN_OFF: + pw.print(ADJ_SCREEN_NAMES_CSV[0]); + break; + case ADJ_SCREEN_ON: + pw.print(ADJ_SCREEN_NAMES_CSV[1]); + break; + default: + pw.print("???"); + break; + } + } + + static private void printMemLabel(PrintWriter pw, int offset) { + switch (offset) { + case ADJ_NOTHING: + pw.print(" "); + break; + case ADJ_MEM_FACTOR_NORMAL: + pw.print("Norm / "); + break; + case ADJ_MEM_FACTOR_MODERATE: + pw.print("Mod / "); + break; + case ADJ_MEM_FACTOR_LOW: + pw.print("Low / "); + break; + case ADJ_MEM_FACTOR_CRITICAL: + pw.print("Crit / "); + break; + default: + pw.print("???? / "); + break; + } + } + + static public void printMemLabelCsv(PrintWriter pw, int offset) { + if (offset >= ADJ_MEM_FACTOR_NORMAL) { + if (offset <= ADJ_MEM_FACTOR_CRITICAL) { + pw.print(ADJ_MEM_NAMES_CSV[offset]); + } else { + pw.print("???"); + } + } + } + + static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations, + int curState, long curStartTime, long now) { + long totalTime = 0; + int printedScreen = -1; + for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { + int printedMem = -1; + for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { + int state = imem+iscreen; + long time = durations[state]; + String running = ""; + if (curState == state) { + time += now - curStartTime; + if (pw != null) { + running = " (running)"; + } + } + if (time != 0) { + if (pw != null) { + pw.print(prefix); + printScreenLabel(pw, printedScreen != iscreen + ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); + printedMem = imem; + TimeUtils.formatDuration(time, pw); pw.println(running); + } + totalTime += time; + } + } + } + if (totalTime != 0 && pw != null) { + pw.print(prefix); + printScreenLabel(pw, STATE_NOTHING); + pw.print("TOTAL: "); + TimeUtils.formatDuration(totalTime, pw); + pw.println(); + } + return totalTime; + } + + static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations, + int curState, long curStartTime, long now) { + for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { + for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { + int state = imem+iscreen; + long time = durations[state]; + if (curState == state) { + time += now - curStartTime; + } + if (time != 0) { + printAdjTagAndValue(pw, state, time); + } + } + } + } + + static void dumpServiceTimeCheckin(PrintWriter pw, String label, String packageName, + int uid, String serviceName, ServiceState svc, int serviceType, int opCount, + int curState, long curStartTime, long now) { + if (opCount <= 0) { + return; + } + pw.print(label); + pw.print(","); + pw.print(packageName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(serviceName); + pw.print(","); + pw.print(opCount); + boolean didCurState = false; + for (int i=0; i<svc.mDurationsTableSize; i++) { + int off = svc.mDurationsTable[i]; + int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + int memFactor = type / ServiceState.SERVICE_COUNT; + type %= ServiceState.SERVICE_COUNT; + if (type != serviceType) { + continue; + } + long time = svc.mProcessStats.getLong(off, 0); + if (curState == memFactor) { + didCurState = true; + time += now - curStartTime; + } + printAdjTagAndValue(pw, memFactor, time); + } + if (!didCurState && curState != STATE_NOTHING) { + printAdjTagAndValue(pw, curState, now - curStartTime); + } + pw.println(); + } + + static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) { + data.totalTime = 0; + data.numPss = data.minPss = data.avgPss = data.maxPss = + data.minUss = data.avgUss = data.maxUss = 0; + for (int is=0; is<data.screenStates.length; is++) { + for (int im=0; im<data.memStates.length; im++) { + for (int ip=0; ip<data.procStates.length; ip++) { + int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT) + + data.procStates[ip]; + data.totalTime += proc.getDuration(bucket, now); + long samples = proc.getPssSampleCount(bucket); + if (samples > 0) { + long minPss = proc.getPssMinimum(bucket); + long avgPss = proc.getPssAverage(bucket); + long maxPss = proc.getPssMaximum(bucket); + long minUss = proc.getPssUssMinimum(bucket); + long avgUss = proc.getPssUssAverage(bucket); + long maxUss = proc.getPssUssMaximum(bucket); + if (data.numPss == 0) { + data.minPss = minPss; + data.avgPss = avgPss; + data.maxPss = maxPss; + data.minUss = minUss; + data.avgUss = avgUss; + data.maxUss = maxUss; + } else { + if (minPss < data.minPss) { + data.minPss = minPss; + } + data.avgPss = (long)( ((data.avgPss*(double)data.numPss) + + (avgPss*(double)samples)) / (data.numPss+samples) ); + if (maxPss > data.maxPss) { + data.maxPss = maxPss; + } + if (minUss < data.minUss) { + data.minUss = minUss; + } + data.avgUss = (long)( ((data.avgUss*(double)data.numPss) + + (avgUss*(double)samples)) / (data.numPss+samples) ); + if (maxUss > data.maxUss) { + data.maxUss = maxUss; + } + } + data.numPss += samples; + } + } + } + } + } + + static long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates, + int[] procStates, long now) { + long totalTime = 0; + /* + for (int i=0; i<proc.mDurationsTableSize; i++) { + int val = proc.mDurationsTable[i]; + totalTime += proc.mState.getLong(val, 0); + if ((val&0xff) == proc.mCurState) { + totalTime += now - proc.mStartTime; + } + } + */ + for (int is=0; is<screenStates.length; is++) { + for (int im=0; im<memStates.length; im++) { + for (int ip=0; ip<procStates.length; ip++) { + int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT) + + procStates[ip]; + totalTime += proc.getDuration(bucket, now); + } + } + } + proc.mTmpTotalTime = totalTime; + return totalTime; + } + + static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc, + int[] screenStates, int[] memStates, int[] procStates, long now) { + long totalTime = 0; + int printedScreen = -1; + for (int is=0; is<screenStates.length; is++) { + int printedMem = -1; + for (int im=0; im<memStates.length; im++) { + for (int ip=0; ip<procStates.length; ip++) { + final int iscreen = screenStates[is]; + final int imem = memStates[im]; + final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip]; + long time = proc.getDuration(bucket, now); + String running = ""; + if (proc.mCurState == bucket) { + running = " (running)"; + } + if (time != 0) { + pw.print(prefix); + if (screenStates.length > 1) { + printScreenLabel(pw, printedScreen != iscreen + ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + } + if (memStates.length > 1) { + printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); + printedMem = imem; + } + pw.print(STATE_NAMES[procStates[ip]]); pw.print(": "); + TimeUtils.formatDuration(time, pw); pw.println(running); + totalTime += time; + } + } + } + } + if (totalTime != 0) { + pw.print(prefix); + if (screenStates.length > 1) { + printScreenLabel(pw, STATE_NOTHING); + } + if (memStates.length > 1) { + printMemLabel(pw, STATE_NOTHING); + } + pw.print("TOTAL : "); + TimeUtils.formatDuration(totalTime, pw); + pw.println(); + } + } + + static void dumpProcessPss(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates, + int[] memStates, int[] procStates) { + boolean printedHeader = false; + int printedScreen = -1; + for (int is=0; is<screenStates.length; is++) { + int printedMem = -1; + for (int im=0; im<memStates.length; im++) { + for (int ip=0; ip<procStates.length; ip++) { + final int iscreen = screenStates[is]; + final int imem = memStates[im]; + final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip]; + long count = proc.getPssSampleCount(bucket); + if (count > 0) { + if (!printedHeader) { + pw.print(prefix); + pw.print("PSS/USS ("); + pw.print(proc.mPssTableSize); + pw.println(" entries):"); + printedHeader = true; + } + pw.print(prefix); + pw.print(" "); + if (screenStates.length > 1) { + printScreenLabel(pw, printedScreen != iscreen + ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + } + if (memStates.length > 1) { + printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); + printedMem = imem; + } + pw.print(STATE_NAMES[procStates[ip]]); pw.print(": "); + pw.print(count); + pw.print(" samples "); + printSizeValue(pw, proc.getPssMinimum(bucket) * 1024); + pw.print(" "); + printSizeValue(pw, proc.getPssAverage(bucket) * 1024); + pw.print(" "); + printSizeValue(pw, proc.getPssMaximum(bucket) * 1024); + pw.print(" / "); + printSizeValue(pw, proc.getPssUssMinimum(bucket) * 1024); + pw.print(" "); + printSizeValue(pw, proc.getPssUssAverage(bucket) * 1024); + pw.print(" "); + printSizeValue(pw, proc.getPssUssMaximum(bucket) * 1024); + pw.println(); + } + } + } + } + if (proc.mNumExcessiveWake != 0) { + pw.print(prefix); pw.print("Killed for excessive wake locks: "); + pw.print(proc.mNumExcessiveWake); pw.println(" times"); + } + if (proc.mNumExcessiveCpu != 0) { + pw.print(prefix); pw.print("Killed for excessive CPU use: "); + pw.print(proc.mNumExcessiveCpu); pw.println(" times"); + } + } + + static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates, + int[] memStates, int[] procStates) { + final int NS = screenStates != null ? screenStates.length : 1; + final int NM = memStates != null ? memStates.length : 1; + final int NP = procStates != null ? procStates.length : 1; + for (int is=0; is<NS; is++) { + for (int im=0; im<NM; im++) { + for (int ip=0; ip<NP; ip++) { + pw.print(sep); + boolean printed = false; + if (screenStates != null && screenStates.length > 1) { + printScreenLabelCsv(pw, screenStates[is]); + printed = true; + } + if (memStates != null && memStates.length > 1) { + if (printed) { + pw.print("-"); + } + printMemLabelCsv(pw, memStates[im]); + printed = true; + } + if (procStates != null && procStates.length > 1) { + if (printed) { + pw.print("-"); + } + pw.print(STATE_NAMES_CSV[procStates[ip]]); + } + } + } + } + } + + static void dumpProcessStateCsv(PrintWriter pw, ProcessState proc, + boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, + boolean sepProcStates, int[] procStates, long now) { + final int NSS = sepScreenStates ? screenStates.length : 1; + final int NMS = sepMemStates ? memStates.length : 1; + final int NPS = sepProcStates ? procStates.length : 1; + for (int iss=0; iss<NSS; iss++) { + for (int ims=0; ims<NMS; ims++) { + for (int ips=0; ips<NPS; ips++) { + final int vsscreen = sepScreenStates ? screenStates[iss] : 0; + final int vsmem = sepMemStates ? memStates[ims] : 0; + final int vsproc = sepProcStates ? procStates[ips] : 0; + final int NSA = sepScreenStates ? 1 : screenStates.length; + final int NMA = sepMemStates ? 1 : memStates.length; + final int NPA = sepProcStates ? 1 : procStates.length; + long totalTime = 0; + for (int isa=0; isa<NSA; isa++) { + for (int ima=0; ima<NMA; ima++) { + for (int ipa=0; ipa<NPA; ipa++) { + final int vascreen = sepScreenStates ? 0 : screenStates[isa]; + final int vamem = sepMemStates ? 0 : memStates[ima]; + final int vaproc = sepProcStates ? 0 : procStates[ipa]; + final int bucket = ((vsscreen + vascreen + vsmem + vamem) + * STATE_COUNT) + vsproc + vaproc; + totalTime += proc.getDuration(bucket, now); + } + } + } + pw.print(CSV_SEP); + pw.print(totalTime); + } + } + } + } + + static void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs, + int[] screenStates, int[] memStates, int[] procStates, long now) { + String innerPrefix = prefix + " "; + for (int i=procs.size()-1; i>=0; i--) { + ProcessState proc = procs.get(i); + pw.print(prefix); + pw.print(proc.mName); + pw.print(" / "); + UserHandle.formatUid(pw, proc.mUid); + pw.print(" ("); + pw.print(proc.mDurationsTableSize); + pw.print(" entries)"); + pw.println(":"); + dumpProcessState(pw, innerPrefix, proc, screenStates, memStates, procStates, now); + if (proc.mPssTableSize > 0) { + dumpProcessPss(pw, innerPrefix, proc, screenStates, memStates, procStates); + } + } + } + + static void dumpProcessSummaryDetails(PrintWriter pw, ProcessState proc, String prefix, + String label, int[] screenStates, int[] memStates, int[] procStates, + long now, long totalTime, boolean full) { + ProcessDataCollection totals = new ProcessDataCollection(screenStates, + memStates, procStates); + computeProcessData(proc, totals, now); + if (totals.totalTime != 0 || totals.numPss != 0) { + if (prefix != null) { + pw.print(prefix); + } + if (label != null) { + pw.print(label); + } + totals.print(pw, totalTime, full); + if (prefix != null) { + pw.println(); + } + } + } + + static void dumpProcessSummaryLocked(PrintWriter pw, String prefix, + ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates, + long now, long totalTime) { + for (int i=procs.size()-1; i>=0; i--) { + ProcessState proc = procs.get(i); + pw.print(prefix); + pw.print("* "); + pw.print(proc.mName); + pw.print(" / "); + UserHandle.formatUid(pw, proc.mUid); + pw.println(":"); + dumpProcessSummaryDetails(pw, proc, prefix, " TOTAL: ", screenStates, memStates, + procStates, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Persistent: ", screenStates, memStates, + new int[] { STATE_PERSISTENT }, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Top: ", screenStates, memStates, + new int[] {STATE_TOP}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Imp Fg: ", screenStates, memStates, + new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Imp Bg: ", screenStates, memStates, + new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Backup: ", screenStates, memStates, + new int[] {STATE_BACKUP}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Heavy Wgt: ", screenStates, memStates, + new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Service: ", screenStates, memStates, + new int[] {STATE_SERVICE}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Service Rs: ", screenStates, memStates, + new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Receiver: ", screenStates, memStates, + new int[] {STATE_RECEIVER}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Home: ", screenStates, memStates, + new int[] {STATE_HOME}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " (Last Act): ", screenStates, memStates, + new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " (Cached): ", screenStates, memStates, + new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT, + STATE_CACHED_EMPTY}, now, totalTime, true); + } + } + + static void printPercent(PrintWriter pw, double fraction) { + fraction *= 100; + if (fraction < 1) { + pw.print(String.format("%.2f", fraction)); + } else if (fraction < 10) { + pw.print(String.format("%.1f", fraction)); + } else { + pw.print(String.format("%.0f", fraction)); + } + pw.print("%"); + } + + static void printSizeValue(PrintWriter pw, long number) { + float result = number; + String suffix = ""; + if (result > 900) { + suffix = "KB"; + result = result / 1024; + } + if (result > 900) { + suffix = "MB"; + result = result / 1024; + } + if (result > 900) { + suffix = "GB"; + result = result / 1024; + } + if (result > 900) { + suffix = "TB"; + result = result / 1024; + } + if (result > 900) { + suffix = "PB"; + result = result / 1024; + } + String value; + if (result < 1) { + value = String.format("%.2f", result); + } else if (result < 10) { + value = String.format("%.1f", result); + } else if (result < 100) { + value = String.format("%.0f", result); + } else { + value = String.format("%.0f", result); + } + pw.print(value); + pw.print(suffix); + } + + public static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs, + boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, + boolean sepProcStates, int[] procStates, long now) { + pw.print("process"); + pw.print(CSV_SEP); + pw.print("uid"); + dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null, + sepMemStates ? memStates : null, + sepProcStates ? procStates : null); + pw.println(); + for (int i=procs.size()-1; i>=0; i--) { + ProcessState proc = procs.get(i); + pw.print(proc.mName); + pw.print(CSV_SEP); + UserHandle.formatUid(pw, proc.mUid); + dumpProcessStateCsv(pw, proc, sepScreenStates, screenStates, + sepMemStates, memStates, sepProcStates, procStates, now); + pw.println(); + } + } + + static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) { + int index = value/mod; + if (index >= 0 && index < array.length) { + pw.print(array[index]); + } else { + pw.print('?'); + } + return value - index*mod; + } + + static void printProcStateTag(PrintWriter pw, int state) { + state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD*STATE_COUNT); + state = printArrayEntry(pw, ADJ_MEM_TAGS, state, STATE_COUNT); + printArrayEntry(pw, STATE_TAGS, state, 1); + } + + static void printAdjTag(PrintWriter pw, int state) { + state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD); + printArrayEntry(pw, ADJ_MEM_TAGS, state, 1); + } + + static void printProcStateTagAndValue(PrintWriter pw, int state, long value) { + pw.print(','); + printProcStateTag(pw, state); + pw.print(':'); + pw.print(value); + } + + static void printAdjTagAndValue(PrintWriter pw, int state, long value) { + pw.print(','); + printAdjTag(pw, state); + pw.print(':'); + pw.print(value); + } + + static void dumpAllProcessStateCheckin(PrintWriter pw, ProcessState proc, long now) { + boolean didCurState = false; + for (int i=0; i<proc.mDurationsTableSize; i++) { + int off = proc.mDurationsTable[i]; + int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + long time = proc.mProcessStats.getLong(off, 0); + if (proc.mCurState == type) { + didCurState = true; + time += now - proc.mStartTime; + } + printProcStateTagAndValue(pw, type, time); + } + if (!didCurState && proc.mCurState != STATE_NOTHING) { + printProcStateTagAndValue(pw, proc.mCurState, now - proc.mStartTime); + } + } + + static void dumpAllProcessPssCheckin(PrintWriter pw, ProcessState proc) { + for (int i=0; i<proc.mPssTableSize; i++) { + int off = proc.mPssTable[i]; + int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + long count = proc.mProcessStats.getLong(off, PSS_SAMPLE_COUNT); + long min = proc.mProcessStats.getLong(off, PSS_MINIMUM); + long avg = proc.mProcessStats.getLong(off, PSS_AVERAGE); + long max = proc.mProcessStats.getLong(off, PSS_MAXIMUM); + long umin = proc.mProcessStats.getLong(off, PSS_USS_MINIMUM); + long uavg = proc.mProcessStats.getLong(off, PSS_USS_AVERAGE); + long umax = proc.mProcessStats.getLong(off, PSS_USS_MAXIMUM); + pw.print(','); + printProcStateTag(pw, type); + pw.print(':'); + pw.print(count); + pw.print(':'); + pw.print(min); + pw.print(':'); + pw.print(avg); + pw.print(':'); + pw.print(max); + pw.print(':'); + pw.print(umin); + pw.print(':'); + pw.print(uavg); + pw.print(':'); + pw.print(umax); + } + } + + public void reset() { + if (DEBUG) Slog.d(TAG, "Resetting state of " + mTimePeriodStartClockStr); + resetCommon(); + mPackages.getMap().clear(); + mProcesses.getMap().clear(); + mMemFactor = STATE_NOTHING; + mStartTime = 0; + if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr); + } + + public void resetSafely() { + if (DEBUG) Slog.d(TAG, "Safely resetting state of " + mTimePeriodStartClockStr); + resetCommon(); + long now = SystemClock.uptimeMillis(); + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + for (int ip=procMap.size()-1; ip>=0; ip--) { + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=uids.size()-1; iu>=0; iu--) { + uids.valueAt(iu).resetSafely(now); + } + } + ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); + for (int ip=pkgMap.size()-1; ip>=0; ip--) { + SparseArray<PackageState> uids = pkgMap.valueAt(ip); + for (int iu=uids.size()-1; iu>=0; iu--) { + PackageState pkgState = uids.valueAt(iu); + for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) { + pkgState.mProcesses.valueAt(iproc).resetSafely(now); + } + for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) { + ServiceState ss = pkgState.mServices.valueAt(isvc); + if (ss.isActive()) { + pkgState.mServices.valueAt(isvc).resetSafely(now); + } else { + pkgState.mServices.removeAt(isvc); + } + } + } + } + mStartTime = SystemClock.uptimeMillis(); + if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr); + } + + private void resetCommon() { + mTimePeriodStartClock = System.currentTimeMillis(); + buildTimePeriodStartClockStr(); + mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); + mLongs.clear(); + mLongs.add(new long[LONGS_SIZE]); + mNextLong = 0; + Arrays.fill(mMemFactorDurations, 0); + mStartTime = 0; + mReadError = null; + mFlags = 0; + evaluateSystemProperties(true); + } + + public boolean evaluateSystemProperties(boolean update) { + boolean changed = false; + String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib", + VMRuntime.getRuntime().vmLibrary()); + if (!Objects.equals(runtime, mRuntime)) { + changed = true; + if (update) { + mRuntime = runtime; + } + } + String webview = WebViewFactory.useExperimentalWebView() ? "chromeview" : "webview"; + if (!Objects.equals(webview, mWebView)) { + changed = true; + if (update) { + mWebView = webview; + } + } + return changed; + } + + private void buildTimePeriodStartClockStr() { + mTimePeriodStartClockStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss", + mTimePeriodStartClock).toString(); + } + + static final int[] BAD_TABLE = new int[0]; + + private int[] readTableFromParcel(Parcel in, String name, String what) { + final int size = in.readInt(); + if (size < 0) { + Slog.w(TAG, "Ignoring existing stats; bad " + what + " table size: " + size); + return BAD_TABLE; + } + if (size == 0) { + return null; + } + final int[] table = new int[size]; + for (int i=0; i<size; i++) { + table[i] = in.readInt(); + if (DEBUG) Slog.i(TAG, "Reading in " + name + " table #" + i + ": " + + ProcessStats.printLongOffset(table[i])); + if (!validateLongOffset(table[i])) { + Slog.w(TAG, "Ignoring existing stats; bad " + what + " table entry: " + + ProcessStats.printLongOffset(table[i])); + return null; + } + } + return table; + } + + public void writeToParcel(Parcel out) { + long now = SystemClock.uptimeMillis(); + out.writeInt(MAGIC); + out.writeInt(PARCEL_VERSION); + out.writeInt(STATE_COUNT); + out.writeInt(ADJ_COUNT); + out.writeInt(PSS_COUNT); + out.writeInt(LONGS_SIZE); + + out.writeLong(mTimePeriodStartClock); + out.writeLong(mTimePeriodStartRealtime); + out.writeLong(mTimePeriodEndRealtime); + out.writeString(mRuntime); + out.writeString(mWebView); + out.writeInt(mFlags); + + out.writeInt(mLongs.size()); + out.writeInt(mNextLong); + for (int i=0; i<(mLongs.size()-1); i++) { + out.writeLongArray(mLongs.get(i)); + } + long[] lastLongs = mLongs.get(mLongs.size()-1); + for (int i=0; i<mNextLong; i++) { + out.writeLong(lastLongs[i]); + if (DEBUG) Slog.d(TAG, "Writing last long #" + i + ": " + lastLongs[i]); + } + + if (mMemFactor != STATE_NOTHING) { + mMemFactorDurations[mMemFactor] += now - mStartTime; + mStartTime = now; + } + out.writeLongArray(mMemFactorDurations); + + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + final int NPROC = procMap.size(); + out.writeInt(NPROC); + for (int ip=0; ip<NPROC; ip++) { + out.writeString(procMap.keyAt(ip)); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + final int NUID = uids.size(); + out.writeInt(NUID); + for (int iu=0; iu<NUID; iu++) { + out.writeInt(uids.keyAt(iu)); + ProcessState proc = uids.valueAt(iu); + out.writeString(proc.mPackage); + proc.writeToParcel(out, now); + } + } + ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); + final int NPKG = pkgMap.size(); + out.writeInt(NPKG); + for (int ip=0; ip<NPKG; ip++) { + out.writeString(pkgMap.keyAt(ip)); + SparseArray<PackageState> uids = pkgMap.valueAt(ip); + final int NUID = uids.size(); + out.writeInt(NUID); + for (int iu=0; iu<NUID; iu++) { + out.writeInt(uids.keyAt(iu)); + PackageState pkgState = uids.valueAt(iu); + final int NPROCS = pkgState.mProcesses.size(); + out.writeInt(NPROCS); + for (int iproc=0; iproc<NPROCS; iproc++) { + out.writeString(pkgState.mProcesses.keyAt(iproc)); + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (proc.mCommonProcess == proc) { + // This is the same as the common process we wrote above. + out.writeInt(0); + } else { + // There is separate data for this package's process. + out.writeInt(1); + proc.writeToParcel(out, now); + } + } + final int NSRVS = pkgState.mServices.size(); + out.writeInt(NSRVS); + for (int isvc=0; isvc<NSRVS; isvc++) { + out.writeString(pkgState.mServices.keyAt(isvc)); + ServiceState svc = pkgState.mServices.valueAt(isvc); + svc.writeToParcel(out, now); + } + } + } + } + + private boolean readCheckedInt(Parcel in, int val, String what) { + int got; + if ((got=in.readInt()) != val) { + mReadError = "bad " + what + ": " + got; + return false; + } + return true; + } + + public void readFromParcel(Parcel in) { + final boolean hadData = mPackages.getMap().size() > 0 + || mProcesses.getMap().size() > 0; + if (hadData) { + resetSafely(); + } + + if (!readCheckedInt(in, MAGIC, "magic number")) { + return; + } + int version = in.readInt(); + if (version != PARCEL_VERSION && version != 6) { + mReadError = "bad version: " + version; + return; + } + if (!readCheckedInt(in, STATE_COUNT, "state count")) { + return; + } + if (!readCheckedInt(in, ADJ_COUNT, "adj count")) { + return; + } + if (!readCheckedInt(in, PSS_COUNT, "pss count")) { + return; + } + if (!readCheckedInt(in, LONGS_SIZE, "longs size")) { + return; + } + + mTimePeriodStartClock = in.readLong(); + buildTimePeriodStartClockStr(); + mTimePeriodStartRealtime = in.readLong(); + mTimePeriodEndRealtime = in.readLong(); + if (version == PARCEL_VERSION) { + mRuntime = in.readString(); + mWebView = in.readString(); + } + mFlags = in.readInt(); + + final int NLONGS = in.readInt(); + final int NEXTLONG = in.readInt(); + mLongs.clear(); + for (int i=0; i<(NLONGS-1); i++) { + while (i >= mLongs.size()) { + mLongs.add(new long[LONGS_SIZE]); + } + in.readLongArray(mLongs.get(i)); + } + long[] longs = new long[LONGS_SIZE]; + mNextLong = NEXTLONG; + for (int i=0; i<NEXTLONG; i++) { + longs[i] = in.readLong(); + if (DEBUG) Slog.d(TAG, "Reading last long #" + i + ": " + longs[i]); + } + mLongs.add(longs); + + in.readLongArray(mMemFactorDurations); + + int NPROC = in.readInt(); + if (NPROC < 0) { + mReadError = "bad process count: " + NPROC; + return; + } + while (NPROC > 0) { + NPROC--; + String procName = in.readString(); + if (procName == null) { + mReadError = "bad process name"; + return; + } + int NUID = in.readInt(); + if (NUID < 0) { + mReadError = "bad uid count: " + NUID; + return; + } + while (NUID > 0) { + NUID--; + int uid = in.readInt(); + if (uid < 0) { + mReadError = "bad uid: " + uid; + return; + } + String pkgName = in.readString(); + if (pkgName == null) { + mReadError = "bad process package name"; + return; + } + ProcessState proc = hadData ? mProcesses.get(procName, uid) : null; + if (proc != null) { + if (!proc.readFromParcel(in, false)) { + return; + } + } else { + proc = new ProcessState(this, pkgName, uid, procName); + if (!proc.readFromParcel(in, true)) { + return; + } + } + if (DEBUG) Slog.d(TAG, "Adding process: " + procName + " " + uid + " " + proc); + mProcesses.put(procName, uid, proc); + } + } + + if (DEBUG) Slog.d(TAG, "Read " + mProcesses.getMap().size() + " processes"); + + int NPKG = in.readInt(); + if (NPKG < 0) { + mReadError = "bad package count: " + NPKG; + return; + } + while (NPKG > 0) { + NPKG--; + String pkgName = in.readString(); + if (pkgName == null) { + mReadError = "bad package name"; + return; + } + int NUID = in.readInt(); + if (NUID < 0) { + mReadError = "bad uid count: " + NUID; + return; + } + while (NUID > 0) { + NUID--; + int uid = in.readInt(); + if (uid < 0) { + mReadError = "bad uid: " + uid; + return; + } + PackageState pkgState = new PackageState(uid); + mPackages.put(pkgName, uid, pkgState); + int NPROCS = in.readInt(); + if (NPROCS < 0) { + mReadError = "bad package process count: " + NPROCS; + return; + } + while (NPROCS > 0) { + NPROCS--; + String procName = in.readString(); + if (procName == null) { + mReadError = "bad package process name"; + return; + } + int hasProc = in.readInt(); + if (DEBUG) Slog.d(TAG, "Reading package " + pkgName + " " + uid + + " process " + procName + " hasProc=" + hasProc); + ProcessState commonProc = mProcesses.get(procName, uid); + if (DEBUG) Slog.d(TAG, "Got common proc " + procName + " " + uid + + ": " + commonProc); + if (commonProc == null) { + mReadError = "no common proc: " + procName; + return; + } + if (hasProc != 0) { + // The process for this package is unique to the package; we + // need to load it. We don't need to do anything about it if + // it is not unique because if someone later looks for it + // they will find and use it from the global procs. + ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null; + if (proc != null) { + if (!proc.readFromParcel(in, false)) { + return; + } + } else { + proc = new ProcessState(commonProc, pkgName, uid, procName, 0); + if (!proc.readFromParcel(in, true)) { + return; + } + } + if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: " + + procName + " " + uid + " " + proc); + pkgState.mProcesses.put(procName, proc); + } else { + if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: " + + procName + " " + uid + " " + commonProc); + pkgState.mProcesses.put(procName, commonProc); + } + } + int NSRVS = in.readInt(); + if (NSRVS < 0) { + mReadError = "bad package service count: " + NSRVS; + return; + } + while (NSRVS > 0) { + NSRVS--; + String serviceName = in.readString(); + if (serviceName == null) { + mReadError = "bad package service name"; + return; + } + ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null; + if (serv == null) { + serv = new ServiceState(this, pkgName, null); + } + if (!serv.readFromParcel(in)) { + return; + } + if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " service: " + + serviceName + " " + uid + " " + serv); + pkgState.mServices.put(serviceName, serv); + } + } + } + + if (DEBUG) Slog.d(TAG, "Successfully read procstats!"); + } + + int addLongData(int index, int type, int num) { + int tableLen = mAddLongTable != null ? mAddLongTable.length : 0; + if (mAddLongTableSize >= tableLen) { + int newSize = ArrayUtils.idealIntArraySize(tableLen + 1); + int[] newTable = new int[newSize]; + if (tableLen > 0) { + System.arraycopy(mAddLongTable, 0, newTable, 0, tableLen); + } + mAddLongTable = newTable; + } + if (mAddLongTableSize > 0 && mAddLongTableSize - index != 0) { + System.arraycopy(mAddLongTable, index, mAddLongTable, index + 1, + mAddLongTableSize - index); + } + int off = allocLongData(num); + mAddLongTable[index] = type | off; + mAddLongTableSize++; + return off; + } + + int allocLongData(int num) { + int whichLongs = mLongs.size()-1; + long[] longs = mLongs.get(whichLongs); + if (mNextLong + num > longs.length) { + longs = new long[LONGS_SIZE]; + mLongs.add(longs); + whichLongs++; + mNextLong = 0; + } + int off = (whichLongs<<OFFSET_ARRAY_SHIFT) | (mNextLong<<OFFSET_INDEX_SHIFT); + mNextLong += num; + return off; + } + + boolean validateLongOffset(int off) { + int arr = (off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK; + if (arr >= mLongs.size()) { + return false; + } + int idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK; + if (idx >= LONGS_SIZE) { + return false; + } + if (DEBUG) Slog.d(TAG, "Validated long " + printLongOffset(off) + + ": " + getLong(off, 0)); + return true; + } + + static String printLongOffset(int off) { + StringBuilder sb = new StringBuilder(16); + sb.append("a"); sb.append((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + sb.append("i"); sb.append((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK); + sb.append("t"); sb.append((off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK); + return sb.toString(); + } + + void setLong(int off, int index, long value) { + long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)] = value; + } + + long getLong(int off, int index) { + long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + return longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)]; + } + + static int binarySearch(int[] array, int size, int value) { + int lo = 0; + int hi = size - 1; + + while (lo <= hi) { + int mid = (lo + hi) >>> 1; + int midVal = (array[mid] >> OFFSET_TYPE_SHIFT) & OFFSET_TYPE_MASK; + + if (midVal < value) { + lo = mid + 1; + } else if (midVal > value) { + hi = mid - 1; + } else { + return mid; // value found + } + } + return ~lo; // value not present + } + + public PackageState getPackageStateLocked(String packageName, int uid) { + PackageState as = mPackages.get(packageName, uid); + if (as != null) { + return as; + } + as = new PackageState(uid); + mPackages.put(packageName, uid, as); + return as; + } + + public ProcessState getProcessStateLocked(String packageName, int uid, String processName) { + final PackageState pkgState = getPackageStateLocked(packageName, uid); + ProcessState ps = pkgState.mProcesses.get(processName); + if (ps != null) { + return ps; + } + ProcessState commonProc = mProcesses.get(processName, uid); + if (commonProc == null) { + commonProc = new ProcessState(this, packageName, uid, processName); + mProcesses.put(processName, uid, commonProc); + } + if (!commonProc.mMultiPackage) { + if (packageName.equals(commonProc.mPackage)) { + // This common process is not in use by multiple packages, and + // is for the calling package, so we can just use it directly. + ps = commonProc; + } else { + // This common process has not been in use by multiple packages, + // but it was created for a different package than the caller. + // We need to convert it to a multi-package process. + commonProc.mMultiPackage = true; + // The original package it was created for now needs to point + // to its own copy. + long now = SystemClock.uptimeMillis(); + pkgState.mProcesses.put(commonProc.mName, commonProc.clone( + commonProc.mPackage, now)); + ps = new ProcessState(commonProc, packageName, uid, processName, now); + } + } else { + // The common process is for multiple packages, we need to create a + // separate object for the per-package data. + ps = new ProcessState(commonProc, packageName, uid, processName, + SystemClock.uptimeMillis()); + } + pkgState.mProcesses.put(processName, ps); + return ps; + } + + public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpAll) { + long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, + mStartTime, now); + ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); + boolean printedHeader = false; + for (int ip=0; ip<pkgMap.size(); ip++) { + String pkgName = pkgMap.keyAt(ip); + if (reqPackage != null && !reqPackage.equals(pkgName)) { + continue; + } + SparseArray<PackageState> uids = pkgMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + PackageState pkgState = uids.valueAt(iu); + final int NPROCS = pkgState.mProcesses.size(); + final int NSRVS = pkgState.mServices.size(); + if (NPROCS > 0 || NSRVS > 0) { + if (!printedHeader) { + pw.println("Per-Package Process Stats:"); + printedHeader = true; + } + pw.print(" * "); pw.print(pkgName); pw.print(" / "); + UserHandle.formatUid(pw, uid); pw.println(":"); + } + if (dumpAll) { + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + pw.print(" Process "); + pw.print(pkgState.mProcesses.keyAt(iproc)); + pw.print(" ("); + pw.print(proc.mDurationsTableSize); + pw.print(" entries)"); + pw.println(":"); + dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES, now); + dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES); + if (dumpAll) { + pw.print(" mNumStartedServices="); + pw.println(proc.mNumStartedServices); + } + } + } else { + ArrayList<ProcessState> procs = new ArrayList<ProcessState>(); + for (int iproc=0; iproc<NPROCS; iproc++) { + procs.add(pkgState.mProcesses.valueAt(iproc)); + } + dumpProcessSummaryLocked(pw, " ", procs, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + NON_CACHED_PROC_STATES, now, totalTime); + } + for (int isvc=0; isvc<NSRVS; isvc++) { + if (dumpAll) { + pw.print(" Service "); + } else { + pw.print(" * "); + } + pw.print(pkgState.mServices.keyAt(isvc)); + pw.println(":"); + ServiceState svc = pkgState.mServices.valueAt(isvc); + dumpServiceStats(pw, " ", " ", " ", "Started", svc, + svc.mStartedCount, ServiceState.SERVICE_STARTED, svc.mStartedState, + svc.mStartedStartTime, now, totalTime, dumpAll); + dumpServiceStats(pw, " ", " ", " ", "Bound", svc, + svc.mBoundCount, ServiceState.SERVICE_BOUND, svc.mBoundState, + svc.mBoundStartTime, now, totalTime, dumpAll); + dumpServiceStats(pw, " ", " ", " ", "Executing", svc, + svc.mExecCount, ServiceState.SERVICE_EXEC, svc.mExecState, + svc.mExecStartTime, now, totalTime, dumpAll); + } + } + } + + if (reqPackage == null) { + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + printedHeader = false; + for (int ip=0; ip<procMap.size(); ip++) { + String procName = procMap.keyAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + ProcessState proc = uids.valueAt(iu); + if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING + && proc.mPssTableSize == 0) { + continue; + } + if (!printedHeader) { + pw.println("Process Stats:"); + printedHeader = true; + } + pw.print(" * "); pw.print(procName); pw.print(" / "); + UserHandle.formatUid(pw, uid); + pw.print(" ("); pw.print(proc.mDurationsTableSize); + pw.print(" entries)"); pw.println(":"); + dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES, now); + dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES); + } + } + + pw.println(); + pw.println("Summary:"); + dumpSummaryLocked(pw, reqPackage, now); + } else { + pw.println(); + dumpTotalsLocked(pw, now); + } + + if (dumpAll) { + pw.println(); + pw.println("Internal state:"); + pw.print(" Num long arrays: "); pw.println(mLongs.size()); + pw.print(" Next long entry: "); pw.println(mNextLong); + pw.print(" mRunning="); pw.println(mRunning); + } + } + + static long dumpSingleServiceTime(PrintWriter pw, String prefix, ServiceState service, + int serviceType, int curState, long curStartTime, long now) { + long totalTime = 0; + int printedScreen = -1; + for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { + int printedMem = -1; + for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { + int state = imem+iscreen; + long time = service.getDuration(serviceType, curState, curStartTime, + state, now); + String running = ""; + if (curState == state) { + time += now - curStartTime; + if (pw != null) { + running = " (running)"; + } + } + if (time != 0) { + if (pw != null) { + pw.print(prefix); + printScreenLabel(pw, printedScreen != iscreen + ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); + printedMem = imem; + TimeUtils.formatDuration(time, pw); pw.println(running); + } + totalTime += time; + } + } + } + if (totalTime != 0 && pw != null) { + pw.print(prefix); + printScreenLabel(pw, STATE_NOTHING); + pw.print("TOTAL: "); + TimeUtils.formatDuration(totalTime, pw); + pw.println(); + } + return totalTime; + } + + void dumpServiceStats(PrintWriter pw, String prefix, String prefixInner, + String headerPrefix, String header, ServiceState service, + int count, int serviceType, int state, long startTime, long now, long totalTime, + boolean dumpAll) { + if (count != 0) { + if (dumpAll) { + pw.print(prefix); pw.print(header); + pw.print(" op count "); pw.print(count); pw.println(":"); + dumpSingleServiceTime(pw, prefixInner, service, serviceType, state, startTime, + now); + } else { + long myTime = dumpSingleServiceTime(null, null, service, serviceType, state, + startTime, now); + pw.print(prefix); pw.print(headerPrefix); pw.print(header); + pw.print(" count "); pw.print(count); + pw.print(" / time "); + printPercent(pw, (double)myTime/(double)totalTime); + pw.println(); + } + } + } + + public void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now) { + long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, + mStartTime, now); + dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, + NON_CACHED_PROC_STATES, now, totalTime, reqPackage); + pw.println(); + dumpTotalsLocked(pw, now); + } + + void dumpTotalsLocked(PrintWriter pw, long now) { + pw.println("Run time Stats:"); + dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now); + pw.println(); + pw.print(" Start time: "); + pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock)); + pw.println(); + pw.print(" Total elapsed time: "); + TimeUtils.formatDuration( + (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime) + - mTimePeriodStartRealtime, pw); + boolean partial = true; + if ((mFlags&FLAG_SHUTDOWN) != 0) { + pw.print(" (shutdown)"); + partial = false; + } + if ((mFlags&FLAG_SYSPROPS) != 0) { + pw.print(" (sysprops)"); + partial = false; + } + if ((mFlags&FLAG_COMPLETE) != 0) { + pw.print(" (complete)"); + partial = false; + } + if (partial) { + pw.print(" (partial)"); + } + pw.print(' '); + pw.print(mRuntime); + pw.print(' '); + pw.print(mWebView); + pw.println(); + } + + void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix, + int[] screenStates, int[] memStates, int[] procStates, long now, long totalTime, + String reqPackage) { + ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates, + procStates, now, reqPackage); + if (procs.size() > 0) { + if (header != null) { + pw.println(); + pw.println(header); + } + dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates, procStates, + now, totalTime); + } + } + + public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates, + int[] procStates, long now, String reqPackage) { + ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>(); + ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); + for (int ip=0; ip<pkgMap.size(); ip++) { + if (reqPackage != null && !reqPackage.equals(pkgMap.keyAt(ip))) { + continue; + } + SparseArray<PackageState> procs = pkgMap.valueAt(ip); + for (int iu=0; iu<procs.size(); iu++) { + PackageState state = procs.valueAt(iu); + for (int iproc=0; iproc<state.mProcesses.size(); iproc++) { + ProcessState proc = state.mProcesses.valueAt(iproc); + foundProcs.add(proc.mCommonProcess); + } + } + } + ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>(foundProcs.size()); + for (int i=0; i<foundProcs.size(); i++) { + ProcessState proc = foundProcs.valueAt(i); + if (computeProcessTimeLocked(proc, screenStates, memStates, + procStates, now) > 0) { + outProcs.add(proc); + } + } + Collections.sort(outProcs, new Comparator<ProcessState>() { + @Override + public int compare(ProcessState lhs, ProcessState rhs) { + if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) { + return -1; + } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) { + return 1; + } + return 0; + } + }); + return outProcs; + } + + String collapseString(String pkgName, String itemName) { + if (itemName.startsWith(pkgName)) { + final int ITEMLEN = itemName.length(); + final int PKGLEN = pkgName.length(); + if (ITEMLEN == PKGLEN) { + return ""; + } else if (ITEMLEN >= PKGLEN) { + if (itemName.charAt(PKGLEN) == '.') { + return itemName.substring(PKGLEN); + } + } + } + return itemName; + } + + public void dumpCheckinLocked(PrintWriter pw, String reqPackage) { + final long now = SystemClock.uptimeMillis(); + ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); + pw.println("vers,3"); + pw.print("period,"); pw.print(mTimePeriodStartClockStr); + pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(","); + pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime); + boolean partial = true; + if ((mFlags&FLAG_SHUTDOWN) != 0) { + pw.print(",shutdown"); + partial = false; + } + if ((mFlags&FLAG_SYSPROPS) != 0) { + pw.print(",sysprops"); + partial = false; + } + if ((mFlags&FLAG_COMPLETE) != 0) { + pw.print(",complete"); + partial = false; + } + if (partial) { + pw.print(",partial"); + } + pw.println(); + pw.print("config,"); pw.print(mRuntime); pw.print(','); pw.println(mWebView); + for (int ip=0; ip<pkgMap.size(); ip++) { + String pkgName = pkgMap.keyAt(ip); + if (reqPackage != null && !reqPackage.equals(pkgName)) { + continue; + } + SparseArray<PackageState> uids = pkgMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + PackageState pkgState = uids.valueAt(iu); + final int NPROCS = pkgState.mProcesses.size(); + final int NSRVS = pkgState.mServices.size(); + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + pw.print("pkgproc,"); + pw.print(pkgName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc))); + dumpAllProcessStateCheckin(pw, proc, now); + pw.println(); + if (proc.mPssTableSize > 0) { + pw.print("pkgpss,"); + pw.print(pkgName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc))); + dumpAllProcessPssCheckin(pw, proc); + pw.println(); + } + if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0) { + pw.print("pkgkills,"); + pw.print(pkgName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc))); + pw.print(","); + pw.print(proc.mNumExcessiveWake); + pw.print(","); + pw.print(proc.mNumExcessiveCpu); + pw.println(); + } + } + for (int isvc=0; isvc<NSRVS; isvc++) { + String serviceName = collapseString(pkgName, + pkgState.mServices.keyAt(isvc)); + ServiceState svc = pkgState.mServices.valueAt(isvc); + dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, serviceName, + svc, ServiceState.SERVICE_STARTED, svc.mStartedCount, + svc.mStartedState, svc.mStartedStartTime, now); + dumpServiceTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, serviceName, + svc, ServiceState.SERVICE_BOUND, svc.mBoundCount, + svc.mBoundState, svc.mBoundStartTime, now); + dumpServiceTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, serviceName, + svc, ServiceState.SERVICE_EXEC, svc.mExecCount, + svc.mExecState, svc.mExecStartTime, now); + } + } + } + + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + for (int ip=0; ip<procMap.size(); ip++) { + String procName = procMap.keyAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + ProcessState procState = uids.valueAt(iu); + if (procState.mDurationsTableSize > 0) { + pw.print("proc,"); + pw.print(procName); + pw.print(","); + pw.print(uid); + dumpAllProcessStateCheckin(pw, procState, now); + pw.println(); + } + if (procState.mPssTableSize > 0) { + pw.print("pss,"); + pw.print(procName); + pw.print(","); + pw.print(uid); + dumpAllProcessPssCheckin(pw, procState); + pw.println(); + } + if (procState.mNumExcessiveWake > 0 || procState.mNumExcessiveCpu > 0) { + pw.print("kills,"); + pw.print(procName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(procState.mNumExcessiveWake); + pw.print(","); + pw.print(procState.mNumExcessiveCpu); + pw.println(); + } + } + } + pw.print("total"); + dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor, + mStartTime, now); + pw.println(); + } + + public static final class ProcessState { + final ProcessStats mProcessStats; + final ProcessState mCommonProcess; + final String mPackage; + final int mUid; + final String mName; + + int[] mDurationsTable; + int mDurationsTableSize; + + //final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT]; + int mCurState = STATE_NOTHING; + long mStartTime; + + int mLastPssState = STATE_NOTHING; + long mLastPssTime; + int[] mPssTable; + int mPssTableSize; + + int mNumStartedServices; + + int mNumExcessiveWake; + int mNumExcessiveCpu; + + boolean mMultiPackage; + + long mTmpTotalTime; + + /** + * Create a new top-level process state, for the initial case where there is only + * a single package running in a process. The initial state is not running. + */ + public ProcessState(ProcessStats processStats, String pkg, int uid, String name) { + mProcessStats = processStats; + mCommonProcess = this; + mPackage = pkg; + mUid = uid; + mName = name; + } + + /** + * Create a new per-package process state for an existing top-level process + * state. The current running state of the top-level process is also copied, + * marked as started running at 'now'. + */ + public ProcessState(ProcessState commonProcess, String pkg, int uid, String name, + long now) { + mProcessStats = commonProcess.mProcessStats; + mCommonProcess = commonProcess; + mPackage = pkg; + mUid = uid; + mName = name; + mCurState = commonProcess.mCurState; + mStartTime = now; + } + + ProcessState clone(String pkg, long now) { + ProcessState pnew = new ProcessState(this, pkg, mUid, mName, now); + if (mDurationsTable != null) { + mProcessStats.mAddLongTable = new int[mDurationsTable.length]; + mProcessStats.mAddLongTableSize = 0; + for (int i=0; i<mDurationsTableSize; i++) { + int origEnt = mDurationsTable[i]; + int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + int newOff = mProcessStats.addLongData(i, type, 1); + mProcessStats.mAddLongTable[i] = newOff | type; + mProcessStats.setLong(newOff, 0, mProcessStats.getLong(origEnt, 0)); + } + pnew.mDurationsTable = mProcessStats.mAddLongTable; + pnew.mDurationsTableSize = mProcessStats.mAddLongTableSize; + } + if (mPssTable != null) { + mProcessStats.mAddLongTable = new int[mPssTable.length]; + mProcessStats.mAddLongTableSize = 0; + for (int i=0; i<mPssTableSize; i++) { + int origEnt = mPssTable[i]; + int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + int newOff = mProcessStats.addLongData(i, type, PSS_COUNT); + mProcessStats.mAddLongTable[i] = newOff | type; + for (int j=0; j<PSS_COUNT; j++) { + mProcessStats.setLong(newOff, j, mProcessStats.getLong(origEnt, j)); + } + } + pnew.mPssTable = mProcessStats.mAddLongTable; + pnew.mPssTableSize = mProcessStats.mAddLongTableSize; + } + pnew.mNumExcessiveWake = mNumExcessiveWake; + pnew.mNumExcessiveCpu = mNumExcessiveCpu; + pnew.mNumStartedServices = mNumStartedServices; + return pnew; + } + + void resetSafely(long now) { + mDurationsTable = null; + mDurationsTableSize = 0; + mStartTime = now; + mLastPssState = STATE_NOTHING; + mLastPssTime = 0; + mPssTable = null; + mPssTableSize = 0; + mNumExcessiveWake = 0; + mNumExcessiveCpu = 0; + } + + void writeToParcel(Parcel out, long now) { + commitStateTime(now); + out.writeInt(mMultiPackage ? 1 : 0); + out.writeInt(mDurationsTableSize); + for (int i=0; i<mDurationsTableSize; i++) { + if (DEBUG) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": " + + printLongOffset(mDurationsTable[i])); + out.writeInt(mDurationsTable[i]); + } + out.writeInt(mPssTableSize); + for (int i=0; i<mPssTableSize; i++) { + if (DEBUG) Slog.i(TAG, "Writing in " + mName + " pss #" + i + ": " + + printLongOffset(mPssTable[i])); + out.writeInt(mPssTable[i]); + } + out.writeInt(mNumExcessiveWake); + out.writeInt(mNumExcessiveCpu); + } + + boolean readFromParcel(Parcel in, boolean fully) { + boolean multiPackage = in.readInt() != 0; + if (fully) { + mMultiPackage = multiPackage; + } + if (DEBUG) Slog.d(TAG, "Reading durations table..."); + mDurationsTable = mProcessStats.readTableFromParcel(in, mName, "durations"); + if (mDurationsTable == BAD_TABLE) { + return false; + } + mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0; + if (DEBUG) Slog.d(TAG, "Reading pss table..."); + mPssTable = mProcessStats.readTableFromParcel(in, mName, "pss"); + if (mPssTable == BAD_TABLE) { + return false; + } + mPssTableSize = mPssTable != null ? mPssTable.length : 0; + mNumExcessiveWake = in.readInt(); + mNumExcessiveCpu = in.readInt(); + return true; + } + + /** + * Update the current state of the given list of processes. + * + * @param state Current ActivityManager.PROCESS_STATE_* + * @param memFactor Current mem factor constant. + * @param now Current time. + * @param pkgList Processes to update. + */ + public void setState(int state, int memFactor, long now, + ArrayMap<String, ProcessState> pkgList) { + if (state < 0) { + state = mNumStartedServices > 0 + ? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING; + } else { + state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT); + } + + // First update the common process. + mCommonProcess.setState(state, now); + + // If the common process is not multi-package, there is nothing else to do. + if (!mCommonProcess.mMultiPackage) { + return; + } + + if (pkgList != null) { + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).setState(state, now); + } + } + } + + void setState(int state, long now) { + if (mCurState != state) { + //Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state); + commitStateTime(now); + mCurState = state; + } + } + + void commitStateTime(long now) { + if (mCurState != STATE_NOTHING) { + long dur = now - mStartTime; + if (dur > 0) { + int idx = binarySearch(mDurationsTable, mDurationsTableSize, mCurState); + int off; + if (idx >= 0) { + off = mDurationsTable[idx]; + } else { + mProcessStats.mAddLongTable = mDurationsTable; + mProcessStats.mAddLongTableSize = mDurationsTableSize; + off = mProcessStats.addLongData(~idx, mCurState, 1); + mDurationsTable = mProcessStats.mAddLongTable; + mDurationsTableSize = mProcessStats.mAddLongTableSize; + } + long[] longs = mProcessStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur; + } + } + mStartTime = now; + } + + void incStartedServices(int memFactor, long now) { + if (mCommonProcess != this) { + mCommonProcess.incStartedServices(memFactor, now); + } + mNumStartedServices++; + if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) { + setState(STATE_NOTHING, memFactor, now, null); + } + } + + void decStartedServices(int memFactor, long now) { + if (mCommonProcess != this) { + mCommonProcess.decStartedServices(memFactor, now); + } + mNumStartedServices--; + if (mNumStartedServices == 0 && mCurState == STATE_SERVICE_RESTARTING) { + setState(STATE_NOTHING, memFactor, now, null); + } else if (mNumStartedServices < 0) { + throw new IllegalStateException("Proc started services underrun: pkg=" + + mPackage + " uid=" + mUid + " name=" + mName); + } + } + + public void addPss(long pss, long uss, boolean always) { + if (!always) { + if (mLastPssState == mCurState && SystemClock.uptimeMillis() + < (mLastPssTime+(30*1000))) { + return; + } + } + mLastPssState = mCurState; + mLastPssTime = SystemClock.uptimeMillis(); + if (mCurState != STATE_NOTHING) { + int idx = binarySearch(mPssTable, mPssTableSize, mCurState); + int off; + if (idx >= 0) { + off = mPssTable[idx]; + } else { + mProcessStats.mAddLongTable = mPssTable; + mProcessStats.mAddLongTableSize = mPssTableSize; + off = mProcessStats.addLongData(~idx, mCurState, PSS_COUNT); + mPssTable = mProcessStats.mAddLongTable; + mPssTableSize = mProcessStats.mAddLongTableSize; + } + long[] longs = mProcessStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK; + long count = longs[idx+PSS_SAMPLE_COUNT]; + if (count == 0) { + longs[idx+PSS_SAMPLE_COUNT] = 1; + longs[idx+PSS_MINIMUM] = pss; + longs[idx+PSS_AVERAGE] = pss; + longs[idx+PSS_MAXIMUM] = pss; + longs[idx+PSS_USS_MINIMUM] = uss; + longs[idx+PSS_USS_AVERAGE] = uss; + longs[idx+PSS_USS_MAXIMUM] = uss; + } else { + longs[idx+PSS_SAMPLE_COUNT] = count+1; + if (longs[idx+PSS_MINIMUM] > pss) { + longs[idx+PSS_MINIMUM] = pss; + } + longs[idx+PSS_AVERAGE] = (long)( + ((longs[idx+PSS_AVERAGE]*(double)count)+pss) / (count+1) ); + if (longs[idx+PSS_MAXIMUM] < pss) { + longs[idx+PSS_MAXIMUM] = pss; + } + if (longs[idx+PSS_USS_MINIMUM] > uss) { + longs[idx+PSS_USS_MINIMUM] = uss; + } + longs[idx+PSS_USS_AVERAGE] = (long)( + ((longs[idx+PSS_USS_AVERAGE]*(double)count)+uss) / (count+1) ); + if (longs[idx+PSS_USS_MAXIMUM] < uss) { + longs[idx+PSS_USS_MAXIMUM] = uss; + } + } + } + } + + public void reportExcessiveWake(ArrayMap<String, ProcessState> pkgList) { + mCommonProcess.mNumExcessiveWake++; + if (!mCommonProcess.mMultiPackage) { + return; + } + + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).mNumExcessiveWake++; + } + } + + public void reportExcessiveCpu(ArrayMap<String, ProcessState> pkgList) { + mCommonProcess.mNumExcessiveCpu++; + if (!mCommonProcess.mMultiPackage) { + return; + } + + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).mNumExcessiveCpu++; + } + } + + ProcessState pullFixedProc(String pkgName) { + if (mMultiPackage) { + // The array map is still pointing to a common process state + // that is now shared across packages. Update it to point to + // the new per-package state. + ProcessState proc = mProcessStats.mPackages.get(pkgName, + mUid).mProcesses.get(mName); + if (proc == null) { + throw new IllegalStateException("Didn't create per-package process"); + } + return proc; + } + return this; + } + + private ProcessState pullFixedProc(ArrayMap<String, ProcessState> pkgList, + int index) { + ProcessState proc = pkgList.valueAt(index); + if (proc.mMultiPackage) { + // The array map is still pointing to a common process state + // that is now shared across packages. Update it to point to + // the new per-package state. + proc = mProcessStats.mPackages.get(pkgList.keyAt(index), + proc.mUid).mProcesses.get(proc.mName); + if (proc == null) { + throw new IllegalStateException("Didn't create per-package process"); + } + pkgList.setValueAt(index, proc); + } + return proc; + } + + long getDuration(int state, long now) { + int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); + long time = idx >= 0 ? mProcessStats.getLong(mDurationsTable[idx], 0) : 0; + if (mCurState == state) { + time += now - mStartTime; + } + return time; + } + + long getPssSampleCount(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_SAMPLE_COUNT) : 0; + } + + long getPssMinimum(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_MINIMUM) : 0; + } + + long getPssAverage(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_AVERAGE) : 0; + } + + long getPssMaximum(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_MAXIMUM) : 0; + } + + long getPssUssMinimum(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0; + } + + long getPssUssAverage(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0; + } + + long getPssUssMaximum(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mProcessStats.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0; + } + } + + public static final class ServiceState { + final ProcessStats mProcessStats; + final String mPackage; + ProcessState mProc; + + int mActive = 1; + + static final int SERVICE_STARTED = 0; + static final int SERVICE_BOUND = 1; + static final int SERVICE_EXEC = 2; + static final int SERVICE_COUNT = 3; + + int[] mDurationsTable; + int mDurationsTableSize; + + int mStartedCount; + public int mStartedState = STATE_NOTHING; + long mStartedStartTime; + + int mBoundCount; + public int mBoundState = STATE_NOTHING; + long mBoundStartTime; + + int mExecCount; + public int mExecState = STATE_NOTHING; + long mExecStartTime; + + public ServiceState(ProcessStats processStats, String pkg, ProcessState proc) { + mProcessStats = processStats; + mPackage = pkg; + mProc = proc; + } + + public void makeActive() { + mActive++; + } + + public void makeInactive() { + /* + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.i(TAG, "Making " + this + " inactive", here); + */ + mActive--; + } + + public boolean isActive() { + return mActive > 0; + } + + void resetSafely(long now) { + mDurationsTable = null; + mDurationsTableSize = 0; + mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0; + mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0; + mExecCount = mExecState != STATE_NOTHING ? 1 : 0; + mStartedStartTime = mBoundStartTime = mExecStartTime = now; + } + + void writeToParcel(Parcel out, long now) { + if (mStartedState != STATE_NOTHING) { + addStateTime(SERVICE_STARTED, mStartedState, now - mStartedStartTime); + mStartedStartTime = now; + } + if (mBoundState != STATE_NOTHING) { + addStateTime(SERVICE_BOUND, mBoundState, now - mBoundStartTime); + mBoundStartTime = now; + } + if (mExecState != STATE_NOTHING) { + addStateTime(SERVICE_EXEC, mExecState, now - mExecStartTime); + mExecStartTime = now; + } + out.writeInt(mDurationsTableSize); + for (int i=0; i<mDurationsTableSize; i++) { + if (DEBUG) Slog.i(TAG, "Writing service in " + mPackage + " dur #" + i + ": " + + printLongOffset(mDurationsTable[i])); + out.writeInt(mDurationsTable[i]); + } + out.writeInt(mStartedCount); + out.writeInt(mBoundCount); + out.writeInt(mExecCount); + } + + boolean readFromParcel(Parcel in) { + if (DEBUG) Slog.d(TAG, "Reading durations table..."); + mDurationsTable = mProcessStats.readTableFromParcel(in, mPackage, "service"); + if (mDurationsTable == BAD_TABLE) { + return false; + } + mStartedCount = in.readInt(); + mBoundCount = in.readInt(); + mExecCount = in.readInt(); + return true; + } + + void addStateTime(int opType, int memFactor, long time) { + if (time > 0) { + int state = opType + (memFactor*SERVICE_COUNT); + int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); + int off; + if (idx >= 0) { + off = mDurationsTable[idx]; + } else { + mProcessStats.mAddLongTable = mDurationsTable; + mProcessStats.mAddLongTableSize = mDurationsTableSize; + off = mProcessStats.addLongData(~idx, state, 1); + mDurationsTable = mProcessStats.mAddLongTable; + mDurationsTableSize = mProcessStats.mAddLongTableSize; + } + long[] longs = mProcessStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += time; + } + } + + public void setStarted(boolean started, int memFactor, long now) { + if (mActive <= 0) { + throw new IllegalStateException("Service " + this + " has mActive=" + mActive); + } + int state = started ? memFactor : STATE_NOTHING; + if (mStartedState != state) { + if (mStartedState != STATE_NOTHING) { + addStateTime(SERVICE_STARTED, mStartedState, now - mStartedStartTime); + } else if (started) { + mStartedCount++; + } + mStartedState = state; + mStartedStartTime = now; + if (mProc != null) { + mProc = mProc.pullFixedProc(mPackage); + if (started) { + mProc.incStartedServices(memFactor, now); + } else { + mProc.decStartedServices(memFactor, now); + } + } + } + } + + public void setBound(boolean bound, int memFactor, long now) { + if (mActive <= 0) { + throw new IllegalStateException("Service " + this + " has mActive=" + mActive); + } + int state = bound ? memFactor : STATE_NOTHING; + if (mBoundState != state) { + if (mBoundState != STATE_NOTHING) { + addStateTime(SERVICE_BOUND, mBoundState, now - mBoundStartTime); + } else if (bound) { + mBoundCount++; + } + mBoundState = state; + mBoundStartTime = now; + } + } + + public void setExecuting(boolean executing, int memFactor, long now) { + if (mActive <= 0) { + throw new IllegalStateException("Service " + this + " has mActive=" + mActive); + } + int state = executing ? memFactor : STATE_NOTHING; + if (mExecState != state) { + if (mExecState != STATE_NOTHING) { + addStateTime(SERVICE_EXEC, mExecState, now - mExecStartTime); + } else if (executing) { + mExecCount++; + } + mExecState = state; + mExecStartTime = now; + } + } + + long getStartDuration(int opType, int memFactor, long now) { + switch (opType) { + case SERVICE_STARTED: + return getDuration(opType, mStartedState, mStartedStartTime, memFactor, now); + case SERVICE_BOUND: + return getDuration(opType, mBoundState, mBoundStartTime, memFactor, now); + case SERVICE_EXEC: + return getDuration(opType, mExecState, mExecStartTime, memFactor, now); + default: + throw new IllegalArgumentException("Bad opType: " + opType); + } + } + + + private long getDuration(int opType, int curState, long startTime, int memFactor, + long now) { + int state = opType + (memFactor*SERVICE_COUNT); + int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); + long time = idx >= 0 ? mProcessStats.getLong(mDurationsTable[idx], 0) : 0; + if (curState == memFactor) { + time += now - startTime; + } + return time; + } + } + + public static final class PackageState { + public final ArrayMap<String, ProcessState> mProcesses + = new ArrayMap<String, ProcessState>(); + public final ArrayMap<String, ServiceState> mServices + = new ArrayMap<String, ServiceState>(); + final int mUid; + + public PackageState(int uid) { + mUid = uid; + } + } + + static final class ProcessDataCollection { + final int[] screenStates; + final int[] memStates; + final int[] procStates; + + long totalTime; + long numPss; + long minPss; + long avgPss; + long maxPss; + long minUss; + long avgUss; + long maxUss; + + ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) { + screenStates = _screenStates; + memStates = _memStates; + procStates = _procStates; + } + + void print(PrintWriter pw, long overallTime, boolean full) { + printPercent(pw, (double) totalTime / (double) overallTime); + if (numPss > 0) { + pw.print(" ("); + printSizeValue(pw, minPss * 1024); + pw.print("-"); + printSizeValue(pw, avgPss * 1024); + pw.print("-"); + printSizeValue(pw, maxPss * 1024); + pw.print("/"); + printSizeValue(pw, minUss * 1024); + pw.print("-"); + printSizeValue(pw, avgUss * 1024); + pw.print("-"); + printSizeValue(pw, maxUss * 1024); + if (full) { + pw.print(" over "); + pw.print(numPss); + } + pw.print(")"); + } + } + } +} diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index f3aabe2..d61a48d 100644 --- a/services/java/com/android/server/am/ActiveServices.java +++ b/services/java/com/android/server/am/ActiveServices.java @@ -26,6 +26,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import com.android.internal.app.ProcessStats; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.TransferPipe; import com.android.server.am.ActivityManagerService.ItemMatcher; @@ -251,9 +252,9 @@ public final class ActiveServices { } r.lastActivity = SystemClock.uptimeMillis(); r.startRequested = true; - ProcessTracker.ServiceState stracker = r.getTracker(); + ProcessStats.ServiceState stracker = r.getTracker(); if (stracker != null) { - stracker.setStarted(true, mAm.mProcessTracker.getMemFactorLocked(), r.lastActivity); + stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity); } r.callStart = false; r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), @@ -274,7 +275,7 @@ public final class ActiveServices { } service.startRequested = false; if (service.tracker != null) { - service.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(), + service.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(), SystemClock.uptimeMillis()); } service.callStart = false; @@ -374,7 +375,7 @@ public final class ActiveServices { } r.startRequested = false; if (r.tracker != null) { - r.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(), + r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(), SystemClock.uptimeMillis()); } r.callStart = false; @@ -516,9 +517,9 @@ public final class ActiveServices { s.lastActivity = SystemClock.uptimeMillis(); if (!s.hasAutoCreateConnections()) { // This is the first binding, let the tracker know. - ProcessTracker.ServiceState stracker = s.getTracker(); + ProcessStats.ServiceState stracker = s.getTracker(); if (stracker != null) { - stracker.setBound(true, mAm.mProcessTracker.getMemFactorLocked(), + stracker.setBound(true, mAm.mProcessStats.getMemFactorLocked(), s.lastActivity); } } @@ -844,9 +845,9 @@ public final class ActiveServices { long now = SystemClock.uptimeMillis(); if (r.executeNesting == 0) { r.executeFg = fg; - ProcessTracker.ServiceState stracker = r.getTracker(); + ProcessStats.ServiceState stracker = r.getTracker(); if (stracker != null) { - stracker.setExecuting(true, mAm.mProcessTracker.getMemFactorLocked(), now); + stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now); } if (r.app != null) { if (r.app.executingServices.size() == 0) { @@ -1079,7 +1080,7 @@ public final class ActiveServices { Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app); if (app != null && app.thread != null) { try { - app.addPackage(r.appInfo.packageName, mAm.mProcessTracker); + app.addPackage(r.appInfo.packageName, mAm.mProcessStats); realStartServiceLocked(r, app, execInFg); return null; } catch (RemoteException e) { @@ -1364,7 +1365,7 @@ public final class ActiveServices { ((ServiceRestarter)r.restarter).setService(null); } - int memFactor = mAm.mProcessTracker.getMemFactorLocked(); + int memFactor = mAm.mProcessStats.getMemFactorLocked(); long now = SystemClock.uptimeMillis(); if (r.tracker != null) { r.tracker.setStarted(false, memFactor, now); @@ -1435,7 +1436,7 @@ public final class ActiveServices { boolean hasAutoCreate = s.hasAutoCreateConnections(); if (!hasAutoCreate) { if (s.tracker != null) { - s.tracker.setBound(false, mAm.mProcessTracker.getMemFactorLocked(), + s.tracker.setBound(false, mAm.mProcessStats.getMemFactorLocked(), SystemClock.uptimeMillis()); } } @@ -1541,7 +1542,7 @@ public final class ActiveServices { } r.executeFg = false; if (r.tracker != null) { - r.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(), + r.tracker.setExecuting(false, mAm.mProcessStats.getMemFactorLocked(), SystemClock.uptimeMillis()); if (inStopping) { r.tracker.makeInactive(); @@ -1566,7 +1567,7 @@ public final class ActiveServices { mPendingServices.remove(i); i--; - proc.addPackage(sr.appInfo.packageName, mAm.mProcessTracker); + proc.addPackage(sr.appInfo.packageName, mAm.mProcessStats); realStartServiceLocked(sr, proc, sr.createdFromFg); didSomething = true; } @@ -1737,7 +1738,7 @@ public final class ActiveServices { sr.isolatedProc = null; sr.executeNesting = 0; if (sr.tracker != null) { - sr.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(), + sr.tracker.setExecuting(false, mAm.mProcessStats.getMemFactorLocked(), SystemClock.uptimeMillis()); } if (mStoppingServices.remove(sr)) { @@ -1772,7 +1773,7 @@ public final class ActiveServices { if (sr.pendingStarts.size() == 0) { sr.startRequested = false; if (sr.tracker != null) { - sr.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(), + sr.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(), SystemClock.uptimeMillis()); } if (!sr.hasAutoCreateConnections()) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index cb1747f..1d26c2c 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -30,10 +30,11 @@ import android.util.ArrayMap; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsService; +import com.android.internal.app.ProcessStats; import com.android.internal.app.ResolverActivity; import com.android.internal.os.BackgroundThread; import com.android.internal.os.BatteryStatsImpl; -import com.android.internal.os.ProcessStats; +import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.TransferPipe; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; @@ -419,7 +420,7 @@ public final class ActivityManagerService extends ActivityManagerNative * Tracking long-term execution of processes to look for abuse and other * bad app behavior. */ - final ProcessTracker mProcessTracker; + final ProcessStatsService mProcessStats; /** * The currently running isolated processes. @@ -891,19 +892,19 @@ public final class ActivityManagerService extends ActivityManagerNative = new ArrayList<ProcessChangeItem>(); /** - * Runtime statistics collection thread. This object's lock is used to + * Runtime CPU use collection thread. This object's lock is used to * protect all related state. */ - final Thread mProcessStatsThread; + final Thread mProcessCpuThread; /** * Used to collect process stats when showing not responding dialog. - * Protected by mProcessStatsThread. + * Protected by mProcessCpuThread. */ - final ProcessStats mProcessStats = new ProcessStats( + final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker( MONITOR_THREAD_CPU_USAGE); final AtomicLong mLastCpuTime = new AtomicLong(0); - final AtomicBoolean mProcessStatsMutexFree = new AtomicBoolean(true); + final AtomicBoolean mProcessCpuMutexFree = new AtomicBoolean(true); long mLastWriteTime = 0; @@ -1745,9 +1746,9 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - synchronized (mActivityManagerService.mProcessStatsThread) { - pw.print(mActivityManagerService.mProcessStats.printCurrentLoad()); - pw.print(mActivityManagerService.mProcessStats.printCurrentState( + synchronized (mActivityManagerService.mProcessCpuThread) { + pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentLoad()); + pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentState( SystemClock.uptimeMillis())); } } @@ -1769,7 +1770,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - mActivityManagerService.mProcessTracker.dump(fd, pw, args); + mActivityManagerService.mProcessStats.dump(fd, pw, args); } } @@ -1795,7 +1796,7 @@ public final class ActivityManagerService extends ActivityManagerNative : mBatteryStatsService.getActiveStatistics().getIsOnBattery(); mBatteryStatsService.getActiveStatistics().setCallback(this); - mProcessTracker = new ProcessTracker(this, new File(systemDir, "procstats")); + mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats")); mUsageStatsService = new UsageStatsService(new File(systemDir, "usagestats").toString()); mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml")); @@ -1816,14 +1817,14 @@ public final class ActivityManagerService extends ActivityManagerNative mConfiguration.setLocale(Locale.getDefault()); mConfigurationSeq = mConfiguration.seq = 1; - mProcessStats.init(); + mProcessCpuTracker.init(); mCompatModePackages = new CompatModePackages(this, systemDir); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); - mProcessStatsThread = new Thread("ProcessStats") { + mProcessCpuThread = new Thread("CpuTracker") { @Override public void run() { while (true) { @@ -1839,7 +1840,7 @@ public final class ActivityManagerService extends ActivityManagerNative nextCpuDelay = nextWriteDelay; } if (nextCpuDelay > 0) { - mProcessStatsMutexFree.set(true); + mProcessCpuMutexFree.set(true); this.wait(nextCpuDelay); } } @@ -1852,7 +1853,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } }; - mProcessStatsThread.start(); + mProcessCpuThread.start(); } @Override @@ -1902,16 +1903,16 @@ public final class ActivityManagerService extends ActivityManagerNative if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) { return; } - if (mProcessStatsMutexFree.compareAndSet(true, false)) { - synchronized (mProcessStatsThread) { - mProcessStatsThread.notify(); + if (mProcessCpuMutexFree.compareAndSet(true, false)) { + synchronized (mProcessCpuThread) { + mProcessCpuThread.notify(); } } } void updateCpuStatsNow() { - synchronized (mProcessStatsThread) { - mProcessStatsMutexFree.set(false); + synchronized (mProcessCpuThread) { + mProcessCpuMutexFree.set(false); final long now = SystemClock.uptimeMillis(); boolean haveNewCpuStats = false; @@ -1919,19 +1920,19 @@ public final class ActivityManagerService extends ActivityManagerNative mLastCpuTime.get() < (now-MONITOR_CPU_MIN_TIME)) { mLastCpuTime.set(now); haveNewCpuStats = true; - mProcessStats.update(); - //Slog.i(TAG, mProcessStats.printCurrentState()); + mProcessCpuTracker.update(); + //Slog.i(TAG, mProcessCpu.printCurrentState()); //Slog.i(TAG, "Total CPU usage: " - // + mProcessStats.getTotalCpuPercent() + "%"); + // + mProcessCpu.getTotalCpuPercent() + "%"); // Slog the cpu usage if the property is set. if ("true".equals(SystemProperties.get("events.cpu"))) { - int user = mProcessStats.getLastUserTime(); - int system = mProcessStats.getLastSystemTime(); - int iowait = mProcessStats.getLastIoWaitTime(); - int irq = mProcessStats.getLastIrqTime(); - int softIrq = mProcessStats.getLastSoftIrqTime(); - int idle = mProcessStats.getLastIdleTime(); + int user = mProcessCpuTracker.getLastUserTime(); + int system = mProcessCpuTracker.getLastSystemTime(); + int iowait = mProcessCpuTracker.getLastIoWaitTime(); + int irq = mProcessCpuTracker.getLastIrqTime(); + int softIrq = mProcessCpuTracker.getLastSoftIrqTime(); + int idle = mProcessCpuTracker.getLastIdleTime(); int total = user + system + iowait + irq + softIrq + idle; if (total == 0) total = 1; @@ -1946,7 +1947,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - long[] cpuSpeedTimes = mProcessStats.getLastCpuSpeedTimes(); + long[] cpuSpeedTimes = mProcessCpuTracker.getLastCpuSpeedTimes(); final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics(); synchronized(bstats) { synchronized(mPidsSelfLocked) { @@ -1955,9 +1956,9 @@ public final class ActivityManagerService extends ActivityManagerNative int perc = bstats.startAddingCpuLocked(); int totalUTime = 0; int totalSTime = 0; - final int N = mProcessStats.countStats(); + final int N = mProcessCpuTracker.countStats(); for (int i=0; i<N; i++) { - ProcessStats.Stats st = mProcessStats.getStats(i); + ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); if (!st.working) { continue; } @@ -2233,7 +2234,7 @@ public final class ActivityManagerService extends ActivityManagerNative // come up (we have a pid but not yet its thread), so keep it. if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app); // If this is a new package in the process, add the package to the list - app.addPackage(info.packageName, mProcessTracker); + app.addPackage(info.packageName, mProcessStats); return app; } @@ -2288,7 +2289,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } else { // If this is a new package in the process, add the package to the list - app.addPackage(info.packageName, mProcessTracker); + app.addPackage(info.packageName, mProcessStats); } // If the system is not ready yet, then hold off on starting this @@ -3368,7 +3369,7 @@ public final class ActivityManagerService extends ActivityManagerNative * @return file containing stack traces, or null if no dump file is configured */ public static File dumpStackTraces(boolean clearTraces, ArrayList<Integer> firstPids, - ProcessStats processStats, SparseArray<Boolean> lastPids, String[] nativeProcs) { + ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids, String[] nativeProcs) { String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null); if (tracesPath == null || tracesPath.length() == 0) { return null; @@ -3393,12 +3394,12 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } - dumpStackTraces(tracesPath, firstPids, processStats, lastPids, nativeProcs); + dumpStackTraces(tracesPath, firstPids, processCpuTracker, lastPids, nativeProcs); return tracesFile; } private static void dumpStackTraces(String tracesPath, ArrayList<Integer> firstPids, - ProcessStats processStats, SparseArray<Boolean> lastPids, String[] nativeProcs) { + ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids, String[] nativeProcs) { // Use a FileObserver to detect when traces finish writing. // The order of traces is considered important to maintain for legibility. FileObserver observer = new FileObserver(tracesPath, FileObserver.CLOSE_WRITE) { @@ -3425,23 +3426,23 @@ public final class ActivityManagerService extends ActivityManagerNative } // Next measure CPU usage. - if (processStats != null) { - processStats.init(); + if (processCpuTracker != null) { + processCpuTracker.init(); System.gc(); - processStats.update(); + processCpuTracker.update(); try { - synchronized (processStats) { - processStats.wait(500); // measure over 1/2 second. + synchronized (processCpuTracker) { + processCpuTracker.wait(500); // measure over 1/2 second. } } catch (InterruptedException e) { } - processStats.update(); + processCpuTracker.update(); // We'll take the stack crawls of just the top apps using CPU. - final int N = processStats.countWorkingStats(); + final int N = processCpuTracker.countWorkingStats(); int numProcs = 0; for (int i=0; i<N && numProcs<5; i++) { - ProcessStats.Stats stats = processStats.getWorkingStats(i); + ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i); if (lastPids.indexOfKey(stats.pid) >= 0) { numProcs++; try { @@ -3629,21 +3630,21 @@ public final class ActivityManagerService extends ActivityManagerNative info.append("Parent: ").append(parent.shortComponentName).append("\n"); } - final ProcessStats processStats = new ProcessStats(true); + final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true); - File tracesFile = dumpStackTraces(true, firstPids, processStats, lastPids, null); + File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker, lastPids, null); String cpuInfo = null; if (MONITOR_CPU_USAGE) { updateCpuStatsNow(); - synchronized (mProcessStatsThread) { - cpuInfo = mProcessStats.printCurrentState(anrTime); + synchronized (mProcessCpuThread) { + cpuInfo = mProcessCpuTracker.printCurrentState(anrTime); } - info.append(processStats.printCurrentLoad()); + info.append(processCpuTracker.printCurrentLoad()); info.append(cpuInfo); } - info.append(processStats.printCurrentState(anrTime)); + info.append(processCpuTracker.printCurrentState(anrTime)); Slog.e(TAG, info.toString()); if (tracesFile == null) { @@ -4474,7 +4475,7 @@ public final class ActivityManagerService extends ActivityManagerNative thread.asBinder().linkToDeath(adr, 0); app.deathRecipient = adr; } catch (RemoteException e) { - app.resetPackageList(mProcessTracker); + app.resetPackageList(mProcessStats); startProcessLocked(app, "link fail", processName); return false; } @@ -4566,7 +4567,7 @@ public final class ActivityManagerService extends ActivityManagerNative // an infinite loop of restarting processes... Slog.w(TAG, "Exception thrown during bind!", e); - app.resetPackageList(mProcessTracker); + app.resetPackageList(mProcessStats); app.unlinkDeathRecipient(); startProcessLocked(app, "bind fail", processName); return false; @@ -6782,7 +6783,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_MU) Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid); app.pubProviders.put(cpi.name, cpr); - app.addPackage(cpi.applicationInfo.packageName, mProcessTracker); + app.addPackage(cpi.applicationInfo.packageName, mProcessStats); ensurePackageDexOpt(cpi.applicationInfo.packageName); } } @@ -7520,7 +7521,7 @@ public final class ActivityManagerService extends ActivityManagerNative ps = stats.getProcessStatsLocked(info.uid, proc); } return new ProcessRecord(ps, thread, info, proc, uid, - mProcessTracker.getProcessStateLocked(info.packageName, info.uid, proc)); + mProcessStats.getProcessStateLocked(info.packageName, info.uid, proc)); } final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) { @@ -7660,7 +7661,7 @@ public final class ActivityManagerService extends ActivityManagerNative mUsageStatsService.shutdown(); mBatteryStatsService.shutdown(); synchronized (this) { - mProcessTracker.shutdownLocked(); + mProcessStats.shutdownLocked(); } return timedout; @@ -11665,7 +11666,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.crashing = false; app.notResponding = false; - app.resetPackageList(mProcessTracker); + app.resetPackageList(mProcessStats); app.unlinkDeathRecipient(); app.thread = null; app.forcingToForeground = null; @@ -14540,7 +14541,7 @@ public final class ActivityManagerService extends ActivityManagerNative + " to " + app.curProcState); app.setProcState = app.curProcState; if (!doingAll) { - setProcessTrackerState(app, mProcessTracker.getMemFactorLocked(), now); + setProcessTrackerState(app, mProcessStats.getMemFactorLocked(), now); } else { app.procStateChanged = true; } @@ -14818,17 +14819,17 @@ public final class ActivityManagerService extends ActivityManagerNative int memFactor; if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; - memFactor = ProcessTracker.ADJ_MEM_FACTOR_CRITICAL; + memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL; } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; - memFactor = ProcessTracker.ADJ_MEM_FACTOR_LOW; + memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW; } else { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; - memFactor = ProcessTracker.ADJ_MEM_FACTOR_MODERATE; + memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE; } int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; - allChanged = mProcessTracker.setMemFactorLocked(memFactor, !mSleeping, now); - final int trackerMemFactor = mProcessTracker.getMemFactorLocked(); + allChanged = mProcessStats.setMemFactorLocked(memFactor, !mSleeping, now); + final int trackerMemFactor = mProcessStats.getMemFactorLocked(); for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); if (allChanged || app.procStateChanged) { @@ -14916,9 +14917,9 @@ public final class ActivityManagerService extends ActivityManagerNative } } } else { - allChanged = mProcessTracker.setMemFactorLocked( - ProcessTracker.ADJ_MEM_FACTOR_NORMAL, !mSleeping, now); - final int trackerMemFactor = mProcessTracker.getMemFactorLocked(); + allChanged = mProcessStats.setMemFactorLocked( + ProcessStats.ADJ_MEM_FACTOR_NORMAL, !mSleeping, now); + final int trackerMemFactor = mProcessStats.getMemFactorLocked(); for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); if (allChanged || app.procStateChanged) { @@ -14951,14 +14952,14 @@ public final class ActivityManagerService extends ActivityManagerNative } if (allChanged) { - requestPssAllProcsLocked(now, false, mProcessTracker.isMemFactorLowered()); + requestPssAllProcsLocked(now, false, mProcessStats.isMemFactorLowered()); } - if (mProcessTracker.shouldWriteNowLocked(now)) { + if (mProcessStats.shouldWriteNowLocked(now)) { mHandler.post(new Runnable() { @Override public void run() { synchronized (ActivityManagerService.this) { - mProcessTracker.writeStateAsyncLocked(); + mProcessStats.writeStateAsyncLocked(); } } }); diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index cc1be98..3cbd345 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -893,8 +893,8 @@ final class ActivityStack { if (prev.app != null && prev.cpuTimeAtResume > 0 && mService.mBatteryStatsService.isOnBattery()) { long diff; - synchronized (mService.mProcessStatsThread) { - diff = mService.mProcessStats.getCpuTimeForPid(prev.app.pid) + synchronized (mService.mProcessCpuThread) { + diff = mService.mProcessCpuTracker.getCpuTimeForPid(prev.app.pid) - prev.cpuTimeAtResume; } if (diff > 0) { @@ -902,7 +902,7 @@ final class ActivityStack { synchronized (bsi) { BatteryStatsImpl.Uid.Proc ps = bsi.getProcessStatsLocked(prev.info.applicationInfo.uid, - prev.info.packageName); + prev.info.packageName); if (ps != null) { ps.addForegroundTimeLocked(diff); } @@ -935,8 +935,8 @@ final class ActivityStack { // TODO: To be more accurate, the mark should be before the onCreate, // not after the onResume. But for subsequent starts, onResume is fine. if (next.app != null) { - synchronized (mService.mProcessStatsThread) { - next.cpuTimeAtResume = mService.mProcessStats.getCpuTimeForPid(next.app.pid); + synchronized (mService.mProcessCpuThread) { + next.cpuTimeAtResume = mService.mProcessCpuTracker.getCpuTimeForPid(next.app.pid); } } else { next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java index 1681c2d..633de8c 100644 --- a/services/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/java/com/android/server/am/ActivityStackSupervisor.java @@ -1027,7 +1027,7 @@ public final class ActivityStackSupervisor { if (app != null && app.thread != null) { try { - app.addPackage(r.info.packageName, mService.mProcessTracker); + app.addPackage(r.info.packageName, mService.mProcessStats); realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java index c0140e4..cb4b8ff 100644 --- a/services/java/com/android/server/am/BroadcastQueue.java +++ b/services/java/com/android/server/am/BroadcastQueue.java @@ -797,7 +797,7 @@ public final class BroadcastQueue { info.activityInfo.applicationInfo.uid); if (app != null && app.thread != null) { try { - app.addPackage(info.activityInfo.packageName, mService.mProcessTracker); + app.addPackage(info.activityInfo.packageName, mService.mProcessStats); processCurBroadcastLocked(r, app); return; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 2b73b8a..f1a030e 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -17,6 +17,7 @@ package com.android.server.am; import android.util.ArraySet; +import com.android.internal.app.ProcessStats; import com.android.internal.os.BatteryStatsImpl; import android.app.ActivityManager; @@ -51,10 +52,10 @@ final class ProcessRecord { final int uid; // uid of process; may be different from 'info' if isolated final int userId; // user of process. final String processName; // name of the process - final ProcessTracker.ProcessState baseProcessTracker; + final ProcessStats.ProcessState baseProcessTracker; // List of packages running in the process - final ArrayMap<String, ProcessTracker.ProcessState> pkgList - = new ArrayMap<String, ProcessTracker.ProcessState>(); + final ArrayMap<String, ProcessStats.ProcessState> pkgList + = new ArrayMap<String, ProcessStats.ProcessState>(); IApplicationThread thread; // the actual proc... may be null only if // 'persistent' is true (in which case we // are in the process of launching the app) @@ -350,7 +351,7 @@ final class ProcessRecord { ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, IApplicationThread _thread, ApplicationInfo _info, String _processName, int _uid, - ProcessTracker.ProcessState tracker) { + ProcessStats.ProcessState tracker) { batteryStats = _batteryStats; info = _info; isolated = _info.uid != _uid; @@ -487,7 +488,7 @@ final class ProcessRecord { /* * Return true if package has been added false if not */ - public boolean addPackage(String pkg, ProcessTracker tracker) { + public boolean addPackage(String pkg, ProcessStatsService tracker) { if (!pkgList.containsKey(pkg)) { pkgList.put(pkg, tracker.getProcessStateLocked(pkg, info.uid, processName)); return true; @@ -513,9 +514,9 @@ final class ProcessRecord { /* * Delete all packages from list except the package indicated in info */ - public void resetPackageList(ProcessTracker tracker) { + public void resetPackageList(ProcessStatsService tracker) { long now = SystemClock.uptimeMillis(); - baseProcessTracker.setState(ProcessTracker.STATE_NOTHING, + baseProcessTracker.setState(ProcessStats.STATE_NOTHING, tracker.getMemFactorLocked(), now, pkgList); if (pkgList.size() != 1) { pkgList.clear(); diff --git a/services/java/com/android/server/am/ProcessStatsService.java b/services/java/com/android/server/am/ProcessStatsService.java new file mode 100644 index 0000000..ee6f7ec --- /dev/null +++ b/services/java/com/android/server/am/ProcessStatsService.java @@ -0,0 +1,714 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.app.AppGlobals; +import android.content.pm.IPackageManager; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.AtomicFile; +import android.util.Slog; +import android.util.SparseArray; +import com.android.internal.app.ProcessStats; +import com.android.internal.os.BackgroundThread; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.locks.ReentrantLock; + +public final class ProcessStatsService { + static final String TAG = "ProcessStatsService"; + static final boolean DEBUG = false; + + // Most data is kept in a sparse data structure: an integer array which integer + // holds the type of the entry, and the identifier for a long array that data + // exists in and the offset into the array to find it. The constants below + // define the encoding of that data in an integer. + + static final int MAX_HISTORIC_STATES = 4; // Maximum number of historic states we will keep. + static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames. + static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames. + static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in. + static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so. + static long COMMIT_PERIOD = 24*60*60*1000; // Commit current stats every day. + + final Object mLock; + final File mBaseDir; + ProcessStats mProcessStats; + AtomicFile mFile; + boolean mCommitPending; + boolean mShuttingDown; + int mLastMemOnlyState = -1; + boolean mMemFactorLowered; + + final ReentrantLock mWriteLock = new ReentrantLock(); + final Object mPendingWriteLock = new Object(); + AtomicFile mPendingWriteFile; + Parcel mPendingWrite; + boolean mPendingWriteCommitted; + long mLastWriteTime; + + public ProcessStatsService(Object lock, File file) { + mLock = lock; + mBaseDir = file; + mBaseDir.mkdirs(); + mProcessStats = new ProcessStats(true); + updateFile(); + SystemProperties.addChangeCallback(new Runnable() { + @Override public void run() { + synchronized (mLock) { + if (mProcessStats.evaluateSystemProperties(false)) { + mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS; + writeStateLocked(true, true); + mProcessStats.evaluateSystemProperties(true); + } + } + } + }); + } + + public ProcessStats.ProcessState getProcessStateLocked(String packageName, + int uid, String processName) { + return mProcessStats.getProcessStateLocked(packageName, uid, processName); + } + + public ProcessStats.ServiceState getServiceStateLocked(String packageName, int uid, + String processName, String className) { + final ProcessStats.PackageState as = mProcessStats.getPackageStateLocked(packageName, uid); + ProcessStats.ServiceState ss = as.mServices.get(className); + if (ss != null) { + ss.makeActive(); + return ss; + } + final ProcessStats.ProcessState ps = mProcessStats.getProcessStateLocked(packageName, + uid, processName); + ss = new ProcessStats.ServiceState(mProcessStats, packageName, ps); + as.mServices.put(className, ss); + return ss; + } + + public boolean isMemFactorLowered() { + return mMemFactorLowered; + } + + public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) { + mMemFactorLowered = memFactor < mLastMemOnlyState; + mLastMemOnlyState = memFactor; + if (screenOn) { + memFactor += ProcessStats.ADJ_SCREEN_ON; + } + if (memFactor != mProcessStats.mMemFactor) { + if (mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING) { + mProcessStats.mMemFactorDurations[mProcessStats.mMemFactor] + += now - mProcessStats.mStartTime; + } + mProcessStats.mMemFactor = memFactor; + mProcessStats.mStartTime = now; + ArrayMap<String, SparseArray<ProcessStats.PackageState>> pmap + = mProcessStats.mPackages.getMap(); + for (int i=0; i<pmap.size(); i++) { + SparseArray<ProcessStats.PackageState> uids = pmap.valueAt(i); + for (int j=0; j<uids.size(); j++) { + ProcessStats.PackageState pkg = uids.valueAt(j); + ArrayMap<String, ProcessStats.ServiceState> services = pkg.mServices; + for (int k=0; k<services.size(); k++) { + ProcessStats.ServiceState service = services.valueAt(k); + if (service.isActive()) { + if (service.mStartedState != ProcessStats.STATE_NOTHING) { + service.setStarted(true, memFactor, now); + } + if (service.mBoundState != ProcessStats.STATE_NOTHING) { + service.setBound(true, memFactor, now); + } + if (service.mExecState != ProcessStats.STATE_NOTHING) { + service.setExecuting(true, memFactor, now); + } + } + } + } + } + return true; + } + return false; + } + + public int getMemFactorLocked() { + return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0; + } + + public boolean shouldWriteNowLocked(long now) { + if (now > (mLastWriteTime+WRITE_PERIOD)) { + if (SystemClock.elapsedRealtime() + > (mProcessStats.mTimePeriodStartRealtime+COMMIT_PERIOD)) { + mCommitPending = true; + } + return true; + } + return false; + } + + public void shutdownLocked() { + Slog.w(TAG, "Writing process stats before shutdown..."); + mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN; + writeStateSyncLocked(); + mShuttingDown = true; + } + + public void writeStateAsyncLocked() { + writeStateLocked(false); + } + + public void writeStateSyncLocked() { + writeStateLocked(true); + } + + private void writeStateLocked(boolean sync) { + if (mShuttingDown) { + return; + } + boolean commitPending = mCommitPending; + mCommitPending = false; + writeStateLocked(sync, commitPending); + } + + public void writeStateLocked(boolean sync, final boolean commit) { + synchronized (mPendingWriteLock) { + long now = SystemClock.uptimeMillis(); + if (mPendingWrite == null || !mPendingWriteCommitted) { + mPendingWrite = Parcel.obtain(); + mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); + if (commit) { + mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; + } + mProcessStats.writeToParcel(mPendingWrite); + mPendingWriteFile = new AtomicFile(mFile.getBaseFile()); + mPendingWriteCommitted = commit; + } + if (commit) { + mProcessStats.resetSafely(); + updateFile(); + } + mLastWriteTime = SystemClock.uptimeMillis(); + Slog.i(TAG, "Prepared write state in " + (SystemClock.uptimeMillis()-now) + "ms"); + if (!sync) { + BackgroundThread.getHandler().post(new Runnable() { + @Override public void run() { + performWriteState(); + } + }); + return; + } + } + + performWriteState(); + } + + private void updateFile() { + mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX + + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX)); + mLastWriteTime = SystemClock.uptimeMillis(); + } + + void performWriteState() { + if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile()); + Parcel data; + AtomicFile file; + synchronized (mPendingWriteLock) { + data = mPendingWrite; + file = mPendingWriteFile; + mPendingWriteCommitted = false; + if (data == null) { + return; + } + mPendingWrite = null; + mPendingWriteFile = null; + mWriteLock.lock(); + } + + FileOutputStream stream = null; + try { + stream = file.startWrite(); + stream.write(data.marshall()); + stream.flush(); + file.finishWrite(stream); + if (DEBUG) Slog.d(TAG, "Write completed successfully!"); + } catch (IOException e) { + Slog.w(TAG, "Error writing process statistics", e); + file.failWrite(stream); + } finally { + data.recycle(); + trimHistoricStatesWriteLocked(); + mWriteLock.unlock(); + } + } + + static byte[] readFully(FileInputStream stream) throws java.io.IOException { + int pos = 0; + int avail = stream.available(); + byte[] data = new byte[avail]; + while (true) { + int amt = stream.read(data, pos, data.length-pos); + //Log.i("foo", "Read " + amt + " bytes at " + pos + // + " of avail " + data.length); + if (amt <= 0) { + //Log.i("foo", "**** FINISHED READING: pos=" + pos + // + " len=" + data.length); + return data; + } + pos += amt; + avail = stream.available(); + if (avail > data.length-pos) { + byte[] newData = new byte[pos+avail]; + System.arraycopy(data, 0, newData, 0, pos); + data = newData; + } + } + } + + boolean readLocked(ProcessStats stats, AtomicFile file) { + try { + FileInputStream stream = file.openRead(); + + byte[] raw = readFully(stream); + Parcel in = Parcel.obtain(); + in.unmarshall(raw, 0, raw.length); + in.setDataPosition(0); + stream.close(); + + stats.readFromParcel(in); + if (stats.mReadError != null) { + Slog.w(TAG, "Ignoring existing stats; " + stats.mReadError); + if (DEBUG) { + ArrayMap<String, SparseArray<ProcessStats.ProcessState>> procMap + = stats.mProcesses.getMap(); + final int NPROC = procMap.size(); + for (int ip=0; ip<NPROC; ip++) { + Slog.w(TAG, "Process: " + procMap.keyAt(ip)); + SparseArray<ProcessStats.ProcessState> uids = procMap.valueAt(ip); + final int NUID = uids.size(); + for (int iu=0; iu<NUID; iu++) { + Slog.w(TAG, " Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu)); + } + } + ArrayMap<String, SparseArray<ProcessStats.PackageState>> pkgMap + = stats.mPackages.getMap(); + final int NPKG = pkgMap.size(); + for (int ip=0; ip<NPKG; ip++) { + Slog.w(TAG, "Package: " + pkgMap.keyAt(ip)); + SparseArray<ProcessStats.PackageState> uids = pkgMap.valueAt(ip); + final int NUID = uids.size(); + for (int iu=0; iu<NUID; iu++) { + Slog.w(TAG, " Uid: " + uids.keyAt(iu)); + ProcessStats.PackageState pkgState = uids.valueAt(iu); + final int NPROCS = pkgState.mProcesses.size(); + for (int iproc=0; iproc<NPROCS; iproc++) { + Slog.w(TAG, " Process " + pkgState.mProcesses.keyAt(iproc) + + ": " + pkgState.mProcesses.valueAt(iproc)); + } + final int NSRVS = pkgState.mServices.size(); + for (int isvc=0; isvc<NSRVS; isvc++) { + Slog.w(TAG, " Service " + pkgState.mServices.keyAt(isvc) + + ": " + pkgState.mServices.valueAt(isvc)); + } + } + } + } + return false; + } + } catch (Throwable e) { + stats.mReadError = "caught exception: " + e; + Slog.e(TAG, "Error reading process statistics", e); + return false; + } + return true; + } + + private ArrayList<String> getCommittedFiles(int minNum, boolean inclAll) { + File[] files = mBaseDir.listFiles(); + if (files == null || files.length <= minNum) { + return null; + } + ArrayList<String> filesArray = new ArrayList<String>(files.length); + String currentFile = mFile.getBaseFile().getPath(); + if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile); + for (int i=0; i<files.length; i++) { + File file = files[i]; + String fileStr = file.getPath(); + if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr); + if (!inclAll && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) { + if (DEBUG) Slog.d(TAG, "Skipping: already checked in"); + continue; + } + if (fileStr.equals(currentFile)) { + if (DEBUG) Slog.d(TAG, "Skipping: current stats"); + continue; + } + filesArray.add(fileStr); + } + Collections.sort(filesArray); + return filesArray; + } + + public void trimHistoricStatesWriteLocked() { + ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, true); + if (filesArray == null) { + return; + } + while (filesArray.size() > MAX_HISTORIC_STATES) { + String file = filesArray.remove(0); + Slog.i(TAG, "Pruning old procstats: " + file); + (new File(file)).delete(); + } + } + + boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header, + boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, + boolean sepProcStates, int[] procStates, long now, String reqPackage) { + ArrayList<ProcessStats.ProcessState> procs = mProcessStats.collectProcessesLocked( + screenStates, memStates, procStates, now, reqPackage); + if (procs.size() > 0) { + if (header != null) { + pw.println(header); + } + ProcessStats.dumpProcessListCsv(pw, procs, sepScreenStates, screenStates, + sepMemStates, memStates, sepProcStates, procStates, now); + return true; + } + return false; + } + + static int[] parseStateList(String[] states, int mult, String arg, boolean[] outSep, + String[] outError) { + ArrayList<Integer> res = new ArrayList<Integer>(); + int lastPos = 0; + for (int i=0; i<=arg.length(); i++) { + char c = i < arg.length() ? arg.charAt(i) : 0; + if (c != ',' && c != '+' && c != ' ' && c != 0) { + continue; + } + boolean isSep = c == ','; + if (lastPos == 0) { + // We now know the type of op. + outSep[0] = isSep; + } else if (c != 0 && outSep[0] != isSep) { + outError[0] = "inconsistent separators (can't mix ',' with '+')"; + return null; + } + if (lastPos < (i-1)) { + String str = arg.substring(lastPos, i); + for (int j=0; j<states.length; j++) { + if (str.equals(states[j])) { + res.add(j); + str = null; + break; + } + } + if (str != null) { + outError[0] = "invalid word \"" + str + "\""; + return null; + } + } + lastPos = i + 1; + } + + int[] finalRes = new int[res.size()]; + for (int i=0; i<res.size(); i++) { + finalRes[i] = res.get(i) * mult; + } + return finalRes; + } + + static private void dumpHelp(PrintWriter pw) { + pw.println("Process stats (procstats) dump options:"); + pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]"); + pw.println(" [--details] [--current] [--commit] [--write] [-h] [<package.name>]"); + pw.println(" --checkin: perform a checkin: print and delete old committed states."); + pw.println(" --c: print only state in checkin format."); + pw.println(" --csv: output data suitable for putting in a spreadsheet."); + pw.println(" --csv-screen: on, off."); + pw.println(" --csv-mem: norm, mod, low, crit."); + pw.println(" --csv-proc: pers, top, fore, vis, precept, backup,"); + pw.println(" service, home, prev, cached"); + pw.println(" --details: dump all execution details, not just summary."); + pw.println(" --current: only dump current state."); + pw.println(" --commit: commit current stats to disk and reset to start new stats."); + pw.println(" --write: write current in-memory stats to disk."); + pw.println(" --read: replace current stats with last-written stats."); + pw.println(" -a: print everything."); + pw.println(" -h: print this help text."); + pw.println(" <package.name>: optional name of package to filter output by."); + } + + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + final long now = SystemClock.uptimeMillis(); + + boolean isCheckin = false; + boolean isCompact = false; + boolean isCsv = false; + boolean currentOnly = false; + boolean dumpDetails = false; + boolean dumpAll = false; + String reqPackage = null; + boolean csvSepScreenStats = false; + int[] csvScreenStats = new int[] { ProcessStats.ADJ_SCREEN_OFF, ProcessStats.ADJ_SCREEN_ON}; + boolean csvSepMemStats = false; + int[] csvMemStats = new int[] { ProcessStats.ADJ_MEM_FACTOR_CRITICAL}; + boolean csvSepProcStats = true; + int[] csvProcStats = ProcessStats.ALL_PROC_STATES; + if (args != null) { + for (int i=0; i<args.length; i++) { + String arg = args[i]; + if ("--checkin".equals(arg)) { + isCheckin = true; + } else if ("-c".equals(arg)) { + isCompact = true; + } else if ("--csv".equals(arg)) { + isCsv = true; + } else if ("--csv-screen".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("Error: argument required for --csv-screen"); + dumpHelp(pw); + return; + } + boolean[] sep = new boolean[1]; + String[] error = new String[1]; + csvScreenStats = parseStateList(ProcessStats.ADJ_SCREEN_NAMES_CSV, ProcessStats.ADJ_SCREEN_MOD, + args[i], sep, error); + if (csvScreenStats == null) { + pw.println("Error in \"" + args[i] + "\": " + error[0]); + dumpHelp(pw); + return; + } + csvSepScreenStats = sep[0]; + } else if ("--csv-mem".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("Error: argument required for --csv-mem"); + dumpHelp(pw); + return; + } + boolean[] sep = new boolean[1]; + String[] error = new String[1]; + csvMemStats = parseStateList(ProcessStats.ADJ_MEM_NAMES_CSV, 1, args[i], sep, error); + if (csvMemStats == null) { + pw.println("Error in \"" + args[i] + "\": " + error[0]); + dumpHelp(pw); + return; + } + csvSepMemStats = sep[0]; + } else if ("--csv-proc".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("Error: argument required for --csv-proc"); + dumpHelp(pw); + return; + } + boolean[] sep = new boolean[1]; + String[] error = new String[1]; + csvProcStats = parseStateList(ProcessStats.STATE_NAMES_CSV, 1, args[i], sep, error); + if (csvProcStats == null) { + pw.println("Error in \"" + args[i] + "\": " + error[0]); + dumpHelp(pw); + return; + } + csvSepProcStats = sep[0]; + } else if ("--details".equals(arg)) { + dumpDetails = true; + } else if ("--current".equals(arg)) { + currentOnly = true; + } else if ("--commit".equals(arg)) { + mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; + writeStateLocked(true, true); + pw.println("Process stats committed."); + return; + } else if ("--write".equals(arg)) { + writeStateSyncLocked(); + pw.println("Process stats written."); + return; + } else if ("--read".equals(arg)) { + readLocked(mProcessStats, mFile); + pw.println("Process stats read."); + return; + } else if ("-h".equals(arg)) { + dumpHelp(pw); + return; + } else if ("-a".equals(arg)) { + dumpDetails = true; + dumpAll = true; + } else if (arg.length() > 0 && arg.charAt(0) == '-'){ + pw.println("Unknown option: " + arg); + dumpHelp(pw); + return; + } else { + // Not an option, last argument must be a package name. + try { + IPackageManager pm = AppGlobals.getPackageManager(); + if (pm.getPackageUid(arg, UserHandle.getCallingUserId()) >= 0) { + reqPackage = arg; + // Include all details, since we know we are only going to + // be dumping a smaller set of data. In fact only the details + // container per-package data, so that are needed to be able + // to dump anything at all when filtering by package. + dumpDetails = true; + } + } catch (RemoteException e) { + } + if (reqPackage == null) { + pw.println("Unknown package: " + arg); + dumpHelp(pw); + return; + } + } + } + } + + if (isCsv) { + pw.print("Processes running summed over"); + if (!csvSepScreenStats) { + for (int i=0; i<csvScreenStats.length; i++) { + pw.print(" "); + ProcessStats.printScreenLabelCsv(pw, csvScreenStats[i]); + } + } + if (!csvSepMemStats) { + for (int i=0; i<csvMemStats.length; i++) { + pw.print(" "); + ProcessStats.printMemLabelCsv(pw, csvMemStats[i]); + } + } + if (!csvSepProcStats) { + for (int i=0; i<csvProcStats.length; i++) { + pw.print(" "); + pw.print(ProcessStats.STATE_NAMES_CSV[csvProcStats[i]]); + } + } + pw.println(); + synchronized (mLock) { + dumpFilteredProcessesCsvLocked(pw, null, + csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats, + csvSepProcStats, csvProcStats, now, reqPackage); + /* + dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:", + false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, + true, new int[] {ADJ_MEM_FACTOR_CRITICAL}, + true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, + STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, + STATE_PREVIOUS, STATE_CACHED}, + now, reqPackage); + dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:", + false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, + false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW, + ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE}, + true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, + STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, + STATE_PREVIOUS, STATE_CACHED}, + now, reqPackage); + */ + } + return; + } + + boolean sepNeeded = false; + if (!currentOnly || isCheckin) { + mWriteLock.lock(); + try { + ArrayList<String> files = getCommittedFiles(0, !isCheckin); + if (files != null) { + for (int i=0; i<files.size(); i++) { + if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i)); + try { + AtomicFile file = new AtomicFile(new File(files.get(i))); + ProcessStats processStats = new ProcessStats(false); + readLocked(processStats, file); + if (processStats.mReadError != null) { + if (isCheckin || isCompact) pw.print("err,"); + pw.print("Failure reading "); pw.print(files.get(i)); + pw.print("; "); pw.println(processStats.mReadError); + if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i)); + (new File(files.get(i))).delete(); + continue; + } + String fileStr = file.getBaseFile().getPath(); + boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX); + if (isCheckin || isCompact) { + // Don't really need to lock because we uniquely own this object. + processStats.dumpCheckinLocked(pw, reqPackage); + } else { + if (sepNeeded) { + pw.println(); + } else { + sepNeeded = true; + } + pw.print("COMMITTED STATS FROM "); + pw.print(processStats.mTimePeriodStartClockStr); + if (checkedIn) pw.print(" (checked in)"); + pw.println(":"); + // Don't really need to lock because we uniquely own this object. + if (dumpDetails) { + processStats.dumpLocked(pw, reqPackage, now, dumpAll); + } else { + processStats.dumpSummaryLocked(pw, reqPackage, now); + } + } + if (isCheckin) { + // Rename file suffix to mark that it has checked in. + file.getBaseFile().renameTo(new File( + fileStr + STATE_FILE_CHECKIN_SUFFIX)); + } + } catch (Throwable e) { + pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i)); + e.printStackTrace(pw); + } + } + } + } finally { + mWriteLock.unlock(); + } + } + if (!isCheckin) { + synchronized (mLock) { + if (isCompact) { + mProcessStats.dumpCheckinLocked(pw, reqPackage); + } else { + if (sepNeeded) { + pw.println(); + pw.println("CURRENT STATS:"); + } + if (dumpDetails) { + mProcessStats.dumpLocked(pw, reqPackage, now, dumpAll); + if (dumpAll) { + pw.print(" mFile="); pw.println(mFile.getBaseFile()); + } + } else { + mProcessStats.dumpSummaryLocked(pw, reqPackage, now); + } + } + } + } + } +} diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java deleted file mode 100644 index d78fd5c..0000000 --- a/services/java/com/android/server/am/ProcessTracker.java +++ /dev/null @@ -1,3126 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.am; - -import android.app.AppGlobals; -import android.content.pm.IPackageManager; -import android.os.Parcel; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.text.format.DateFormat; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.AtomicFile; -import android.util.Slog; -import android.util.SparseArray; -import android.util.TimeUtils; -import android.webkit.WebViewFactory; -import com.android.internal.os.BackgroundThread; -import com.android.internal.util.ArrayUtils; -import com.android.server.ProcessMap; -import dalvik.system.VMRuntime; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.Objects; -import java.util.concurrent.locks.ReentrantLock; - -public final class ProcessTracker { - static final String TAG = "ProcessTracker"; - static final boolean DEBUG = false; - - public static final int STATE_NOTHING = -1; - public static final int STATE_PERSISTENT = 0; - public static final int STATE_TOP = 1; - public static final int STATE_IMPORTANT_FOREGROUND = 2; - public static final int STATE_IMPORTANT_BACKGROUND = 3; - public static final int STATE_BACKUP = 4; - public static final int STATE_HEAVY_WEIGHT = 5; - public static final int STATE_SERVICE = 6; - public static final int STATE_SERVICE_RESTARTING = 7; - public static final int STATE_RECEIVER = 8; - public static final int STATE_HOME = 9; - public static final int STATE_LAST_ACTIVITY = 10; - public static final int STATE_CACHED_ACTIVITY = 11; - public static final int STATE_CACHED_ACTIVITY_CLIENT = 12; - public static final int STATE_CACHED_EMPTY = 13; - public static final int STATE_COUNT = STATE_CACHED_EMPTY+1; - - static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, - STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, - STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, - STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY, - STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY - }; - - static final int[] NON_CACHED_PROC_STATES = new int[] { STATE_PERSISTENT, - STATE_TOP, STATE_IMPORTANT_FOREGROUND, - STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT, - STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, STATE_HOME - }; - - public static final int PSS_SAMPLE_COUNT = 0; - public static final int PSS_MINIMUM = 1; - public static final int PSS_AVERAGE = 2; - public static final int PSS_MAXIMUM = 3; - public static final int PSS_USS_MINIMUM = 4; - public static final int PSS_USS_AVERAGE = 5; - public static final int PSS_USS_MAXIMUM = 6; - public static final int PSS_COUNT = PSS_USS_MAXIMUM+1; - - public static final int ADJ_NOTHING = -1; - public static final int ADJ_MEM_FACTOR_NORMAL = 0; - public static final int ADJ_MEM_FACTOR_MODERATE = 1; - public static final int ADJ_MEM_FACTOR_LOW = 2; - public static final int ADJ_MEM_FACTOR_CRITICAL = 3; - public static final int ADJ_MEM_FACTOR_COUNT = ADJ_MEM_FACTOR_CRITICAL+1; - public static final int ADJ_SCREEN_MOD = ADJ_MEM_FACTOR_COUNT; - public static final int ADJ_SCREEN_OFF = 0; - public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD; - public static final int ADJ_COUNT = ADJ_SCREEN_ON*2; - - static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON }; - static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, ADJ_MEM_FACTOR_MODERATE, - ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL }; - - // Most data is kept in a sparse data structure: an integer array which integer - // holds the type of the entry, and the identifier for a long array that data - // exists in and the offset into the array to find it. The constants below - // define the encoding of that data in an integer. - - // Where the "type"/"state" part of the data appears in an offset integer. - static int OFFSET_TYPE_SHIFT = 0; - static int OFFSET_TYPE_MASK = 0xff; - - // Where the "which array" part of the data appears in an offset integer. - static int OFFSET_ARRAY_SHIFT = 8; - static int OFFSET_ARRAY_MASK = 0xff; - - // Where the "index into array" part of the data appears in an offset integer. - static int OFFSET_INDEX_SHIFT = 16; - static int OFFSET_INDEX_MASK = 0xffff; - - static final String[] STATE_NAMES = new String[] { - "Persistent", "Top ", "Imp Fg ", "Imp Bg ", - "Backup ", "Heavy Wght", "Service ", "Service Rs", - "Receiver ", "Home ", - "Last Act ", "Cch Act ", "Cch CliAct", "Cch Empty " - }; - - static final String[] ADJ_SCREEN_NAMES_CSV = new String[] { - "off", "on" - }; - - static final String[] ADJ_MEM_NAMES_CSV = new String[] { - "norm", "mod", "low", "crit" - }; - - static final String[] STATE_NAMES_CSV = new String[] { - "pers", "top", "impfg", "impbg", "backup", "heavy", - "service", "service-rs", "receiver", "home", "lastact", - "cch-activity", "cch-aclient", "cch-empty" - }; - - static final String[] ADJ_SCREEN_TAGS = new String[] { - "0", "1" - }; - - static final String[] ADJ_MEM_TAGS = new String[] { - "n", "m", "l", "c" - }; - - static final String[] STATE_TAGS = new String[] { - "p", "t", "f", "b", "u", "w", - "s", "x", "r", "h", "l", "a", "c", "e" - }; - - // Map from process states to the states we track. - static final int[] PROCESS_STATE_TO_STATE = new int[] { - STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT - STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI - STATE_TOP, // ActivityManager.PROCESS_STATE_TOP - STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND - STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND - STATE_BACKUP, // ActivityManager.PROCESS_STATE_BACKUP - STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT - STATE_SERVICE, // ActivityManager.PROCESS_STATE_SERVICE - STATE_RECEIVER, // ActivityManager.PROCESS_STATE_RECEIVER - STATE_HOME, // ActivityManager.PROCESS_STATE_HOME - STATE_LAST_ACTIVITY, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY - STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY - STATE_CACHED_ACTIVITY_CLIENT, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT - STATE_CACHED_EMPTY, // ActivityManager.PROCESS_STATE_CACHED_EMPTY - }; - - static final String CSV_SEP = "\t"; - - static final int MAX_HISTORIC_STATES = 4; // Maximum number of historic states we will keep. - static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames. - static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames. - static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in. - static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so. - static long COMMIT_PERIOD = 24*60*60*1000; // Commit current stats every day. - - final Object mLock; - final File mBaseDir; - State mState; - boolean mCommitPending; - boolean mShuttingDown; - int mLastMemOnlyState = -1; - boolean mMemFactorLowered; - - final ReentrantLock mWriteLock = new ReentrantLock(); - - public static final class ProcessState { - final State mState; - final ProcessState mCommonProcess; - final String mPackage; - final int mUid; - final String mName; - - int[] mDurationsTable; - int mDurationsTableSize; - - //final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT]; - int mCurState = STATE_NOTHING; - long mStartTime; - - int mLastPssState = STATE_NOTHING; - long mLastPssTime; - int[] mPssTable; - int mPssTableSize; - - int mNumStartedServices; - - int mNumExcessiveWake; - int mNumExcessiveCpu; - - boolean mMultiPackage; - - long mTmpTotalTime; - - /** - * Create a new top-level process state, for the initial case where there is only - * a single package running in a process. The initial state is not running. - */ - public ProcessState(State state, String pkg, int uid, String name) { - mState = state; - mCommonProcess = this; - mPackage = pkg; - mUid = uid; - mName = name; - } - - /** - * Create a new per-package process state for an existing top-level process - * state. The current running state of the top-level process is also copied, - * marked as started running at 'now'. - */ - public ProcessState(ProcessState commonProcess, String pkg, int uid, String name, - long now) { - mState = commonProcess.mState; - mCommonProcess = commonProcess; - mPackage = pkg; - mUid = uid; - mName = name; - mCurState = commonProcess.mCurState; - mStartTime = now; - } - - ProcessState clone(String pkg, long now) { - ProcessState pnew = new ProcessState(this, pkg, mUid, mName, now); - if (mDurationsTable != null) { - mState.mAddLongTable = new int[mDurationsTable.length]; - mState.mAddLongTableSize = 0; - for (int i=0; i<mDurationsTableSize; i++) { - int origEnt = mDurationsTable[i]; - int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - int newOff = mState.addLongData(i, type, 1); - mState.mAddLongTable[i] = newOff | type; - mState.setLong(newOff, 0, mState.getLong(origEnt, 0)); - } - pnew.mDurationsTable = mState.mAddLongTable; - pnew.mDurationsTableSize = mState.mAddLongTableSize; - } - if (mPssTable != null) { - mState.mAddLongTable = new int[mPssTable.length]; - mState.mAddLongTableSize = 0; - for (int i=0; i<mPssTableSize; i++) { - int origEnt = mPssTable[i]; - int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - int newOff = mState.addLongData(i, type, PSS_COUNT); - mState.mAddLongTable[i] = newOff | type; - for (int j=0; j<PSS_COUNT; j++) { - mState.setLong(newOff, j, mState.getLong(origEnt, j)); - } - } - pnew.mPssTable = mState.mAddLongTable; - pnew.mPssTableSize = mState.mAddLongTableSize; - } - pnew.mNumExcessiveWake = mNumExcessiveWake; - pnew.mNumExcessiveCpu = mNumExcessiveCpu; - pnew.mNumStartedServices = mNumStartedServices; - return pnew; - } - - void resetSafely(long now) { - mDurationsTable = null; - mDurationsTableSize = 0; - mStartTime = now; - mLastPssState = STATE_NOTHING; - mLastPssTime = 0; - mPssTable = null; - mPssTableSize = 0; - mNumExcessiveWake = 0; - mNumExcessiveCpu = 0; - } - - void writeToParcel(Parcel out, long now) { - commitStateTime(now); - out.writeInt(mMultiPackage ? 1 : 0); - out.writeInt(mDurationsTableSize); - for (int i=0; i<mDurationsTableSize; i++) { - if (DEBUG) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": " - + State.printLongOffset(mDurationsTable[i])); - out.writeInt(mDurationsTable[i]); - } - out.writeInt(mPssTableSize); - for (int i=0; i<mPssTableSize; i++) { - if (DEBUG) Slog.i(TAG, "Writing in " + mName + " pss #" + i + ": " - + State.printLongOffset(mPssTable[i])); - out.writeInt(mPssTable[i]); - } - out.writeInt(mNumExcessiveWake); - out.writeInt(mNumExcessiveCpu); - } - - boolean readFromParcel(Parcel in, boolean fully) { - boolean multiPackage = in.readInt() != 0; - if (fully) { - mMultiPackage = multiPackage; - } - if (DEBUG) Slog.d(TAG, "Reading durations table..."); - mDurationsTable = mState.readTableFromParcel(in, mName, "durations"); - if (mDurationsTable == State.BAD_TABLE) { - return false; - } - mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0; - if (DEBUG) Slog.d(TAG, "Reading pss table..."); - mPssTable = mState.readTableFromParcel(in, mName, "pss"); - if (mPssTable == State.BAD_TABLE) { - return false; - } - mPssTableSize = mPssTable != null ? mPssTable.length : 0; - mNumExcessiveWake = in.readInt(); - mNumExcessiveCpu = in.readInt(); - return true; - } - - /** - * Update the current state of the given list of processes. - * - * @param state Current ActivityManager.PROCESS_STATE_* - * @param memFactor Current mem factor constant. - * @param now Current time. - * @param pkgList Processes to update. - */ - public void setState(int state, int memFactor, long now, - ArrayMap<String, ProcessTracker.ProcessState> pkgList) { - if (state < 0) { - state = mNumStartedServices > 0 - ? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING; - } else { - state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT); - } - - // First update the common process. - mCommonProcess.setState(state, now); - - // If the common process is not multi-package, there is nothing else to do. - if (!mCommonProcess.mMultiPackage) { - return; - } - - if (pkgList != null) { - for (int ip=pkgList.size()-1; ip>=0; ip--) { - pullFixedProc(pkgList, ip).setState(state, now); - } - } - } - - void setState(int state, long now) { - if (mCurState != state) { - //Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state); - commitStateTime(now); - mCurState = state; - } - } - - void commitStateTime(long now) { - if (mCurState != STATE_NOTHING) { - long dur = now - mStartTime; - if (dur > 0) { - int idx = State.binarySearch(mDurationsTable, mDurationsTableSize, mCurState); - int off; - if (idx >= 0) { - off = mDurationsTable[idx]; - } else { - mState.mAddLongTable = mDurationsTable; - mState.mAddLongTableSize = mDurationsTableSize; - off = mState.addLongData(~idx, mCurState, 1); - mDurationsTable = mState.mAddLongTable; - mDurationsTableSize = mState.mAddLongTableSize; - } - long[] longs = mState.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur; - } - } - mStartTime = now; - } - - void incStartedServices(int memFactor, long now) { - if (mCommonProcess != this) { - mCommonProcess.incStartedServices(memFactor, now); - } - mNumStartedServices++; - if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) { - setState(STATE_NOTHING, memFactor, now, null); - } - } - - void decStartedServices(int memFactor, long now) { - if (mCommonProcess != this) { - mCommonProcess.decStartedServices(memFactor, now); - } - mNumStartedServices--; - if (mNumStartedServices == 0 && mCurState == STATE_SERVICE_RESTARTING) { - setState(STATE_NOTHING, memFactor, now, null); - } else if (mNumStartedServices < 0) { - throw new IllegalStateException("Proc started services underrun: pkg=" - + mPackage + " uid=" + mUid + " name=" + mName); - } - } - - public void addPss(long pss, long uss, boolean always) { - if (!always) { - if (mLastPssState == mCurState && SystemClock.uptimeMillis() - < (mLastPssTime+(30*1000))) { - return; - } - } - mLastPssState = mCurState; - mLastPssTime = SystemClock.uptimeMillis(); - if (mCurState != STATE_NOTHING) { - int idx = State.binarySearch(mPssTable, mPssTableSize, mCurState); - int off; - if (idx >= 0) { - off = mPssTable[idx]; - } else { - mState.mAddLongTable = mPssTable; - mState.mAddLongTableSize = mPssTableSize; - off = mState.addLongData(~idx, mCurState, PSS_COUNT); - mPssTable = mState.mAddLongTable; - mPssTableSize = mState.mAddLongTableSize; - } - long[] longs = mState.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK; - long count = longs[idx+PSS_SAMPLE_COUNT]; - if (count == 0) { - longs[idx+PSS_SAMPLE_COUNT] = 1; - longs[idx+PSS_MINIMUM] = pss; - longs[idx+PSS_AVERAGE] = pss; - longs[idx+PSS_MAXIMUM] = pss; - longs[idx+PSS_USS_MINIMUM] = uss; - longs[idx+PSS_USS_AVERAGE] = uss; - longs[idx+PSS_USS_MAXIMUM] = uss; - } else { - longs[idx+PSS_SAMPLE_COUNT] = count+1; - if (longs[idx+PSS_MINIMUM] > pss) { - longs[idx+PSS_MINIMUM] = pss; - } - longs[idx+PSS_AVERAGE] = (long)( - ((longs[idx+PSS_AVERAGE]*(double)count)+pss) / (count+1) ); - if (longs[idx+PSS_MAXIMUM] < pss) { - longs[idx+PSS_MAXIMUM] = pss; - } - if (longs[idx+PSS_USS_MINIMUM] > uss) { - longs[idx+PSS_USS_MINIMUM] = uss; - } - longs[idx+PSS_USS_AVERAGE] = (long)( - ((longs[idx+PSS_USS_AVERAGE]*(double)count)+uss) / (count+1) ); - if (longs[idx+PSS_USS_MAXIMUM] < uss) { - longs[idx+PSS_USS_MAXIMUM] = uss; - } - } - } - } - - public void reportExcessiveWake(ArrayMap<String, ProcessTracker.ProcessState> pkgList) { - mCommonProcess.mNumExcessiveWake++; - if (!mCommonProcess.mMultiPackage) { - return; - } - - for (int ip=pkgList.size()-1; ip>=0; ip--) { - pullFixedProc(pkgList, ip).mNumExcessiveWake++; - } - } - - public void reportExcessiveCpu(ArrayMap<String, ProcessTracker.ProcessState> pkgList) { - mCommonProcess.mNumExcessiveCpu++; - if (!mCommonProcess.mMultiPackage) { - return; - } - - for (int ip=pkgList.size()-1; ip>=0; ip--) { - pullFixedProc(pkgList, ip).mNumExcessiveCpu++; - } - } - - ProcessState pullFixedProc(String pkgName) { - if (mMultiPackage) { - // The array map is still pointing to a common process state - // that is now shared across packages. Update it to point to - // the new per-package state. - ProcessState proc = mState.mPackages.get(pkgName, mUid).mProcesses.get(mName); - if (proc == null) { - throw new IllegalStateException("Didn't create per-package process"); - } - return proc; - } - return this; - } - - private ProcessState pullFixedProc(ArrayMap<String, ProcessTracker.ProcessState> pkgList, - int index) { - ProcessState proc = pkgList.valueAt(index); - if (proc.mMultiPackage) { - // The array map is still pointing to a common process state - // that is now shared across packages. Update it to point to - // the new per-package state. - proc = mState.mPackages.get(pkgList.keyAt(index), - proc.mUid).mProcesses.get(proc.mName); - if (proc == null) { - throw new IllegalStateException("Didn't create per-package process"); - } - pkgList.setValueAt(index, proc); - } - return proc; - } - - long getDuration(int state, long now) { - int idx = State.binarySearch(mDurationsTable, mDurationsTableSize, state); - long time = idx >= 0 ? mState.getLong(mDurationsTable[idx], 0) : 0; - if (mCurState == state) { - time += now - mStartTime; - } - return time; - } - - long getPssSampleCount(int state) { - int idx = State.binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_SAMPLE_COUNT) : 0; - } - - long getPssMinimum(int state) { - int idx = State.binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_MINIMUM) : 0; - } - - long getPssAverage(int state) { - int idx = State.binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_AVERAGE) : 0; - } - - long getPssMaximum(int state) { - int idx = State.binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_MAXIMUM) : 0; - } - - long getPssUssMinimum(int state) { - int idx = State.binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0; - } - - long getPssUssAverage(int state) { - int idx = State.binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0; - } - - long getPssUssMaximum(int state) { - int idx = State.binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0; - } - } - - public static final class ServiceState { - final State mState; - final String mPackage; - ProcessState mProc; - - int mActive = 1; - - static final int SERVICE_STARTED = 0; - static final int SERVICE_BOUND = 1; - static final int SERVICE_EXEC = 2; - static final int SERVICE_COUNT = 3; - - int[] mDurationsTable; - int mDurationsTableSize; - - int mStartedCount; - int mStartedState = STATE_NOTHING; - long mStartedStartTime; - - int mBoundCount; - int mBoundState = STATE_NOTHING; - long mBoundStartTime; - - int mExecCount; - int mExecState = STATE_NOTHING; - long mExecStartTime; - - ServiceState(State state, String pkg, ProcessState proc) { - mState = state; - mPackage = pkg; - mProc = proc; - } - - void makeActive() { - mActive++; - } - - void makeInactive() { - /* - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.i(TAG, "Making " + this + " inactive", here); - */ - mActive--; - } - - boolean isActive() { - return mActive > 0; - } - - void resetSafely(long now) { - mDurationsTable = null; - mDurationsTableSize = 0; - mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0; - mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0; - mExecCount = mExecState != STATE_NOTHING ? 1 : 0; - mStartedStartTime = mBoundStartTime = mExecStartTime = now; - } - - void writeToParcel(Parcel out, long now) { - if (mStartedState != STATE_NOTHING) { - addStateTime(SERVICE_STARTED, mStartedState, now - mStartedStartTime); - mStartedStartTime = now; - } - if (mBoundState != STATE_NOTHING) { - addStateTime(SERVICE_BOUND, mBoundState, now - mBoundStartTime); - mBoundStartTime = now; - } - if (mExecState != STATE_NOTHING) { - addStateTime(SERVICE_EXEC, mExecState, now - mExecStartTime); - mExecStartTime = now; - } - out.writeInt(mDurationsTableSize); - for (int i=0; i<mDurationsTableSize; i++) { - if (DEBUG) Slog.i(TAG, "Writing service in " + mPackage + " dur #" + i + ": " - + State.printLongOffset(mDurationsTable[i])); - out.writeInt(mDurationsTable[i]); - } - out.writeInt(mStartedCount); - out.writeInt(mBoundCount); - out.writeInt(mExecCount); - } - - boolean readFromParcel(Parcel in) { - if (DEBUG) Slog.d(TAG, "Reading durations table..."); - mDurationsTable = mState.readTableFromParcel(in, mPackage, "service"); - if (mDurationsTable == State.BAD_TABLE) { - return false; - } - mStartedCount = in.readInt(); - mBoundCount = in.readInt(); - mExecCount = in.readInt(); - return true; - } - - void addStateTime(int opType, int memFactor, long time) { - if (time > 0) { - int state = opType + (memFactor*SERVICE_COUNT); - int idx = State.binarySearch(mDurationsTable, mDurationsTableSize, state); - int off; - if (idx >= 0) { - off = mDurationsTable[idx]; - } else { - mState.mAddLongTable = mDurationsTable; - mState.mAddLongTableSize = mDurationsTableSize; - off = mState.addLongData(~idx, state, 1); - mDurationsTable = mState.mAddLongTable; - mDurationsTableSize = mState.mAddLongTableSize; - } - long[] longs = mState.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += time; - } - } - - public void setStarted(boolean started, int memFactor, long now) { - if (mActive <= 0) { - throw new IllegalStateException("Service " + this + " has mActive=" + mActive); - } - int state = started ? memFactor : STATE_NOTHING; - if (mStartedState != state) { - if (mStartedState != STATE_NOTHING) { - addStateTime(SERVICE_STARTED, mStartedState, now - mStartedStartTime); - } else if (started) { - mStartedCount++; - } - mStartedState = state; - mStartedStartTime = now; - if (mProc != null) { - mProc = mProc.pullFixedProc(mPackage); - if (started) { - mProc.incStartedServices(memFactor, now); - } else { - mProc.decStartedServices(memFactor, now); - } - } - } - } - - public void setBound(boolean bound, int memFactor, long now) { - if (mActive <= 0) { - throw new IllegalStateException("Service " + this + " has mActive=" + mActive); - } - int state = bound ? memFactor : STATE_NOTHING; - if (mBoundState != state) { - if (mBoundState != STATE_NOTHING) { - addStateTime(SERVICE_BOUND, mBoundState, now - mBoundStartTime); - } else if (bound) { - mBoundCount++; - } - mBoundState = state; - mBoundStartTime = now; - } - } - - public void setExecuting(boolean executing, int memFactor, long now) { - if (mActive <= 0) { - throw new IllegalStateException("Service " + this + " has mActive=" + mActive); - } - int state = executing ? memFactor : STATE_NOTHING; - if (mExecState != state) { - if (mExecState != STATE_NOTHING) { - addStateTime(SERVICE_EXEC, mExecState, now - mExecStartTime); - } else if (executing) { - mExecCount++; - } - mExecState = state; - mExecStartTime = now; - } - } - - long getStartDuration(int opType, int memFactor, long now) { - switch (opType) { - case SERVICE_STARTED: - return getDuration(opType, mStartedState, mStartedStartTime, memFactor, now); - case SERVICE_BOUND: - return getDuration(opType, mBoundState, mBoundStartTime, memFactor, now); - case SERVICE_EXEC: - return getDuration(opType, mExecState, mExecStartTime, memFactor, now); - default: - throw new IllegalArgumentException("Bad opType: " + opType); - } - } - - - private long getDuration(int opType, int curState, long startTime, int memFactor, - long now) { - int state = opType + (memFactor*SERVICE_COUNT); - int idx = State.binarySearch(mDurationsTable, mDurationsTableSize, state); - long time = idx >= 0 ? mState.getLong(mDurationsTable[idx], 0) : 0; - if (curState == memFactor) { - time += now - startTime; - } - return time; - } - } - - public static final class PackageState { - final ArrayMap<String, ProcessState> mProcesses = new ArrayMap<String, ProcessState>(); - final ArrayMap<String, ServiceState> mServices = new ArrayMap<String, ServiceState>(); - final int mUid; - - public PackageState(int uid) { - mUid = uid; - } - } - - static final class State { - // Current version of the parcel format. - private static final int PARCEL_VERSION = 9; - // In-memory Parcel magic number, used to detect attempts to unmarshall bad data - private static final int MAGIC = 0x50535453; - - static final int FLAG_COMPLETE = 1<<0; - static final int FLAG_SHUTDOWN = 1<<1; - static final int FLAG_SYSPROPS = 1<<2; - - final File mBaseDir; - final ProcessTracker mProcessTracker; - AtomicFile mFile; - String mReadError; - - long mTimePeriodStartClock; - String mTimePeriodStartClockStr; - long mTimePeriodStartRealtime; - long mTimePeriodEndRealtime; - String mRuntime; - String mWebView; - boolean mRunning; - int mFlags; - - final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>(); - final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>(); - final long[] mMemFactorDurations = new long[ADJ_COUNT]; - int mMemFactor = STATE_NOTHING; - long mStartTime; - - static final int LONGS_SIZE = 4096; - final ArrayList<long[]> mLongs = new ArrayList<long[]>(); - int mNextLong; - - int[] mAddLongTable; - int mAddLongTableSize; - - final Object mPendingWriteLock = new Object(); - AtomicFile mPendingWriteFile; - Parcel mPendingWrite; - boolean mPendingWriteCommitted; - long mLastWriteTime; - - State(File baseDir, ProcessTracker tracker) { - mBaseDir = baseDir; - reset(); - mProcessTracker = tracker; - } - - State(String file) { - mBaseDir = null; - reset(); - mFile = new AtomicFile(new File(file)); - mProcessTracker = null; - readLocked(); - } - - void reset() { - if (DEBUG && mFile != null) Slog.d(TAG, "Resetting state of " + mFile.getBaseFile()); - resetCommon(); - mPackages.getMap().clear(); - mProcesses.getMap().clear(); - mMemFactor = STATE_NOTHING; - mStartTime = 0; - if (DEBUG && mFile != null) Slog.d(TAG, "State reset; now " + mFile.getBaseFile()); - } - - void resetSafely() { - if (DEBUG && mFile != null) Slog.d(TAG, "Safely resetting state of " + mFile.getBaseFile()); - resetCommon(); - long now = SystemClock.uptimeMillis(); - ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - for (int ip=procMap.size()-1; ip>=0; ip--) { - SparseArray<ProcessState> uids = procMap.valueAt(ip); - for (int iu=uids.size()-1; iu>=0; iu--) { - uids.valueAt(iu).resetSafely(now); - } - } - ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); - for (int ip=pkgMap.size()-1; ip>=0; ip--) { - SparseArray<PackageState> uids = pkgMap.valueAt(ip); - for (int iu=uids.size()-1; iu>=0; iu--) { - PackageState pkgState = uids.valueAt(iu); - for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) { - pkgState.mProcesses.valueAt(iproc).resetSafely(now); - } - for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) { - ServiceState ss = pkgState.mServices.valueAt(isvc); - if (ss.isActive()) { - pkgState.mServices.valueAt(isvc).resetSafely(now); - } else { - pkgState.mServices.removeAt(isvc); - } - } - } - } - mStartTime = SystemClock.uptimeMillis(); - if (DEBUG && mFile != null) Slog.d(TAG, "State reset; now " + mFile.getBaseFile()); - } - - private void resetCommon() { - mLastWriteTime = SystemClock.uptimeMillis(); - mTimePeriodStartClock = System.currentTimeMillis(); - buildTimePeriodStartClockStr(); - mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); - mLongs.clear(); - mLongs.add(new long[LONGS_SIZE]); - mNextLong = 0; - Arrays.fill(mMemFactorDurations, 0); - mStartTime = 0; - mReadError = null; - mFlags = 0; - evaluateSystemProperties(true); - } - - public boolean evaluateSystemProperties(boolean update) { - boolean changed = false; - String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib", - VMRuntime.getRuntime().vmLibrary()); - if (!Objects.equals(runtime, mRuntime)) { - changed = true; - if (update) { - mRuntime = runtime; - } - } - String webview = WebViewFactory.useExperimentalWebView() ? "chromeview" : "webview"; - if (!Objects.equals(webview, mWebView)) { - changed = true; - if (update) { - mWebView = webview; - } - } - return changed; - } - - private void buildTimePeriodStartClockStr() { - mTimePeriodStartClockStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss", - mTimePeriodStartClock).toString(); - if (mBaseDir != null) { - mFile = new AtomicFile(new File(mBaseDir, - STATE_FILE_PREFIX + mTimePeriodStartClockStr + STATE_FILE_SUFFIX)); - } - } - - static byte[] readFully(FileInputStream stream) throws java.io.IOException { - int pos = 0; - int avail = stream.available(); - byte[] data = new byte[avail]; - while (true) { - int amt = stream.read(data, pos, data.length-pos); - //Log.i("foo", "Read " + amt + " bytes at " + pos - // + " of avail " + data.length); - if (amt <= 0) { - //Log.i("foo", "**** FINISHED READING: pos=" + pos - // + " len=" + data.length); - return data; - } - pos += amt; - avail = stream.available(); - if (avail > data.length-pos) { - byte[] newData = new byte[pos+avail]; - System.arraycopy(data, 0, newData, 0, pos); - data = newData; - } - } - } - - boolean readLocked() { - try { - FileInputStream stream = mFile.openRead(); - - byte[] raw = readFully(stream); - Parcel in = Parcel.obtain(); - in.unmarshall(raw, 0, raw.length); - in.setDataPosition(0); - stream.close(); - - readFromParcel(in); - if (mReadError != null) { - Slog.w(TAG, "Ignoring existing stats; " + mReadError); - if (DEBUG) { - ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - final int NPROC = procMap.size(); - for (int ip=0; ip<NPROC; ip++) { - Slog.w(TAG, "Process: " + procMap.keyAt(ip)); - SparseArray<ProcessState> uids = procMap.valueAt(ip); - final int NUID = uids.size(); - for (int iu=0; iu<NUID; iu++) { - Slog.w(TAG, " Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu)); - } - } - ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); - final int NPKG = pkgMap.size(); - for (int ip=0; ip<NPKG; ip++) { - Slog.w(TAG, "Package: " + pkgMap.keyAt(ip)); - SparseArray<PackageState> uids = pkgMap.valueAt(ip); - final int NUID = uids.size(); - for (int iu=0; iu<NUID; iu++) { - Slog.w(TAG, " Uid: " + uids.keyAt(iu)); - PackageState pkgState = uids.valueAt(iu); - final int NPROCS = pkgState.mProcesses.size(); - for (int iproc=0; iproc<NPROCS; iproc++) { - Slog.w(TAG, " Process " + pkgState.mProcesses.keyAt(iproc) - + ": " + pkgState.mProcesses.valueAt(iproc)); - } - final int NSRVS = pkgState.mServices.size(); - for (int isvc=0; isvc<NSRVS; isvc++) { - Slog.w(TAG, " Service " + pkgState.mServices.keyAt(isvc) - + ": " + pkgState.mServices.valueAt(isvc)); - } - } - } - } - return false; - } - } catch (Throwable e) { - mReadError = "caught exception: " + e; - Slog.e(TAG, "Error reading process statistics", e); - return false; - } - return true; - } - - static final int[] BAD_TABLE = new int[0]; - - private int[] readTableFromParcel(Parcel in, String name, String what) { - final int size = in.readInt(); - if (size < 0) { - Slog.w(TAG, "Ignoring existing stats; bad " + what + " table size: " + size); - return BAD_TABLE; - } - if (size == 0) { - return null; - } - final int[] table = new int[size]; - for (int i=0; i<size; i++) { - table[i] = in.readInt(); - if (DEBUG) Slog.i(TAG, "Reading in " + name + " table #" + i + ": " - + State.printLongOffset(table[i])); - if (!validateLongOffset(table[i])) { - Slog.w(TAG, "Ignoring existing stats; bad " + what + " table entry: " - + State.printLongOffset(table[i])); - return null; - } - } - return table; - } - - private void writeStateLocked(boolean sync, final boolean commit) { - synchronized (mPendingWriteLock) { - long now = SystemClock.uptimeMillis(); - if (mPendingWrite == null || !mPendingWriteCommitted) { - mPendingWrite = Parcel.obtain(); - mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); - if (commit) { - mFlags |= State.FLAG_COMPLETE; - } - writeToParcel(mPendingWrite); - mPendingWriteFile = new AtomicFile(mFile.getBaseFile()); - mPendingWriteCommitted = commit; - } - if (commit) { - resetSafely(); - } else { - mLastWriteTime = SystemClock.uptimeMillis(); - } - Slog.i(TAG, "Prepared write state in " + (SystemClock.uptimeMillis()-now) + "ms"); - if (!sync) { - BackgroundThread.getHandler().post(new Runnable() { - @Override public void run() { - performWriteState(); - } - }); - return; - } - } - - performWriteState(); - } - - void performWriteState() { - if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile()); - Parcel data; - AtomicFile file; - synchronized (mPendingWriteLock) { - data = mPendingWrite; - file = mPendingWriteFile; - mPendingWriteCommitted = false; - if (data == null) { - return; - } - mPendingWrite = null; - mPendingWriteFile = null; - if (mProcessTracker != null) { - mProcessTracker.mWriteLock.lock(); - } - } - - FileOutputStream stream = null; - try { - stream = file.startWrite(); - stream.write(data.marshall()); - stream.flush(); - file.finishWrite(stream); - if (DEBUG) Slog.d(TAG, "Write completed successfully!"); - } catch (IOException e) { - Slog.w(TAG, "Error writing process statistics", e); - file.failWrite(stream); - } finally { - data.recycle(); - if (mProcessTracker != null) { - mProcessTracker.trimHistoricStatesWriteLocked(); - mProcessTracker.mWriteLock.unlock(); - } - } - - } - - void writeToParcel(Parcel out) { - long now = SystemClock.uptimeMillis(); - out.writeInt(MAGIC); - out.writeInt(PARCEL_VERSION); - out.writeInt(STATE_COUNT); - out.writeInt(ADJ_COUNT); - out.writeInt(PSS_COUNT); - out.writeInt(LONGS_SIZE); - - out.writeLong(mTimePeriodStartClock); - out.writeLong(mTimePeriodStartRealtime); - out.writeLong(mTimePeriodEndRealtime); - out.writeString(mRuntime); - out.writeString(mWebView); - out.writeInt(mFlags); - - out.writeInt(mLongs.size()); - out.writeInt(mNextLong); - for (int i=0; i<(mLongs.size()-1); i++) { - out.writeLongArray(mLongs.get(i)); - } - long[] lastLongs = mLongs.get(mLongs.size()-1); - for (int i=0; i<mNextLong; i++) { - out.writeLong(lastLongs[i]); - if (DEBUG) Slog.d(TAG, "Writing last long #" + i + ": " + lastLongs[i]); - } - - if (mMemFactor != STATE_NOTHING) { - mMemFactorDurations[mMemFactor] += now - mStartTime; - mStartTime = now; - } - out.writeLongArray(mMemFactorDurations); - - ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - final int NPROC = procMap.size(); - out.writeInt(NPROC); - for (int ip=0; ip<NPROC; ip++) { - out.writeString(procMap.keyAt(ip)); - SparseArray<ProcessState> uids = procMap.valueAt(ip); - final int NUID = uids.size(); - out.writeInt(NUID); - for (int iu=0; iu<NUID; iu++) { - out.writeInt(uids.keyAt(iu)); - ProcessState proc = uids.valueAt(iu); - out.writeString(proc.mPackage); - proc.writeToParcel(out, now); - } - } - ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); - final int NPKG = pkgMap.size(); - out.writeInt(NPKG); - for (int ip=0; ip<NPKG; ip++) { - out.writeString(pkgMap.keyAt(ip)); - SparseArray<PackageState> uids = pkgMap.valueAt(ip); - final int NUID = uids.size(); - out.writeInt(NUID); - for (int iu=0; iu<NUID; iu++) { - out.writeInt(uids.keyAt(iu)); - PackageState pkgState = uids.valueAt(iu); - final int NPROCS = pkgState.mProcesses.size(); - out.writeInt(NPROCS); - for (int iproc=0; iproc<NPROCS; iproc++) { - out.writeString(pkgState.mProcesses.keyAt(iproc)); - ProcessState proc = pkgState.mProcesses.valueAt(iproc); - if (proc.mCommonProcess == proc) { - // This is the same as the common process we wrote above. - out.writeInt(0); - } else { - // There is separate data for this package's process. - out.writeInt(1); - proc.writeToParcel(out, now); - } - } - final int NSRVS = pkgState.mServices.size(); - out.writeInt(NSRVS); - for (int isvc=0; isvc<NSRVS; isvc++) { - out.writeString(pkgState.mServices.keyAt(isvc)); - ServiceState svc = pkgState.mServices.valueAt(isvc); - svc.writeToParcel(out, now); - } - } - } - } - - private boolean readCheckedInt(Parcel in, int val, String what) { - int got; - if ((got=in.readInt()) != val) { - mReadError = "bad " + what + ": " + got; - return false; - } - return true; - } - - private void readFromParcel(Parcel in) { - final boolean hadData = mPackages.getMap().size() > 0 - || mProcesses.getMap().size() > 0; - if (hadData) { - resetSafely(); - } - - if (!readCheckedInt(in, MAGIC, "magic number")) { - return; - } - int version = in.readInt(); - if (version != PARCEL_VERSION && version != 6) { - mReadError = "bad version: " + version; - return; - } - if (!readCheckedInt(in, STATE_COUNT, "state count")) { - return; - } - if (!readCheckedInt(in, ADJ_COUNT, "adj count")) { - return; - } - if (!readCheckedInt(in, PSS_COUNT, "pss count")) { - return; - } - if (!readCheckedInt(in, LONGS_SIZE, "longs size")) { - return; - } - - mTimePeriodStartClock = in.readLong(); - buildTimePeriodStartClockStr(); - mTimePeriodStartRealtime = in.readLong(); - mTimePeriodEndRealtime = in.readLong(); - if (version == PARCEL_VERSION) { - mRuntime = in.readString(); - mWebView = in.readString(); - } - mFlags = in.readInt(); - - final int NLONGS = in.readInt(); - final int NEXTLONG = in.readInt(); - mLongs.clear(); - for (int i=0; i<(NLONGS-1); i++) { - while (i >= mLongs.size()) { - mLongs.add(new long[LONGS_SIZE]); - } - in.readLongArray(mLongs.get(i)); - } - long[] longs = new long[LONGS_SIZE]; - mNextLong = NEXTLONG; - for (int i=0; i<NEXTLONG; i++) { - longs[i] = in.readLong(); - if (DEBUG) Slog.d(TAG, "Reading last long #" + i + ": " + longs[i]); - } - mLongs.add(longs); - - in.readLongArray(mMemFactorDurations); - - int NPROC = in.readInt(); - if (NPROC < 0) { - mReadError = "bad process count: " + NPROC; - return; - } - while (NPROC > 0) { - NPROC--; - String procName = in.readString(); - if (procName == null) { - mReadError = "bad process name"; - return; - } - int NUID = in.readInt(); - if (NUID < 0) { - mReadError = "bad uid count: " + NUID; - return; - } - while (NUID > 0) { - NUID--; - int uid = in.readInt(); - if (uid < 0) { - mReadError = "bad uid: " + uid; - return; - } - String pkgName = in.readString(); - if (pkgName == null) { - mReadError = "bad process package name"; - return; - } - ProcessState proc = hadData ? mProcesses.get(procName, uid) : null; - if (proc != null) { - if (!proc.readFromParcel(in, false)) { - return; - } - } else { - proc = new ProcessState(this, pkgName, uid, procName); - if (!proc.readFromParcel(in, true)) { - return; - } - } - if (DEBUG) Slog.d(TAG, "Adding process: " + procName + " " + uid + " " + proc); - mProcesses.put(procName, uid, proc); - } - } - - if (DEBUG) Slog.d(TAG, "Read " + mProcesses.getMap().size() + " processes"); - - int NPKG = in.readInt(); - if (NPKG < 0) { - mReadError = "bad package count: " + NPKG; - return; - } - while (NPKG > 0) { - NPKG--; - String pkgName = in.readString(); - if (pkgName == null) { - mReadError = "bad package name"; - return; - } - int NUID = in.readInt(); - if (NUID < 0) { - mReadError = "bad uid count: " + NUID; - return; - } - while (NUID > 0) { - NUID--; - int uid = in.readInt(); - if (uid < 0) { - mReadError = "bad uid: " + uid; - return; - } - PackageState pkgState = new PackageState(uid); - mPackages.put(pkgName, uid, pkgState); - int NPROCS = in.readInt(); - if (NPROCS < 0) { - mReadError = "bad package process count: " + NPROCS; - return; - } - while (NPROCS > 0) { - NPROCS--; - String procName = in.readString(); - if (procName == null) { - mReadError = "bad package process name"; - return; - } - int hasProc = in.readInt(); - if (DEBUG) Slog.d(TAG, "Reading package " + pkgName + " " + uid - + " process " + procName + " hasProc=" + hasProc); - ProcessState commonProc = mProcesses.get(procName, uid); - if (DEBUG) Slog.d(TAG, "Got common proc " + procName + " " + uid - + ": " + commonProc); - if (commonProc == null) { - mReadError = "no common proc: " + procName; - return; - } - if (hasProc != 0) { - // The process for this package is unique to the package; we - // need to load it. We don't need to do anything about it if - // it is not unique because if someone later looks for it - // they will find and use it from the global procs. - ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null; - if (proc != null) { - if (!proc.readFromParcel(in, false)) { - return; - } - } else { - proc = new ProcessState(commonProc, pkgName, uid, procName, 0); - if (!proc.readFromParcel(in, true)) { - return; - } - } - if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: " - + procName + " " + uid + " " + proc); - pkgState.mProcesses.put(procName, proc); - } else { - if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: " - + procName + " " + uid + " " + commonProc); - pkgState.mProcesses.put(procName, commonProc); - } - } - int NSRVS = in.readInt(); - if (NSRVS < 0) { - mReadError = "bad package service count: " + NSRVS; - return; - } - while (NSRVS > 0) { - NSRVS--; - String serviceName = in.readString(); - if (serviceName == null) { - mReadError = "bad package service name"; - return; - } - ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null; - if (serv == null) { - serv = new ServiceState(this, pkgName, null); - } - if (!serv.readFromParcel(in)) { - return; - } - if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " service: " - + serviceName + " " + uid + " " + serv); - pkgState.mServices.put(serviceName, serv); - } - } - } - - if (DEBUG) Slog.d(TAG, "Successfully read procstats!"); - } - - int addLongData(int index, int type, int num) { - int tableLen = mAddLongTable != null ? mAddLongTable.length : 0; - if (mAddLongTableSize >= tableLen) { - int newSize = ArrayUtils.idealIntArraySize(tableLen + 1); - int[] newTable = new int[newSize]; - if (tableLen > 0) { - System.arraycopy(mAddLongTable, 0, newTable, 0, tableLen); - } - mAddLongTable = newTable; - } - if (mAddLongTableSize > 0 && mAddLongTableSize - index != 0) { - System.arraycopy(mAddLongTable, index, mAddLongTable, index + 1, - mAddLongTableSize - index); - } - int off = allocLongData(num); - mAddLongTable[index] = type | off; - mAddLongTableSize++; - return off; - } - - int allocLongData(int num) { - int whichLongs = mLongs.size()-1; - long[] longs = mLongs.get(whichLongs); - if (mNextLong + num > longs.length) { - longs = new long[LONGS_SIZE]; - mLongs.add(longs); - whichLongs++; - mNextLong = 0; - } - int off = (whichLongs<<OFFSET_ARRAY_SHIFT) | (mNextLong<<OFFSET_INDEX_SHIFT); - mNextLong += num; - return off; - } - - boolean validateLongOffset(int off) { - int arr = (off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK; - if (arr >= mLongs.size()) { - return false; - } - int idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK; - if (idx >= LONGS_SIZE) { - return false; - } - if (DEBUG) Slog.d(TAG, "Validated long " + printLongOffset(off) - + ": " + getLong(off, 0)); - return true; - } - - static String printLongOffset(int off) { - StringBuilder sb = new StringBuilder(16); - sb.append("a"); sb.append((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - sb.append("i"); sb.append((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK); - sb.append("t"); sb.append((off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK); - return sb.toString(); - } - - void setLong(int off, int index, long value) { - long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)] = value; - } - - long getLong(int off, int index) { - long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - return longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)]; - } - - static int binarySearch(int[] array, int size, int value) { - int lo = 0; - int hi = size - 1; - - while (lo <= hi) { - int mid = (lo + hi) >>> 1; - int midVal = (array[mid] >> OFFSET_TYPE_SHIFT) & OFFSET_TYPE_MASK; - - if (midVal < value) { - lo = mid + 1; - } else if (midVal > value) { - hi = mid - 1; - } else { - return mid; // value found - } - } - return ~lo; // value not present - } - - PackageState getPackageStateLocked(String packageName, int uid) { - PackageState as = mPackages.get(packageName, uid); - if (as != null) { - return as; - } - as = new PackageState(uid); - mPackages.put(packageName, uid, as); - return as; - } - - ProcessState getProcessStateLocked(String packageName, int uid, String processName) { - final PackageState pkgState = getPackageStateLocked(packageName, uid); - ProcessState ps = pkgState.mProcesses.get(processName); - if (ps != null) { - return ps; - } - ProcessState commonProc = mProcesses.get(processName, uid); - if (commonProc == null) { - commonProc = new ProcessState(this, packageName, uid, processName); - mProcesses.put(processName, uid, commonProc); - } - if (!commonProc.mMultiPackage) { - if (packageName.equals(commonProc.mPackage)) { - // This common process is not in use by multiple packages, and - // is for the calling package, so we can just use it directly. - ps = commonProc; - } else { - // This common process has not been in use by multiple packages, - // but it was created for a different package than the caller. - // We need to convert it to a multi-package process. - commonProc.mMultiPackage = true; - // The original package it was created for now needs to point - // to its own copy. - long now = SystemClock.uptimeMillis(); - pkgState.mProcesses.put(commonProc.mName, commonProc.clone( - commonProc.mPackage, now)); - ps = new ProcessState(commonProc, packageName, uid, processName, now); - } - } else { - // The common process is for multiple packages, we need to create a - // separate object for the per-package data. - ps = new ProcessState(commonProc, packageName, uid, processName, - SystemClock.uptimeMillis()); - } - pkgState.mProcesses.put(processName, ps); - return ps; - } - - void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpAll) { - long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, - mStartTime, now); - ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); - boolean printedHeader = false; - for (int ip=0; ip<pkgMap.size(); ip++) { - String pkgName = pkgMap.keyAt(ip); - if (reqPackage != null && !reqPackage.equals(pkgName)) { - continue; - } - SparseArray<PackageState> uids = pkgMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - int uid = uids.keyAt(iu); - PackageState pkgState = uids.valueAt(iu); - final int NPROCS = pkgState.mProcesses.size(); - final int NSRVS = pkgState.mServices.size(); - if (NPROCS > 0 || NSRVS > 0) { - if (!printedHeader) { - pw.println("Per-Package Process Stats:"); - printedHeader = true; - } - pw.print(" * "); pw.print(pkgName); pw.print(" / "); - UserHandle.formatUid(pw, uid); pw.println(":"); - } - if (dumpAll) { - for (int iproc=0; iproc<NPROCS; iproc++) { - ProcessState proc = pkgState.mProcesses.valueAt(iproc); - pw.print(" Process "); - pw.print(pkgState.mProcesses.keyAt(iproc)); - pw.print(" ("); - pw.print(proc.mDurationsTableSize); - pw.print(" entries)"); - pw.println(":"); - dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES, now); - dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES); - if (dumpAll) { - pw.print(" mNumStartedServices="); - pw.println(proc.mNumStartedServices); - } - } - } else { - ArrayList<ProcessState> procs = new ArrayList<ProcessState>(); - for (int iproc=0; iproc<NPROCS; iproc++) { - procs.add(pkgState.mProcesses.valueAt(iproc)); - } - dumpProcessSummaryLocked(pw, " ", procs, ALL_SCREEN_ADJ, ALL_MEM_ADJ, - NON_CACHED_PROC_STATES, now, totalTime); - } - for (int isvc=0; isvc<NSRVS; isvc++) { - if (dumpAll) { - pw.print(" Service "); - } else { - pw.print(" * "); - } - pw.print(pkgState.mServices.keyAt(isvc)); - pw.println(":"); - ServiceState svc = pkgState.mServices.valueAt(isvc); - dumpServiceStats(pw, " ", " ", " ", "Started", svc, - svc.mStartedCount, ServiceState.SERVICE_STARTED, svc.mStartedState, - svc.mStartedStartTime, now, totalTime, dumpAll); - dumpServiceStats(pw, " ", " ", " ", "Bound", svc, - svc.mBoundCount, ServiceState.SERVICE_BOUND, svc.mBoundState, - svc.mBoundStartTime, now, totalTime, dumpAll); - dumpServiceStats(pw, " ", " ", " ", "Executing", svc, - svc.mExecCount, ServiceState.SERVICE_EXEC, svc.mExecState, - svc.mExecStartTime, now, totalTime, dumpAll); - } - } - } - - if (reqPackage == null) { - ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - printedHeader = false; - for (int ip=0; ip<procMap.size(); ip++) { - String procName = procMap.keyAt(ip); - SparseArray<ProcessState> uids = procMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - int uid = uids.keyAt(iu); - ProcessState proc = uids.valueAt(iu); - if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING - && proc.mPssTableSize == 0) { - continue; - } - if (!printedHeader) { - pw.println("Process Stats:"); - printedHeader = true; - } - pw.print(" * "); pw.print(procName); pw.print(" / "); - UserHandle.formatUid(pw, uid); - pw.print(" ("); pw.print(proc.mDurationsTableSize); - pw.print(" entries)"); pw.println(":"); - dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES, now); - dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES); - } - } - - pw.println(); - pw.println("Summary:"); - dumpSummaryLocked(pw, reqPackage, now); - } else { - pw.println(); - dumpTotalsLocked(pw, now); - } - - if (dumpAll) { - pw.println(); - pw.println("Internal state:"); - pw.print(" mFile="); pw.println(mFile.getBaseFile()); - pw.print(" Num long arrays: "); pw.println(mLongs.size()); - pw.print(" Next long entry: "); pw.println(mNextLong); - pw.print(" mRunning="); pw.println(mRunning); - } - } - - static long dumpSingleServiceTime(PrintWriter pw, String prefix, ServiceState service, - int serviceType, int curState, long curStartTime, long now) { - long totalTime = 0; - int printedScreen = -1; - for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { - int printedMem = -1; - for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { - int state = imem+iscreen; - long time = service.getDuration(serviceType, curState, curStartTime, - state, now); - String running = ""; - if (curState == state) { - time += now - curStartTime; - if (pw != null) { - running = " (running)"; - } - } - if (time != 0) { - if (pw != null) { - pw.print(prefix); - printScreenLabel(pw, printedScreen != iscreen - ? iscreen : STATE_NOTHING); - printedScreen = iscreen; - printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); - printedMem = imem; - TimeUtils.formatDuration(time, pw); pw.println(running); - } - totalTime += time; - } - } - } - if (totalTime != 0 && pw != null) { - pw.print(prefix); - printScreenLabel(pw, STATE_NOTHING); - pw.print("TOTAL: "); - TimeUtils.formatDuration(totalTime, pw); - pw.println(); - } - return totalTime; - } - - void dumpServiceStats(PrintWriter pw, String prefix, String prefixInner, - String headerPrefix, String header, ServiceState service, - int count, int serviceType, int state, long startTime, long now, long totalTime, - boolean dumpAll) { - if (count != 0) { - if (dumpAll) { - pw.print(prefix); pw.print(header); - pw.print(" op count "); pw.print(count); pw.println(":"); - dumpSingleServiceTime(pw, prefixInner, service, serviceType, state, startTime, - now); - } else { - long myTime = dumpSingleServiceTime(null, null, service, serviceType, state, - startTime, now); - pw.print(prefix); pw.print(headerPrefix); pw.print(header); - pw.print(" count "); pw.print(count); - pw.print(" / time "); - printPercent(pw, (double)myTime/(double)totalTime); - pw.println(); - } - } - } - - void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now) { - long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, - mStartTime, now); - dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, - NON_CACHED_PROC_STATES, now, totalTime, reqPackage); - pw.println(); - dumpTotalsLocked(pw, now); - } - - void dumpTotalsLocked(PrintWriter pw, long now) { - pw.println("Run time Stats:"); - dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now); - pw.println(); - pw.print(" Start time: "); - pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock)); - pw.println(); - pw.print(" Total elapsed time: "); - TimeUtils.formatDuration( - (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime) - - mTimePeriodStartRealtime, pw); - boolean partial = true; - if ((mFlags&FLAG_SHUTDOWN) != 0) { - pw.print(" (shutdown)"); - partial = false; - } - if ((mFlags&FLAG_SYSPROPS) != 0) { - pw.print(" (sysprops)"); - partial = false; - } - if ((mFlags&FLAG_COMPLETE) != 0) { - pw.print(" (complete)"); - partial = false; - } - if (partial) { - pw.print(" (partial)"); - } - pw.print(' '); - pw.print(mRuntime); - pw.print(' '); - pw.print(mWebView); - pw.println(); - } - - void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix, - int[] screenStates, int[] memStates, int[] procStates, long now, long totalTime, - String reqPackage) { - ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates, - procStates, now, reqPackage); - if (procs.size() > 0) { - if (header != null) { - pw.println(); - pw.println(header); - } - dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates, procStates, - now, totalTime); - } - } - - ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates, - int[] procStates, long now, String reqPackage) { - ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>(); - ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); - for (int ip=0; ip<pkgMap.size(); ip++) { - if (reqPackage != null && !reqPackage.equals(pkgMap.keyAt(ip))) { - continue; - } - SparseArray<PackageState> procs = pkgMap.valueAt(ip); - for (int iu=0; iu<procs.size(); iu++) { - PackageState state = procs.valueAt(iu); - for (int iproc=0; iproc<state.mProcesses.size(); iproc++) { - ProcessState proc = state.mProcesses.valueAt(iproc); - foundProcs.add(proc.mCommonProcess); - } - } - } - ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>(foundProcs.size()); - for (int i=0; i<foundProcs.size(); i++) { - ProcessState proc = foundProcs.valueAt(i); - if (computeProcessTimeLocked(proc, screenStates, memStates, - procStates, now) > 0) { - outProcs.add(proc); - } - } - Collections.sort(outProcs, new Comparator<ProcessState>() { - @Override - public int compare(ProcessState lhs, ProcessState rhs) { - if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) { - return -1; - } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) { - return 1; - } - return 0; - } - }); - return outProcs; - } - - String collapseString(String pkgName, String itemName) { - if (itemName.startsWith(pkgName)) { - final int ITEMLEN = itemName.length(); - final int PKGLEN = pkgName.length(); - if (ITEMLEN == PKGLEN) { - return ""; - } else if (ITEMLEN >= PKGLEN) { - if (itemName.charAt(PKGLEN) == '.') { - return itemName.substring(PKGLEN); - } - } - } - return itemName; - } - - void dumpCheckinLocked(PrintWriter pw, String reqPackage) { - final long now = SystemClock.uptimeMillis(); - ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); - pw.println("vers,3"); - pw.print("period,"); pw.print(mTimePeriodStartClockStr); - pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(","); - pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime); - boolean partial = true; - if ((mFlags&FLAG_SHUTDOWN) != 0) { - pw.print(",shutdown"); - partial = false; - } - if ((mFlags&FLAG_SYSPROPS) != 0) { - pw.print(",sysprops"); - partial = false; - } - if ((mFlags&FLAG_COMPLETE) != 0) { - pw.print(",complete"); - partial = false; - } - if (partial) { - pw.print(",partial"); - } - pw.println(); - pw.print("config,"); pw.print(mRuntime); pw.print(','); pw.println(mWebView); - for (int ip=0; ip<pkgMap.size(); ip++) { - String pkgName = pkgMap.keyAt(ip); - if (reqPackage != null && !reqPackage.equals(pkgName)) { - continue; - } - SparseArray<PackageState> uids = pkgMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - int uid = uids.keyAt(iu); - PackageState pkgState = uids.valueAt(iu); - final int NPROCS = pkgState.mProcesses.size(); - final int NSRVS = pkgState.mServices.size(); - for (int iproc=0; iproc<NPROCS; iproc++) { - ProcessState proc = pkgState.mProcesses.valueAt(iproc); - pw.print("pkgproc,"); - pw.print(pkgName); - pw.print(","); - pw.print(uid); - pw.print(","); - pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc))); - dumpAllProcessStateCheckin(pw, proc, now); - pw.println(); - if (proc.mPssTableSize > 0) { - pw.print("pkgpss,"); - pw.print(pkgName); - pw.print(","); - pw.print(uid); - pw.print(","); - pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc))); - dumpAllProcessPssCheckin(pw, proc); - pw.println(); - } - if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0) { - pw.print("pkgkills,"); - pw.print(pkgName); - pw.print(","); - pw.print(uid); - pw.print(","); - pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc))); - pw.print(","); - pw.print(proc.mNumExcessiveWake); - pw.print(","); - pw.print(proc.mNumExcessiveCpu); - pw.println(); - } - } - for (int isvc=0; isvc<NSRVS; isvc++) { - String serviceName = collapseString(pkgName, - pkgState.mServices.keyAt(isvc)); - ServiceState svc = pkgState.mServices.valueAt(isvc); - dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, serviceName, - svc, ServiceState.SERVICE_STARTED, svc.mStartedCount, - svc.mStartedState, svc.mStartedStartTime, now); - dumpServiceTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, serviceName, - svc, ServiceState.SERVICE_BOUND, svc.mBoundCount, - svc.mBoundState, svc.mBoundStartTime, now); - dumpServiceTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, serviceName, - svc, ServiceState.SERVICE_EXEC, svc.mExecCount, - svc.mExecState, svc.mExecStartTime, now); - } - } - } - - ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - for (int ip=0; ip<procMap.size(); ip++) { - String procName = procMap.keyAt(ip); - SparseArray<ProcessState> uids = procMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - int uid = uids.keyAt(iu); - ProcessState procState = uids.valueAt(iu); - if (procState.mDurationsTableSize > 0) { - pw.print("proc,"); - pw.print(procName); - pw.print(","); - pw.print(uid); - dumpAllProcessStateCheckin(pw, procState, now); - pw.println(); - } - if (procState.mPssTableSize > 0) { - pw.print("pss,"); - pw.print(procName); - pw.print(","); - pw.print(uid); - dumpAllProcessPssCheckin(pw, procState); - pw.println(); - } - if (procState.mNumExcessiveWake > 0 || procState.mNumExcessiveCpu > 0) { - pw.print("kills,"); - pw.print(procName); - pw.print(","); - pw.print(uid); - pw.print(","); - pw.print(procState.mNumExcessiveWake); - pw.print(","); - pw.print(procState.mNumExcessiveCpu); - pw.println(); - } - } - } - pw.print("total"); - dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor, - mStartTime, now); - pw.println(); - } - } - - public ProcessTracker(Object lock, File file) { - mLock = lock; - mBaseDir = file; - mBaseDir.mkdirs(); - mState = new State(mBaseDir, this); - mState.mRunning = true; - SystemProperties.addChangeCallback(new Runnable() { - @Override public void run() { - synchronized (mLock) { - if (mState.evaluateSystemProperties(false)) { - mState.mFlags |= State.FLAG_SYSPROPS; - mState.writeStateLocked(true, true); - mState.evaluateSystemProperties(true); - } - } - } - }); - } - - public ProcessState getProcessStateLocked(String packageName, int uid, String processName) { - return mState.getProcessStateLocked(packageName, uid, processName); - } - - public ServiceState getServiceStateLocked(String packageName, int uid, - String processName, String className) { - final PackageState as = mState.getPackageStateLocked(packageName, uid); - ServiceState ss = as.mServices.get(className); - if (ss != null) { - ss.makeActive(); - return ss; - } - final ProcessState ps = mState.getProcessStateLocked(packageName, uid, processName); - ss = new ServiceState(mState, packageName, ps); - as.mServices.put(className, ss); - return ss; - } - - public boolean isMemFactorLowered() { - return mMemFactorLowered; - } - - public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) { - mMemFactorLowered = memFactor < mLastMemOnlyState; - mLastMemOnlyState = memFactor; - if (screenOn) { - memFactor += ADJ_SCREEN_ON; - } - if (memFactor != mState.mMemFactor) { - if (mState.mMemFactor != STATE_NOTHING) { - mState.mMemFactorDurations[mState.mMemFactor] += now - mState.mStartTime; - } - mState.mMemFactor = memFactor; - mState.mStartTime = now; - ArrayMap<String, SparseArray<PackageState>> pmap = mState.mPackages.getMap(); - for (int i=0; i<pmap.size(); i++) { - SparseArray<PackageState> uids = pmap.valueAt(i); - for (int j=0; j<uids.size(); j++) { - PackageState pkg = uids.valueAt(j); - ArrayMap<String, ServiceState> services = pkg.mServices; - for (int k=0; k<services.size(); k++) { - ServiceState service = services.valueAt(k); - if (service.isActive()) { - if (service.mStartedState != STATE_NOTHING) { - service.setStarted(true, memFactor, now); - } - if (service.mBoundState != STATE_NOTHING) { - service.setBound(true, memFactor, now); - } - if (service.mExecState != STATE_NOTHING) { - service.setExecuting(true, memFactor, now); - } - } - } - } - } - return true; - } - return false; - } - - public int getMemFactorLocked() { - return mState.mMemFactor != STATE_NOTHING ? mState.mMemFactor : 0; - } - - public void readLocked() { - mState.readLocked(); - } - - public boolean shouldWriteNowLocked(long now) { - if (now > (mState.mLastWriteTime+WRITE_PERIOD)) { - if (SystemClock.elapsedRealtime() > (mState.mTimePeriodStartRealtime+COMMIT_PERIOD)) { - mCommitPending = true; - } - return true; - } - return false; - } - - public void shutdownLocked() { - Slog.w(TAG, "Writing process stats before shutdown..."); - mState.mFlags |= State.FLAG_SHUTDOWN; - writeStateSyncLocked(); - mShuttingDown = true; - } - - public void writeStateAsyncLocked() { - writeStateLocked(false); - } - - public void writeStateSyncLocked() { - writeStateLocked(true); - } - - private void writeStateLocked(boolean sync) { - if (mShuttingDown) { - return; - } - boolean commitPending = mCommitPending; - mCommitPending = false; - mState.writeStateLocked(sync, commitPending); - } - - private ArrayList<String> getCommittedFiles(int minNum, boolean inclAll) { - File[] files = mBaseDir.listFiles(); - if (files == null || files.length <= minNum) { - return null; - } - ArrayList<String> filesArray = new ArrayList<String>(files.length); - String currentFile = mState.mFile.getBaseFile().getPath(); - if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile); - for (int i=0; i<files.length; i++) { - File file = files[i]; - String fileStr = file.getPath(); - if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr); - if (!inclAll && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) { - if (DEBUG) Slog.d(TAG, "Skipping: already checked in"); - continue; - } - if (fileStr.equals(currentFile)) { - if (DEBUG) Slog.d(TAG, "Skipping: current stats"); - continue; - } - filesArray.add(fileStr); - } - Collections.sort(filesArray); - return filesArray; - } - - public void trimHistoricStatesWriteLocked() { - ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, true); - if (filesArray == null) { - return; - } - while (filesArray.size() > MAX_HISTORIC_STATES) { - String file = filesArray.remove(0); - Slog.i(TAG, "Pruning old procstats: " + file); - (new File(file)).delete(); - } - } - - static private void printScreenLabel(PrintWriter pw, int offset) { - switch (offset) { - case ADJ_NOTHING: - pw.print(" "); - break; - case ADJ_SCREEN_OFF: - pw.print("Screen Off / "); - break; - case ADJ_SCREEN_ON: - pw.print("Screen On / "); - break; - default: - pw.print("?????????? / "); - break; - } - } - - static private void printScreenLabelCsv(PrintWriter pw, int offset) { - switch (offset) { - case ADJ_NOTHING: - break; - case ADJ_SCREEN_OFF: - pw.print(ADJ_SCREEN_NAMES_CSV[0]); - break; - case ADJ_SCREEN_ON: - pw.print(ADJ_SCREEN_NAMES_CSV[1]); - break; - default: - pw.print("???"); - break; - } - } - - static private void printMemLabel(PrintWriter pw, int offset) { - switch (offset) { - case ADJ_NOTHING: - pw.print(" "); - break; - case ADJ_MEM_FACTOR_NORMAL: - pw.print("Norm / "); - break; - case ADJ_MEM_FACTOR_MODERATE: - pw.print("Mod / "); - break; - case ADJ_MEM_FACTOR_LOW: - pw.print("Low / "); - break; - case ADJ_MEM_FACTOR_CRITICAL: - pw.print("Crit / "); - break; - default: - pw.print("???? / "); - break; - } - } - - static private void printMemLabelCsv(PrintWriter pw, int offset) { - if (offset >= ADJ_MEM_FACTOR_NORMAL) { - if (offset <= ADJ_MEM_FACTOR_CRITICAL) { - pw.print(ADJ_MEM_NAMES_CSV[offset]); - } else { - pw.print("???"); - } - } - } - - static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations, - int curState, long curStartTime, long now) { - long totalTime = 0; - int printedScreen = -1; - for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { - int printedMem = -1; - for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { - int state = imem+iscreen; - long time = durations[state]; - String running = ""; - if (curState == state) { - time += now - curStartTime; - if (pw != null) { - running = " (running)"; - } - } - if (time != 0) { - if (pw != null) { - pw.print(prefix); - printScreenLabel(pw, printedScreen != iscreen - ? iscreen : STATE_NOTHING); - printedScreen = iscreen; - printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); - printedMem = imem; - TimeUtils.formatDuration(time, pw); pw.println(running); - } - totalTime += time; - } - } - } - if (totalTime != 0 && pw != null) { - pw.print(prefix); - printScreenLabel(pw, STATE_NOTHING); - pw.print("TOTAL: "); - TimeUtils.formatDuration(totalTime, pw); - pw.println(); - } - return totalTime; - } - - static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations, - int curState, long curStartTime, long now) { - for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { - for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { - int state = imem+iscreen; - long time = durations[state]; - if (curState == state) { - time += now - curStartTime; - } - if (time != 0) { - printAdjTagAndValue(pw, state, time); - } - } - } - } - - static void dumpServiceTimeCheckin(PrintWriter pw, String label, String packageName, - int uid, String serviceName, ServiceState svc, int serviceType, int opCount, - int curState, long curStartTime, long now) { - if (opCount <= 0) { - return; - } - pw.print(label); - pw.print(","); - pw.print(packageName); - pw.print(","); - pw.print(uid); - pw.print(","); - pw.print(serviceName); - pw.print(","); - pw.print(opCount); - boolean didCurState = false; - for (int i=0; i<svc.mDurationsTableSize; i++) { - int off = svc.mDurationsTable[i]; - int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - int memFactor = type / ServiceState.SERVICE_COUNT; - type %= ServiceState.SERVICE_COUNT; - if (type != serviceType) { - continue; - } - long time = svc.mState.getLong(off, 0); - if (curState == memFactor) { - didCurState = true; - time += now - curStartTime; - } - printAdjTagAndValue(pw, memFactor, time); - } - if (!didCurState && curState != STATE_NOTHING) { - printAdjTagAndValue(pw, curState, now - curStartTime); - } - pw.println(); - } - - static final class ProcessDataCollection { - final int[] screenStates; - final int[] memStates; - final int[] procStates; - - long totalTime; - long numPss; - long minPss; - long avgPss; - long maxPss; - long minUss; - long avgUss; - long maxUss; - - ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) { - screenStates = _screenStates; - memStates = _memStates; - procStates = _procStates; - } - - void print(PrintWriter pw, long overallTime, boolean full) { - printPercent(pw, (double) totalTime / (double) overallTime); - if (numPss > 0) { - pw.print(" ("); - printSizeValue(pw, minPss * 1024); - pw.print("-"); - printSizeValue(pw, avgPss * 1024); - pw.print("-"); - printSizeValue(pw, maxPss * 1024); - pw.print("/"); - printSizeValue(pw, minUss * 1024); - pw.print("-"); - printSizeValue(pw, avgUss * 1024); - pw.print("-"); - printSizeValue(pw, maxUss * 1024); - if (full) { - pw.print(" over "); - pw.print(numPss); - } - pw.print(")"); - } - } - } - - static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) { - data.totalTime = 0; - data.numPss = data.minPss = data.avgPss = data.maxPss = - data.minUss = data.avgUss = data.maxUss = 0; - for (int is=0; is<data.screenStates.length; is++) { - for (int im=0; im<data.memStates.length; im++) { - for (int ip=0; ip<data.procStates.length; ip++) { - int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT) - + data.procStates[ip]; - data.totalTime += proc.getDuration(bucket, now); - long samples = proc.getPssSampleCount(bucket); - if (samples > 0) { - long minPss = proc.getPssMinimum(bucket); - long avgPss = proc.getPssAverage(bucket); - long maxPss = proc.getPssMaximum(bucket); - long minUss = proc.getPssUssMinimum(bucket); - long avgUss = proc.getPssUssAverage(bucket); - long maxUss = proc.getPssUssMaximum(bucket); - if (data.numPss == 0) { - data.minPss = minPss; - data.avgPss = avgPss; - data.maxPss = maxPss; - data.minUss = minUss; - data.avgUss = avgUss; - data.maxUss = maxUss; - } else { - if (minPss < data.minPss) { - data.minPss = minPss; - } - data.avgPss = (long)( ((data.avgPss*(double)data.numPss) - + (avgPss*(double)samples)) / (data.numPss+samples) ); - if (maxPss > data.maxPss) { - data.maxPss = maxPss; - } - if (minUss < data.minUss) { - data.minUss = minUss; - } - data.avgUss = (long)( ((data.avgUss*(double)data.numPss) - + (avgUss*(double)samples)) / (data.numPss+samples) ); - if (maxUss > data.maxUss) { - data.maxUss = maxUss; - } - } - data.numPss += samples; - } - } - } - } - } - - static long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates, - int[] procStates, long now) { - long totalTime = 0; - /* - for (int i=0; i<proc.mDurationsTableSize; i++) { - int val = proc.mDurationsTable[i]; - totalTime += proc.mState.getLong(val, 0); - if ((val&0xff) == proc.mCurState) { - totalTime += now - proc.mStartTime; - } - } - */ - for (int is=0; is<screenStates.length; is++) { - for (int im=0; im<memStates.length; im++) { - for (int ip=0; ip<procStates.length; ip++) { - int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT) - + procStates[ip]; - totalTime += proc.getDuration(bucket, now); - } - } - } - proc.mTmpTotalTime = totalTime; - return totalTime; - } - - static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc, - int[] screenStates, int[] memStates, int[] procStates, long now) { - long totalTime = 0; - int printedScreen = -1; - for (int is=0; is<screenStates.length; is++) { - int printedMem = -1; - for (int im=0; im<memStates.length; im++) { - for (int ip=0; ip<procStates.length; ip++) { - final int iscreen = screenStates[is]; - final int imem = memStates[im]; - final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip]; - long time = proc.getDuration(bucket, now); - String running = ""; - if (proc.mCurState == bucket) { - running = " (running)"; - } - if (time != 0) { - pw.print(prefix); - if (screenStates.length > 1) { - printScreenLabel(pw, printedScreen != iscreen - ? iscreen : STATE_NOTHING); - printedScreen = iscreen; - } - if (memStates.length > 1) { - printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); - printedMem = imem; - } - pw.print(STATE_NAMES[procStates[ip]]); pw.print(": "); - TimeUtils.formatDuration(time, pw); pw.println(running); - totalTime += time; - } - } - } - } - if (totalTime != 0) { - pw.print(prefix); - if (screenStates.length > 1) { - printScreenLabel(pw, STATE_NOTHING); - } - if (memStates.length > 1) { - printMemLabel(pw, STATE_NOTHING); - } - pw.print("TOTAL : "); - TimeUtils.formatDuration(totalTime, pw); - pw.println(); - } - } - - static void dumpProcessPss(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates, - int[] memStates, int[] procStates) { - boolean printedHeader = false; - int printedScreen = -1; - for (int is=0; is<screenStates.length; is++) { - int printedMem = -1; - for (int im=0; im<memStates.length; im++) { - for (int ip=0; ip<procStates.length; ip++) { - final int iscreen = screenStates[is]; - final int imem = memStates[im]; - final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip]; - long count = proc.getPssSampleCount(bucket); - if (count > 0) { - if (!printedHeader) { - pw.print(prefix); - pw.print("PSS/USS ("); - pw.print(proc.mPssTableSize); - pw.println(" entries):"); - printedHeader = true; - } - pw.print(prefix); - pw.print(" "); - if (screenStates.length > 1) { - printScreenLabel(pw, printedScreen != iscreen - ? iscreen : STATE_NOTHING); - printedScreen = iscreen; - } - if (memStates.length > 1) { - printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); - printedMem = imem; - } - pw.print(STATE_NAMES[procStates[ip]]); pw.print(": "); - pw.print(count); - pw.print(" samples "); - printSizeValue(pw, proc.getPssMinimum(bucket) * 1024); - pw.print(" "); - printSizeValue(pw, proc.getPssAverage(bucket) * 1024); - pw.print(" "); - printSizeValue(pw, proc.getPssMaximum(bucket) * 1024); - pw.print(" / "); - printSizeValue(pw, proc.getPssUssMinimum(bucket) * 1024); - pw.print(" "); - printSizeValue(pw, proc.getPssUssAverage(bucket) * 1024); - pw.print(" "); - printSizeValue(pw, proc.getPssUssMaximum(bucket) * 1024); - pw.println(); - } - } - } - } - if (proc.mNumExcessiveWake != 0) { - pw.print(prefix); pw.print("Killed for excessive wake locks: "); - pw.print(proc.mNumExcessiveWake); pw.println(" times"); - } - if (proc.mNumExcessiveCpu != 0) { - pw.print(prefix); pw.print("Killed for excessive CPU use: "); - pw.print(proc.mNumExcessiveCpu); pw.println(" times"); - } - } - - static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates, - int[] memStates, int[] procStates) { - final int NS = screenStates != null ? screenStates.length : 1; - final int NM = memStates != null ? memStates.length : 1; - final int NP = procStates != null ? procStates.length : 1; - for (int is=0; is<NS; is++) { - for (int im=0; im<NM; im++) { - for (int ip=0; ip<NP; ip++) { - pw.print(sep); - boolean printed = false; - if (screenStates != null && screenStates.length > 1) { - printScreenLabelCsv(pw, screenStates[is]); - printed = true; - } - if (memStates != null && memStates.length > 1) { - if (printed) { - pw.print("-"); - } - printMemLabelCsv(pw, memStates[im]); - printed = true; - } - if (procStates != null && procStates.length > 1) { - if (printed) { - pw.print("-"); - } - pw.print(STATE_NAMES_CSV[procStates[ip]]); - } - } - } - } - } - - static void dumpProcessStateCsv(PrintWriter pw, ProcessState proc, - boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, - boolean sepProcStates, int[] procStates, long now) { - final int NSS = sepScreenStates ? screenStates.length : 1; - final int NMS = sepMemStates ? memStates.length : 1; - final int NPS = sepProcStates ? procStates.length : 1; - for (int iss=0; iss<NSS; iss++) { - for (int ims=0; ims<NMS; ims++) { - for (int ips=0; ips<NPS; ips++) { - final int vsscreen = sepScreenStates ? screenStates[iss] : 0; - final int vsmem = sepMemStates ? memStates[ims] : 0; - final int vsproc = sepProcStates ? procStates[ips] : 0; - final int NSA = sepScreenStates ? 1 : screenStates.length; - final int NMA = sepMemStates ? 1 : memStates.length; - final int NPA = sepProcStates ? 1 : procStates.length; - long totalTime = 0; - for (int isa=0; isa<NSA; isa++) { - for (int ima=0; ima<NMA; ima++) { - for (int ipa=0; ipa<NPA; ipa++) { - final int vascreen = sepScreenStates ? 0 : screenStates[isa]; - final int vamem = sepMemStates ? 0 : memStates[ima]; - final int vaproc = sepProcStates ? 0 : procStates[ipa]; - final int bucket = ((vsscreen + vascreen + vsmem + vamem) - * STATE_COUNT) + vsproc + vaproc; - totalTime += proc.getDuration(bucket, now); - } - } - } - pw.print(CSV_SEP); - pw.print(totalTime); - } - } - } - } - - static void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs, - int[] screenStates, int[] memStates, int[] procStates, long now) { - String innerPrefix = prefix + " "; - for (int i=procs.size()-1; i>=0; i--) { - ProcessState proc = procs.get(i); - pw.print(prefix); - pw.print(proc.mName); - pw.print(" / "); - UserHandle.formatUid(pw, proc.mUid); - pw.print(" ("); - pw.print(proc.mDurationsTableSize); - pw.print(" entries)"); - pw.println(":"); - dumpProcessState(pw, innerPrefix, proc, screenStates, memStates, procStates, now); - if (proc.mPssTableSize > 0) { - dumpProcessPss(pw, innerPrefix, proc, screenStates, memStates, procStates); - } - } - } - - static void dumpProcessSummaryDetails(PrintWriter pw, ProcessState proc, String prefix, - String label, int[] screenStates, int[] memStates, int[] procStates, - long now, long totalTime, boolean full) { - ProcessDataCollection totals = new ProcessDataCollection(screenStates, - memStates, procStates); - computeProcessData(proc, totals, now); - if (totals.totalTime != 0 || totals.numPss != 0) { - if (prefix != null) { - pw.print(prefix); - } - if (label != null) { - pw.print(label); - } - totals.print(pw, totalTime, full); - if (prefix != null) { - pw.println(); - } - } - } - - static void dumpProcessSummaryLocked(PrintWriter pw, String prefix, - ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates, - long now, long totalTime) { - for (int i=procs.size()-1; i>=0; i--) { - ProcessState proc = procs.get(i); - pw.print(prefix); - pw.print("* "); - pw.print(proc.mName); - pw.print(" / "); - UserHandle.formatUid(pw, proc.mUid); - pw.println(":"); - dumpProcessSummaryDetails(pw, proc, prefix, " TOTAL: ", screenStates, memStates, - procStates, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Persistent: ", screenStates, memStates, - new int[] { STATE_PERSISTENT }, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Top: ", screenStates, memStates, - new int[] {STATE_TOP}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Imp Fg: ", screenStates, memStates, - new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Imp Bg: ", screenStates, memStates, - new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Backup: ", screenStates, memStates, - new int[] {STATE_BACKUP}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Heavy Wgt: ", screenStates, memStates, - new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Service: ", screenStates, memStates, - new int[] {STATE_SERVICE}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Service Rs: ", screenStates, memStates, - new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Receiver: ", screenStates, memStates, - new int[] {STATE_RECEIVER}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Home: ", screenStates, memStates, - new int[] {STATE_HOME}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " (Last Act): ", screenStates, memStates, - new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " (Cached): ", screenStates, memStates, - new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT, - STATE_CACHED_EMPTY}, now, totalTime, true); - } - } - - static void printPercent(PrintWriter pw, double fraction) { - fraction *= 100; - if (fraction < 1) { - pw.print(String.format("%.2f", fraction)); - } else if (fraction < 10) { - pw.print(String.format("%.1f", fraction)); - } else { - pw.print(String.format("%.0f", fraction)); - } - pw.print("%"); - } - - static void printSizeValue(PrintWriter pw, long number) { - float result = number; - String suffix = ""; - if (result > 900) { - suffix = "KB"; - result = result / 1024; - } - if (result > 900) { - suffix = "MB"; - result = result / 1024; - } - if (result > 900) { - suffix = "GB"; - result = result / 1024; - } - if (result > 900) { - suffix = "TB"; - result = result / 1024; - } - if (result > 900) { - suffix = "PB"; - result = result / 1024; - } - String value; - if (result < 1) { - value = String.format("%.2f", result); - } else if (result < 10) { - value = String.format("%.1f", result); - } else if (result < 100) { - value = String.format("%.0f", result); - } else { - value = String.format("%.0f", result); - } - pw.print(value); - pw.print(suffix); - } - - static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs, - boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, - boolean sepProcStates, int[] procStates, long now) { - pw.print("process"); - pw.print(CSV_SEP); - pw.print("uid"); - dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null, - sepMemStates ? memStates : null, - sepProcStates ? procStates : null); - pw.println(); - for (int i=procs.size()-1; i>=0; i--) { - ProcessState proc = procs.get(i); - pw.print(proc.mName); - pw.print(CSV_SEP); - UserHandle.formatUid(pw, proc.mUid); - dumpProcessStateCsv(pw, proc, sepScreenStates, screenStates, - sepMemStates, memStates, sepProcStates, procStates, now); - pw.println(); - } - } - - boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header, - boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, - boolean sepProcStates, int[] procStates, long now, String reqPackage) { - ArrayList<ProcessState> procs = mState.collectProcessesLocked(screenStates, memStates, - procStates, now, reqPackage); - if (procs.size() > 0) { - if (header != null) { - pw.println(header); - } - dumpProcessListCsv(pw, procs, sepScreenStates, screenStates, - sepMemStates, memStates, sepProcStates, procStates, now); - return true; - } - return false; - } - - static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) { - int index = value/mod; - if (index >= 0 && index < array.length) { - pw.print(array[index]); - } else { - pw.print('?'); - } - return value - index*mod; - } - - static void printProcStateTag(PrintWriter pw, int state) { - state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD*STATE_COUNT); - state = printArrayEntry(pw, ADJ_MEM_TAGS, state, STATE_COUNT); - printArrayEntry(pw, STATE_TAGS, state, 1); - } - - static void printAdjTag(PrintWriter pw, int state) { - state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD); - printArrayEntry(pw, ADJ_MEM_TAGS, state, 1); - } - - static void printProcStateTagAndValue(PrintWriter pw, int state, long value) { - pw.print(','); - printProcStateTag(pw, state); - pw.print(':'); - pw.print(value); - } - - static void printAdjTagAndValue(PrintWriter pw, int state, long value) { - pw.print(','); - printAdjTag(pw, state); - pw.print(':'); - pw.print(value); - } - - static void dumpAllProcessStateCheckin(PrintWriter pw, ProcessState proc, long now) { - boolean didCurState = false; - for (int i=0; i<proc.mDurationsTableSize; i++) { - int off = proc.mDurationsTable[i]; - int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - long time = proc.mState.getLong(off, 0); - if (proc.mCurState == type) { - didCurState = true; - time += now - proc.mStartTime; - } - printProcStateTagAndValue(pw, type, time); - } - if (!didCurState && proc.mCurState != STATE_NOTHING) { - printProcStateTagAndValue(pw, proc.mCurState, now - proc.mStartTime); - } - } - - static void dumpAllProcessPssCheckin(PrintWriter pw, ProcessState proc) { - for (int i=0; i<proc.mPssTableSize; i++) { - int off = proc.mPssTable[i]; - int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - long count = proc.mState.getLong(off, PSS_SAMPLE_COUNT); - long min = proc.mState.getLong(off, PSS_MINIMUM); - long avg = proc.mState.getLong(off, PSS_AVERAGE); - long max = proc.mState.getLong(off, PSS_MAXIMUM); - long umin = proc.mState.getLong(off, PSS_USS_MINIMUM); - long uavg = proc.mState.getLong(off, PSS_USS_AVERAGE); - long umax = proc.mState.getLong(off, PSS_USS_MAXIMUM); - pw.print(','); - printProcStateTag(pw, type); - pw.print(':'); - pw.print(count); - pw.print(':'); - pw.print(min); - pw.print(':'); - pw.print(avg); - pw.print(':'); - pw.print(max); - pw.print(':'); - pw.print(umin); - pw.print(':'); - pw.print(uavg); - pw.print(':'); - pw.print(umax); - } - } - - static int[] parseStateList(String[] states, int mult, String arg, boolean[] outSep, - String[] outError) { - ArrayList<Integer> res = new ArrayList<Integer>(); - int lastPos = 0; - for (int i=0; i<=arg.length(); i++) { - char c = i < arg.length() ? arg.charAt(i) : 0; - if (c != ',' && c != '+' && c != ' ' && c != 0) { - continue; - } - boolean isSep = c == ','; - if (lastPos == 0) { - // We now know the type of op. - outSep[0] = isSep; - } else if (c != 0 && outSep[0] != isSep) { - outError[0] = "inconsistent separators (can't mix ',' with '+')"; - return null; - } - if (lastPos < (i-1)) { - String str = arg.substring(lastPos, i); - for (int j=0; j<states.length; j++) { - if (str.equals(states[j])) { - res.add(j); - str = null; - break; - } - } - if (str != null) { - outError[0] = "invalid word \"" + str + "\""; - return null; - } - } - lastPos = i + 1; - } - - int[] finalRes = new int[res.size()]; - for (int i=0; i<res.size(); i++) { - finalRes[i] = res.get(i) * mult; - } - return finalRes; - } - - static private void dumpHelp(PrintWriter pw) { - pw.println("Process stats (procstats) dump options:"); - pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]"); - pw.println(" [--details] [--current] [--commit] [--write] [-h] [<package.name>]"); - pw.println(" --checkin: perform a checkin: print and delete old committed states."); - pw.println(" --c: print only state in checkin format."); - pw.println(" --csv: output data suitable for putting in a spreadsheet."); - pw.println(" --csv-screen: on, off."); - pw.println(" --csv-mem: norm, mod, low, crit."); - pw.println(" --csv-proc: pers, top, fore, vis, precept, backup,"); - pw.println(" service, home, prev, cached"); - pw.println(" --details: dump all execution details, not just summary."); - pw.println(" --current: only dump current state."); - pw.println(" --commit: commit current stats to disk and reset to start new stats."); - pw.println(" --write: write current in-memory stats to disk."); - pw.println(" --read: replace current stats with last-written stats."); - pw.println(" -a: print everything."); - pw.println(" -h: print this help text."); - pw.println(" <package.name>: optional name of package to filter output by."); - } - - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - final long now = SystemClock.uptimeMillis(); - - boolean isCheckin = false; - boolean isCompact = false; - boolean isCsv = false; - boolean currentOnly = false; - boolean dumpDetails = false; - boolean dumpAll = false; - String reqPackage = null; - boolean csvSepScreenStats = false; - int[] csvScreenStats = new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}; - boolean csvSepMemStats = false; - int[] csvMemStats = new int[] {ADJ_MEM_FACTOR_CRITICAL}; - boolean csvSepProcStats = true; - int[] csvProcStats = ALL_PROC_STATES; - if (args != null) { - for (int i=0; i<args.length; i++) { - String arg = args[i]; - if ("--checkin".equals(arg)) { - isCheckin = true; - } else if ("-c".equals(arg)) { - isCompact = true; - } else if ("--csv".equals(arg)) { - isCsv = true; - } else if ("--csv-screen".equals(arg)) { - i++; - if (i >= args.length) { - pw.println("Error: argument required for --csv-screen"); - dumpHelp(pw); - return; - } - boolean[] sep = new boolean[1]; - String[] error = new String[1]; - csvScreenStats = parseStateList(ADJ_SCREEN_NAMES_CSV, ADJ_SCREEN_MOD, - args[i], sep, error); - if (csvScreenStats == null) { - pw.println("Error in \"" + args[i] + "\": " + error[0]); - dumpHelp(pw); - return; - } - csvSepScreenStats = sep[0]; - } else if ("--csv-mem".equals(arg)) { - i++; - if (i >= args.length) { - pw.println("Error: argument required for --csv-mem"); - dumpHelp(pw); - return; - } - boolean[] sep = new boolean[1]; - String[] error = new String[1]; - csvMemStats = parseStateList(ADJ_MEM_NAMES_CSV, 1, args[i], sep, error); - if (csvMemStats == null) { - pw.println("Error in \"" + args[i] + "\": " + error[0]); - dumpHelp(pw); - return; - } - csvSepMemStats = sep[0]; - } else if ("--csv-proc".equals(arg)) { - i++; - if (i >= args.length) { - pw.println("Error: argument required for --csv-proc"); - dumpHelp(pw); - return; - } - boolean[] sep = new boolean[1]; - String[] error = new String[1]; - csvProcStats = parseStateList(STATE_NAMES_CSV, 1, args[i], sep, error); - if (csvProcStats == null) { - pw.println("Error in \"" + args[i] + "\": " + error[0]); - dumpHelp(pw); - return; - } - csvSepProcStats = sep[0]; - } else if ("--details".equals(arg)) { - dumpDetails = true; - } else if ("--current".equals(arg)) { - currentOnly = true; - } else if ("--commit".equals(arg)) { - mState.mFlags |= State.FLAG_COMPLETE; - mState.writeStateLocked(true, true); - pw.println("Process stats committed."); - return; - } else if ("--write".equals(arg)) { - writeStateSyncLocked(); - pw.println("Process stats written."); - return; - } else if ("--read".equals(arg)) { - readLocked(); - pw.println("Process stats read."); - return; - } else if ("-h".equals(arg)) { - dumpHelp(pw); - return; - } else if ("-a".equals(arg)) { - dumpDetails = true; - dumpAll = true; - } else if (arg.length() > 0 && arg.charAt(0) == '-'){ - pw.println("Unknown option: " + arg); - dumpHelp(pw); - return; - } else { - // Not an option, last argument must be a package name. - try { - IPackageManager pm = AppGlobals.getPackageManager(); - if (pm.getPackageUid(arg, UserHandle.getCallingUserId()) >= 0) { - reqPackage = arg; - // Include all details, since we know we are only going to - // be dumping a smaller set of data. In fact only the details - // container per-package data, so that are needed to be able - // to dump anything at all when filtering by package. - dumpDetails = true; - } - } catch (RemoteException e) { - } - if (reqPackage == null) { - pw.println("Unknown package: " + arg); - dumpHelp(pw); - return; - } - } - } - } - - if (isCsv) { - pw.print("Processes running summed over"); - if (!csvSepScreenStats) { - for (int i=0; i<csvScreenStats.length; i++) { - pw.print(" "); - printScreenLabelCsv(pw, csvScreenStats[i]); - } - } - if (!csvSepMemStats) { - for (int i=0; i<csvMemStats.length; i++) { - pw.print(" "); - printMemLabelCsv(pw, csvMemStats[i]); - } - } - if (!csvSepProcStats) { - for (int i=0; i<csvProcStats.length; i++) { - pw.print(" "); - pw.print(STATE_NAMES_CSV[csvProcStats[i]]); - } - } - pw.println(); - synchronized (mLock) { - dumpFilteredProcessesCsvLocked(pw, null, - csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats, - csvSepProcStats, csvProcStats, now, reqPackage); - /* - dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:", - false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, - true, new int[] {ADJ_MEM_FACTOR_CRITICAL}, - true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, - STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, - STATE_PREVIOUS, STATE_CACHED}, - now, reqPackage); - dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:", - false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, - false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW, - ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE}, - true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, - STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, - STATE_PREVIOUS, STATE_CACHED}, - now, reqPackage); - */ - } - return; - } - - boolean sepNeeded = false; - if (!currentOnly || isCheckin) { - mWriteLock.lock(); - try { - ArrayList<String> files = getCommittedFiles(0, !isCheckin); - if (files != null) { - for (int i=0; i<files.size(); i++) { - if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i)); - try { - State state = new State(files.get(i)); - if (state.mReadError != null) { - if (isCheckin || isCompact) pw.print("err,"); - pw.print("Failure reading "); pw.print(files.get(i)); - pw.print("; "); pw.println(state.mReadError); - if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i)); - (new File(files.get(i))).delete(); - continue; - } - String fileStr = state.mFile.getBaseFile().getPath(); - boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX); - if (isCheckin || isCompact) { - // Don't really need to lock because we uniquely own this object. - state.dumpCheckinLocked(pw, reqPackage); - } else { - if (sepNeeded) { - pw.println(); - } else { - sepNeeded = true; - } - pw.print("COMMITTED STATS FROM "); - pw.print(state.mTimePeriodStartClockStr); - if (checkedIn) pw.print(" (checked in)"); - pw.println(":"); - // Don't really need to lock because we uniquely own this object. - if (dumpDetails) { - state.dumpLocked(pw, reqPackage, now, dumpAll); - } else { - state.dumpSummaryLocked(pw, reqPackage, now); - } - } - if (isCheckin) { - // Rename file suffix to mark that it has checked in. - state.mFile.getBaseFile().renameTo(new File( - fileStr + STATE_FILE_CHECKIN_SUFFIX)); - } - } catch (Throwable e) { - pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i)); - e.printStackTrace(pw); - } - } - } - } finally { - mWriteLock.unlock(); - } - } - if (!isCheckin) { - synchronized (mLock) { - if (isCompact) { - mState.dumpCheckinLocked(pw, reqPackage); - } else { - if (sepNeeded) { - pw.println(); - pw.println("CURRENT STATS:"); - } - if (dumpDetails) { - mState.dumpLocked(pw, reqPackage, now, dumpAll); - } else { - mState.dumpSummaryLocked(pw, reqPackage, now); - } - } - } - } - } -} diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index 14ccece..39756c3 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -16,6 +16,7 @@ package com.android.server.am; +import com.android.internal.app.ProcessStats; import com.android.internal.os.BatteryStatsImpl; import com.android.server.NotificationManagerService; @@ -83,7 +84,7 @@ final class ServiceRecord extends Binder { ProcessRecord app; // where this service is running or null. ProcessRecord isolatedProc; // keep track of isolated process, if requested - ProcessTracker.ServiceState tracker; // tracking service execution, may be null + ProcessStats.ServiceState tracker; // tracking service execution, may be null boolean isForeground; // is service currently in foreground mode? int foregroundId; // Notification ID of last foreground req. Notification foregroundNoti; // Notification record of foreground state. @@ -310,12 +311,12 @@ final class ServiceRecord extends Binder { createdFromFg = callerIsFg; } - public ProcessTracker.ServiceState getTracker() { + public ProcessStats.ServiceState getTracker() { if (tracker != null) { return tracker; } if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) { - tracker = ams.mProcessTracker.getServiceStateLocked(serviceInfo.packageName, + tracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName, serviceInfo.applicationInfo.uid, serviceInfo.processName, serviceInfo.name); } return tracker; |