/* * 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.procstats; import android.os.Debug; import android.os.Parcel; import android.os.Parcelable; 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.DebugUtils; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; import com.android.internal.app.ProcessMap; import com.android.internal.app.procstats.DurationsTable; import com.android.internal.app.procstats.ProcessState; import com.android.internal.app.procstats.PssTable; import com.android.internal.app.procstats.ServiceState; import com.android.internal.app.procstats.SparseMappingTable; import com.android.internal.app.procstats.SysMemUsageTable; import com.android.internal.app.procstats.DumpUtils.*; import dalvik.system.VMRuntime; import libcore.util.EmptyArray; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; 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.regex.Pattern; import java.util.regex.Matcher; public final class ProcessStats implements Parcelable { public static final String TAG = "ProcessStats"; static final boolean DEBUG = false; static final boolean DEBUG_PARCEL = false; public static final String SERVICE_NAME = "procstats"; // How often the service commits its data, giving the minimum batching // that is done. public static long COMMIT_PERIOD = 3*60*60*1000; // Commit current stats every 3 hours // Minimum uptime period before committing. If the COMMIT_PERIOD has elapsed but // the total uptime has not exceeded this amount, then the commit will be held until // it is reached. public static long COMMIT_UPTIME_PERIOD = 60*60*1000; // Must have at least 1 hour elapsed 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 SYS_MEM_USAGE_SAMPLE_COUNT = 0; public static final int SYS_MEM_USAGE_CACHED_MINIMUM = 1; public static final int SYS_MEM_USAGE_CACHED_AVERAGE = 2; public static final int SYS_MEM_USAGE_CACHED_MAXIMUM = 3; public static final int SYS_MEM_USAGE_FREE_MINIMUM = 4; public static final int SYS_MEM_USAGE_FREE_AVERAGE = 5; public static final int SYS_MEM_USAGE_FREE_MAXIMUM = 6; public static final int SYS_MEM_USAGE_ZRAM_MINIMUM = 7; public static final int SYS_MEM_USAGE_ZRAM_AVERAGE = 8; public static final int SYS_MEM_USAGE_ZRAM_MAXIMUM = 9; public static final int SYS_MEM_USAGE_KERNEL_MINIMUM = 10; public static final int SYS_MEM_USAGE_KERNEL_AVERAGE = 11; public static final int SYS_MEM_USAGE_KERNEL_MAXIMUM = 12; public static final int SYS_MEM_USAGE_NATIVE_MINIMUM = 13; public static final int SYS_MEM_USAGE_NATIVE_AVERAGE = 14; public static final int SYS_MEM_USAGE_NATIVE_MAXIMUM = 15; public static final int SYS_MEM_USAGE_COUNT = SYS_MEM_USAGE_NATIVE_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; public static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL }; public static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON }; public 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 }; public static final int[] BACKGROUND_PROC_STATES = new int[] { STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER }; 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 }; // Current version of the parcel format. private static final int PARCEL_VERSION = 21; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0x50535454; public String mReadError; public String mTimePeriodStartClockStr; public int mFlags; public final ProcessMap> mPackages = new ProcessMap>(); public final ProcessMap mProcesses = new ProcessMap(); 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; public long mTimePeriodStartUptime; public long mTimePeriodEndUptime; String mRuntime; boolean mRunning; boolean mHasSwappedOutPss; public final SparseMappingTable mTableData = new SparseMappingTable(); public final long[] mSysMemUsageArgs = new long[SYS_MEM_USAGE_COUNT]; public final SysMemUsageTable mSysMemUsage = new SysMemUsageTable(mTableData); // For writing parcels. ArrayMap mCommonStringToIndex; // For reading parcels. ArrayList mIndexToCommonString; private static final Pattern sPageTypeRegex = Pattern.compile( "^Node\\s+(\\d+),.*. type\\s+(\\w+)\\s+([\\s\\d]+?)\\s*$"); private final ArrayList mPageTypeZones = new ArrayList(); private final ArrayList mPageTypeLabels = new ArrayList(); private final ArrayList mPageTypeSizes = new ArrayList(); public ProcessStats(boolean running) { mRunning = running; reset(); if (running) { // If we are actively running, we need to determine whether the system is // collecting swap pss data. Debug.MemoryInfo info = new Debug.MemoryInfo(); Debug.getMemoryInfo(android.os.Process.myPid(), info); mHasSwappedOutPss = info.hasSwappedOutPss(); } } public ProcessStats(Parcel in) { reset(); readFromParcel(in); } public void add(ProcessStats other) { ArrayMap>> pkgMap = other.mPackages.getMap(); for (int ip=0; ip> uids = pkgMap.valueAt(ip); for (int iu=0; iu versions = uids.valueAt(iu); for (int iv=0; iv> procMap = other.mProcesses.getMap(); for (int ip=0; ip uids = procMap.valueAt(ip); for (int iu=0; iu CREATOR = new Parcelable.Creator() { public ProcessStats createFromParcel(Parcel in) { return new ProcessStats(in); } public ProcessStats[] newArray(int size) { return new ProcessStats[size]; } }; public void computeTotalMemoryUse(TotalMemoryUseCollection data, long now) { data.totalTime = 0; for (int i=0; i= 3) { SysMemUsageTable.mergeSysMemUsage(data.sysMemUsage, 0, longs, idx); longs = tmpLongs; idx = tmpIndex; } } data.sysMemCachedWeight += longs[idx+SYS_MEM_USAGE_CACHED_AVERAGE] * (double)memTime; data.sysMemFreeWeight += longs[idx+SYS_MEM_USAGE_FREE_AVERAGE] * (double)memTime; data.sysMemZRamWeight += longs[idx + SYS_MEM_USAGE_ZRAM_AVERAGE] * (double) memTime; data.sysMemKernelWeight += longs[idx+SYS_MEM_USAGE_KERNEL_AVERAGE] * (double)memTime; data.sysMemNativeWeight += longs[idx+SYS_MEM_USAGE_NATIVE_AVERAGE] * (double)memTime; data.sysMemSamples += longs[idx+SYS_MEM_USAGE_SAMPLE_COUNT]; } } data.hasSwappedOutPss = mHasSwappedOutPss; ArrayMap> procMap = mProcesses.getMap(); for (int iproc=0; iproc uids = procMap.valueAt(iproc); for (int iu=0; iu> procMap = mProcesses.getMap(); for (int ip=procMap.size()-1; ip>=0; ip--) { final SparseArray uids = procMap.valueAt(ip); for (int iu=uids.size()-1; iu>=0; iu--) { uids.valueAt(iu).tmpNumInUse = 0; } } // Next reset or prune all per-package processes, and for the ones that are reset // track this back to the common processes. final ArrayMap>> pkgMap = mPackages.getMap(); for (int ip=pkgMap.size()-1; ip>=0; ip--) { final SparseArray> uids = pkgMap.valueAt(ip); for (int iu=uids.size()-1; iu>=0; iu--) { final SparseArray vpkgs = uids.valueAt(iu); for (int iv=vpkgs.size()-1; iv>=0; iv--) { final PackageState pkgState = vpkgs.valueAt(iv); for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) { final ProcessState ps = pkgState.mProcesses.valueAt(iproc); if (ps.isInUse()) { ps.resetSafely(now); ps.getCommonProcess().tmpNumInUse++; ps.getCommonProcess().tmpFoundSubProc = ps; } else { pkgState.mProcesses.valueAt(iproc).makeDead(); pkgState.mProcesses.removeAt(iproc); } } for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) { final ServiceState ss = pkgState.mServices.valueAt(isvc); if (ss.isInUse()) { ss.resetSafely(now); } else { pkgState.mServices.removeAt(isvc); } } if (pkgState.mProcesses.size() <= 0 && pkgState.mServices.size() <= 0) { vpkgs.removeAt(iv); } } if (vpkgs.size() <= 0) { uids.removeAt(iu); } } if (uids.size() <= 0) { pkgMap.removeAt(ip); } } // Finally prune out any common processes that are no longer in use. for (int ip=procMap.size()-1; ip>=0; ip--) { final SparseArray uids = procMap.valueAt(ip); for (int iu=uids.size()-1; iu>=0; iu--) { ProcessState ps = uids.valueAt(iu); if (ps.isInUse() || ps.tmpNumInUse > 0) { // If this is a process for multiple packages, we could at this point // be back down to one package. In that case, we want to revert back // to a single shared ProcessState. We can do this by converting the // current package-specific ProcessState up to the shared ProcessState, // throwing away the current one we have here (because nobody else is // using it). if (!ps.isActive() && ps.isMultiPackage() && ps.tmpNumInUse == 1) { // Here we go... ps = ps.tmpFoundSubProc; ps.makeStandalone(); uids.setValueAt(iu, ps); } else { ps.resetSafely(now); } } else { ps.makeDead(); uids.removeAt(iu); } } if (uids.size() <= 0) { procMap.removeAt(ip); } } mStartTime = now; if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr); } private void resetCommon() { mTimePeriodStartClock = System.currentTimeMillis(); buildTimePeriodStartClockStr(); mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); mTimePeriodStartUptime = mTimePeriodEndUptime = SystemClock.uptimeMillis(); mTableData.reset(); Arrays.fill(mMemFactorDurations, 0); mSysMemUsage.resetTable(); mStartTime = 0; mReadError = null; mFlags = 0; evaluateSystemProperties(true); updateFragmentation(); } public boolean evaluateSystemProperties(boolean update) { boolean changed = false; String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary()); if (!Objects.equals(runtime, mRuntime)) { changed = true; if (update) { mRuntime = runtime; } } return changed; } private void buildTimePeriodStartClockStr() { mTimePeriodStartClockStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss", mTimePeriodStartClock).toString(); } static final int[] BAD_TABLE = new int[0]; /** * Load the system's memory fragmentation info. */ public void updateFragmentation() { // Parse /proc/pagetypeinfo and store the values. BufferedReader reader = null; try { reader = new BufferedReader(new FileReader("/proc/pagetypeinfo")); final Matcher matcher = sPageTypeRegex.matcher(""); mPageTypeZones.clear(); mPageTypeLabels.clear(); mPageTypeSizes.clear(); while (true) { final String line = reader.readLine(); if (line == null) { break; } matcher.reset(line); if (matcher.matches()) { final Integer zone = Integer.valueOf(matcher.group(1), 10); if (zone == null) { continue; } mPageTypeZones.add(zone); mPageTypeLabels.add(matcher.group(2)); mPageTypeSizes.add(splitAndParseNumbers(matcher.group(3))); } } } catch (IOException ex) { mPageTypeZones.clear(); mPageTypeLabels.clear(); mPageTypeSizes.clear(); return; } finally { if (reader != null) { try { reader.close(); } catch (IOException allHopeIsLost) { } } } } /** * Split the string of digits separaed by spaces. There must be no * leading or trailing spaces. The format is ensured by the regex * above. */ private static int[] splitAndParseNumbers(String s) { // These are always positive and the numbers can't be so big that we'll overflow // so just do the parsing inline. boolean digit = false; int count = 0; final int N = s.length(); // Count the numbers for (int i=0; i= '0' && c <= '9') { if (!digit) { digit = true; count++; } } else { digit = false; } } // Parse the numbers final int[] result = new int[count]; int p = 0; int val = 0; for (int i=0; i= '0' && c <= '9') { if (!digit) { digit = true; val = c - '0'; } else { val *= 10; val += c - '0'; } } else { if (digit) { digit = false; result[p++] = val; } } } if (count > 0) { result[count-1] = val; } return result; } private void writeCompactedLongArray(Parcel out, long[] array, int num) { for (int i=0; i>32)&0x7fffffff)); int bottom = (int)(val&0x0ffffffffL); out.writeInt(top); out.writeInt(bottom); } } } private void readCompactedLongArray(Parcel in, int version, long[] array, int num) { if (version <= 10) { in.readLongArray(array); return; } final int alen = array.length; if (num > alen) { throw new RuntimeException("bad array lengths: got " + num + " array is " + alen); } int i; for (i=0; i= 0) { array[i] = val; } else { int bottom = in.readInt(); array[i] = (((long)~val)<<32) | bottom; } } while (i < alen) { array[i] = 0; i++; } } private void writeCommonString(Parcel out, String name) { Integer index = mCommonStringToIndex.get(name); if (index != null) { out.writeInt(index); return; } index = mCommonStringToIndex.size(); mCommonStringToIndex.put(name, index); out.writeInt(~index); out.writeString(name); } private String readCommonString(Parcel in, int version) { if (version <= 9) { return in.readString(); } int index = in.readInt(); if (index >= 0) { return mIndexToCommonString.get(index); } index = ~index; String name = in.readString(); while (mIndexToCommonString.size() <= index) { mIndexToCommonString.add(null); } mIndexToCommonString.set(index, name); return name; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { writeToParcel(out, SystemClock.uptimeMillis(), flags); } /** @hide */ public void writeToParcel(Parcel out, long now, int flags) { out.writeInt(MAGIC); out.writeInt(PARCEL_VERSION); out.writeInt(STATE_COUNT); out.writeInt(ADJ_COUNT); out.writeInt(PSS_COUNT); out.writeInt(SYS_MEM_USAGE_COUNT); out.writeInt(SparseMappingTable.ARRAY_SIZE); mCommonStringToIndex = new ArrayMap(mProcesses.size()); // First commit all running times. ArrayMap> procMap = mProcesses.getMap(); final int NPROC = procMap.size(); for (int ip=0; ip uids = procMap.valueAt(ip); final int NUID = uids.size(); for (int iu=0; iu>> pkgMap = mPackages.getMap(); final int NPKG = pkgMap.size(); for (int ip=0; ip> uids = pkgMap.valueAt(ip); final int NUID = uids.size(); for (int iu=0; iu vpkgs = uids.valueAt(iu); final int NVERS = vpkgs.size(); for (int iv=0; iv uids = procMap.valueAt(ip); final int NUID = uids.size(); out.writeInt(NUID); for (int iu=0; iu> uids = pkgMap.valueAt(ip); final int NUID = uids.size(); out.writeInt(NUID); for (int iu=0; iu vpkgs = uids.valueAt(iu); final int NVERS = vpkgs.size(); out.writeInt(NVERS); for (int iv=0; iv 0 ? (initialAvail+1) : 16384]; while (true) { int amt = stream.read(data, pos, data.length-pos); if (DEBUG_PARCEL) Slog.i("foo", "Read " + amt + " bytes at " + pos + " of avail " + data.length); if (amt < 0) { if (DEBUG_PARCEL) Slog.i("foo", "**** FINISHED READING: pos=" + pos + " len=" + data.length); outLen[0] = pos; return data; } pos += amt; if (pos >= data.length) { byte[] newData = new byte[pos+16384]; if (DEBUG_PARCEL) Slog.i(TAG, "Copying " + pos + " bytes to new array len " + newData.length); System.arraycopy(data, 0, newData, 0, pos); data = newData; } } } public void read(InputStream stream) { try { int[] len = new int[1]; byte[] raw = readFully(stream, len); Parcel in = Parcel.obtain(); in.unmarshall(raw, 0, len[0]); in.setDataPosition(0); stream.close(); readFromParcel(in); } catch (IOException e) { mReadError = "caught exception: " + e; } } 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) { 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, SYS_MEM_USAGE_COUNT, "sys mem usage count")) { return; } if (!readCheckedInt(in, SparseMappingTable.ARRAY_SIZE, "longs size")) { return; } mIndexToCommonString = new ArrayList(); mTimePeriodStartClock = in.readLong(); buildTimePeriodStartClockStr(); mTimePeriodStartRealtime = in.readLong(); mTimePeriodEndRealtime = in.readLong(); mTimePeriodStartUptime = in.readLong(); mTimePeriodEndUptime = in.readLong(); mRuntime = in.readString(); mHasSwappedOutPss = in.readInt() != 0; mFlags = in.readInt(); mTableData.readFromParcel(in); readCompactedLongArray(in, version, mMemFactorDurations, mMemFactorDurations.length); if (!mSysMemUsage.readFromParcel(in)) { return; } int NPROC = in.readInt(); if (NPROC < 0) { mReadError = "bad process count: " + NPROC; return; } while (NPROC > 0) { NPROC--; final String procName = readCommonString(in, version); 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--; final int uid = in.readInt(); if (uid < 0) { mReadError = "bad uid: " + uid; return; } final String pkgName = readCommonString(in, version); if (pkgName == null) { mReadError = "bad process package name"; return; } final int vers = in.readInt(); ProcessState proc = hadData ? mProcesses.get(procName, uid) : null; if (proc != null) { if (!proc.readFromParcel(in, false)) { return; } } else { proc = new ProcessState(this, pkgName, uid, vers, procName); if (!proc.readFromParcel(in, true)) { return; } } if (DEBUG_PARCEL) Slog.d(TAG, "Adding process: " + procName + " " + uid + " " + proc); mProcesses.put(procName, uid, proc); } } if (DEBUG_PARCEL) 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--; final String pkgName = readCommonString(in, version); 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--; final int uid = in.readInt(); if (uid < 0) { mReadError = "bad uid: " + uid; return; } int NVERS = in.readInt(); if (NVERS < 0) { mReadError = "bad versions count: " + NVERS; return; } while (NVERS > 0) { NVERS--; final int vers = in.readInt(); PackageState pkgState = new PackageState(pkgName, uid); SparseArray vpkg = mPackages.get(pkgName, uid); if (vpkg == null) { vpkg = new SparseArray(); mPackages.put(pkgName, uid, vpkg); } vpkg.put(vers, pkgState); int NPROCS = in.readInt(); if (NPROCS < 0) { mReadError = "bad package process count: " + NPROCS; return; } while (NPROCS > 0) { NPROCS--; String procName = readCommonString(in, version); if (procName == null) { mReadError = "bad package process name"; return; } int hasProc = in.readInt(); if (DEBUG_PARCEL) Slog.d(TAG, "Reading package " + pkgName + " " + uid + " process " + procName + " hasProc=" + hasProc); ProcessState commonProc = mProcesses.get(procName, uid); if (DEBUG_PARCEL) 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, vers, procName, 0); if (!proc.readFromParcel(in, true)) { return; } } if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: " + procName + " " + uid + " " + proc); pkgState.mProcesses.put(procName, proc); } else { if (DEBUG_PARCEL) 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; } String processName = version > 9 ? readCommonString(in, version) : null; ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null; if (serv == null) { serv = new ServiceState(this, pkgName, serviceName, processName, null); } if (!serv.readFromParcel(in)) { return; } if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " service: " + serviceName + " " + uid + " " + serv); pkgState.mServices.put(serviceName, serv); } } } } // Fragmentation info final int NPAGETYPES = in.readInt(); mPageTypeZones.clear(); mPageTypeZones.ensureCapacity(NPAGETYPES); mPageTypeLabels.clear(); mPageTypeLabels.ensureCapacity(NPAGETYPES); mPageTypeSizes.clear(); mPageTypeSizes.ensureCapacity(NPAGETYPES); for (int i=0; i vpkg = mPackages.get(packageName, uid); if (vpkg == null) { vpkg = new SparseArray(); mPackages.put(packageName, uid, vpkg); } PackageState as = vpkg.get(vers); if (as != null) { return as; } as = new PackageState(packageName, uid); vpkg.put(vers, as); return as; } public ProcessState getProcessStateLocked(String packageName, int uid, int vers, String processName) { final PackageState pkgState = getPackageStateLocked(packageName, uid, vers); 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, vers, processName); mProcesses.put(processName, uid, commonProc); if (DEBUG) Slog.d(TAG, "GETPROC created new common " + commonProc); } if (!commonProc.isMultiPackage()) { if (packageName.equals(commonProc.getPackage()) && vers == commonProc.getVersion()) { // 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; if (DEBUG) Slog.d(TAG, "GETPROC also using for pkg " + commonProc); } else { if (DEBUG) Slog.d(TAG, "GETPROC need to split common proc!"); // 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.setMultiPackage(true); // To do this, we need to make two new process states, one a copy // of the current state for the process under the original package // name, and the second a free new process state for it as the // new package name. long now = SystemClock.uptimeMillis(); // First let's make a copy of the current process state and put // that under the now unique state for its original package name. final PackageState commonPkgState = getPackageStateLocked(commonProc.getPackage(), uid, commonProc.getVersion()); if (commonPkgState != null) { ProcessState cloned = commonProc.clone(now); if (DEBUG) Slog.d(TAG, "GETPROC setting clone to pkg " + commonProc.getPackage() + ": " + cloned); commonPkgState.mProcesses.put(commonProc.getName(), cloned); // If this has active services, we need to update their process pointer // to point to the new package-specific process state. for (int i=commonPkgState.mServices.size()-1; i>=0; i--) { ServiceState ss = commonPkgState.mServices.valueAt(i); if (ss.getProcess() == commonProc) { if (DEBUG) Slog.d(TAG, "GETPROC switching service to cloned: " + ss); ss.setProcess(cloned); } else if (DEBUG) { Slog.d(TAG, "GETPROC leaving proc of " + ss); } } } else { Slog.w(TAG, "Cloning proc state: no package state " + commonProc.getPackage() + "/" + uid + " for proc " + commonProc.getName()); } // And now make a fresh new process state for the new package name. ps = new ProcessState(commonProc, packageName, uid, vers, processName, now); if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps); } } 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, vers, processName, SystemClock.uptimeMillis()); if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps); } pkgState.mProcesses.put(processName, ps); if (DEBUG) Slog.d(TAG, "GETPROC adding new pkg " + ps); return ps; } public ServiceState getServiceStateLocked(String packageName, int uid, int vers, String processName, String className) { final ProcessStats.PackageState as = getPackageStateLocked(packageName, uid, vers); ServiceState ss = as.mServices.get(className); if (ss != null) { if (DEBUG) Slog.d(TAG, "GETSVC: returning existing " + ss); return ss; } final ProcessState ps = processName != null ? getProcessStateLocked(packageName, uid, vers, processName) : null; ss = new ServiceState(this, packageName, className, processName, ps); as.mServices.put(className, ss); if (DEBUG) Slog.d(TAG, "GETSVC: creating " + ss + " in " + ps); return ss; } public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary, boolean dumpAll, boolean activeOnly) { long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, mStartTime, now); boolean sepNeeded = false; if (mSysMemUsage.getKeyCount() > 0) { pw.println("System memory usage:"); mSysMemUsage.dump(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ); sepNeeded = true; } ArrayMap>> pkgMap = mPackages.getMap(); boolean printedHeader = false; for (int ip=0; ip> uids = pkgMap.valueAt(ip); for (int iu=0; iu vpkgs = uids.valueAt(iu); for (int iv=0; iv 0 || NSRVS > 0) { if (!printedHeader) { if (sepNeeded) pw.println(); pw.println("Per-Package Stats:"); printedHeader = true; sepNeeded = true; } pw.print(" * "); pw.print(pkgName); pw.print(" / "); UserHandle.formatUid(pw, uid); pw.print(" / v"); pw.print(vers); pw.println(":"); } if (!dumpSummary || dumpAll) { for (int iproc=0; iproc procs = new ArrayList(); for (int iproc=0; iproc> procMap = mProcesses.getMap(); printedHeader = false; int numShownProcs = 0, numTotalProcs = 0; for (int ip=0; ip uids = procMap.valueAt(ip); for (int iu=0; iu procs = collectProcessesLocked(screenStates, memStates, procStates, sortProcStates, now, reqPackage, activeOnly); if (procs.size() > 0) { if (header != null) { pw.println(); pw.println(header); } DumpUtils.dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates, sortProcStates, now, totalTime); } } public ArrayList collectProcessesLocked(int[] screenStates, int[] memStates, int[] procStates, int sortProcStates[], long now, String reqPackage, boolean activeOnly) { final ArraySet foundProcs = new ArraySet(); final ArrayMap>> pkgMap = mPackages.getMap(); for (int ip=0; ip> procs = pkgMap.valueAt(ip); for (int iu=0; iu vpkgs = procs.valueAt(iu); final int NVERS = vpkgs.size(); for (int iv=0; iv outProcs = new ArrayList(foundProcs.size()); for (int i=0; i 0) { outProcs.add(proc); if (procStates != sortProcStates) { proc.computeProcessTimeLocked(screenStates, memStates, sortProcStates, now); } } } Collections.sort(outProcs, ProcessState.COMPARATOR); return outProcs; } public void dumpCheckinLocked(PrintWriter pw, String reqPackage) { final long now = SystemClock.uptimeMillis(); final ArrayMap>> pkgMap = mPackages.getMap(); pw.println("vers,5"); 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"); } if (mHasSwappedOutPss) { pw.print(",swapped-out-pss"); } pw.println(); pw.print("config,"); pw.println(mRuntime); for (int ip=0; ip> uids = pkgMap.valueAt(ip); for (int iu=0; iu vpkgs = uids.valueAt(iu); for (int iv=0; iv> procMap = mProcesses.getMap(); for (int ip=0; ip uids = procMap.valueAt(ip); for (int iu=0; iu 0) { pw.print("sysmemusage"); for (int i=0; i SYS_MEM_USAGE_CACHED_MINIMUM) { pw.print(":"); } pw.print(mSysMemUsage.getValue(key, j)); } } } pw.println(); TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ, ALL_MEM_ADJ); computeTotalMemoryUse(totalMem, now); pw.print("weights,"); pw.print(totalMem.totalTime); pw.print(","); pw.print(totalMem.sysMemCachedWeight); pw.print(":"); pw.print(totalMem.sysMemSamples); pw.print(","); pw.print(totalMem.sysMemFreeWeight); pw.print(":"); pw.print(totalMem.sysMemSamples); pw.print(","); pw.print(totalMem.sysMemZRamWeight); pw.print(":"); pw.print(totalMem.sysMemSamples); pw.print(","); pw.print(totalMem.sysMemKernelWeight); pw.print(":"); pw.print(totalMem.sysMemSamples); pw.print(","); pw.print(totalMem.sysMemNativeWeight); pw.print(":"); pw.print(totalMem.sysMemSamples); for (int i=0; i mProcesses = new ArrayMap(); public final ArrayMap mServices = new ArrayMap(); public final String mPackageName; public final int mUid; public PackageState(String packageName, int uid) { mUid = uid; mPackageName = packageName; } } public static final class ProcessDataCollection { final int[] screenStates; final int[] memStates; final int[] procStates; public long totalTime; public long numPss; public long minPss; public long avgPss; public long maxPss; public long minUss; public long avgUss; public long maxUss; public ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) { screenStates = _screenStates; memStates = _memStates; procStates = _procStates; } void print(PrintWriter pw, long overallTime, boolean full) { if (totalTime > overallTime) { pw.print("*"); } DumpUtils.printPercent(pw, (double) totalTime / (double) overallTime); if (numPss > 0) { pw.print(" ("); DebugUtils.printSizeValue(pw, minPss * 1024); pw.print("-"); DebugUtils.printSizeValue(pw, avgPss * 1024); pw.print("-"); DebugUtils.printSizeValue(pw, maxPss * 1024); pw.print("/"); DebugUtils.printSizeValue(pw, minUss * 1024); pw.print("-"); DebugUtils.printSizeValue(pw, avgUss * 1024); pw.print("-"); DebugUtils.printSizeValue(pw, maxUss * 1024); if (full) { pw.print(" over "); pw.print(numPss); } pw.print(")"); } } } public static class TotalMemoryUseCollection { final int[] screenStates; final int[] memStates; public TotalMemoryUseCollection(int[] _screenStates, int[] _memStates) { screenStates = _screenStates; memStates = _memStates; } public long totalTime; public long[] processStatePss = new long[STATE_COUNT]; public double[] processStateWeight = new double[STATE_COUNT]; public long[] processStateTime = new long[STATE_COUNT]; public int[] processStateSamples = new int[STATE_COUNT]; public long[] sysMemUsage = new long[SYS_MEM_USAGE_COUNT]; public double sysMemCachedWeight; public double sysMemFreeWeight; public double sysMemZRamWeight; public double sysMemKernelWeight; public double sysMemNativeWeight; public int sysMemSamples; public boolean hasSwappedOutPss; } }