Fix issue #10948509: Crash in procstats when there is no data

Not dealing with the case where there is a null list.

Also fixed some bugs I found while looking at this:

- When resetting the stats, we would use a newly computed time stamp
  for the total durations rather than the one we used to reset the
  proc/service entries.  This would result in them being able to be
  slightly > 100%.
- There was a bug in how we split a single process state into its
  per-package representation, where we would but the cloned process
  state into the new package's entry (instead of properly for its
  own package entry), to be immediately overwritten by the new
  process state we make for that package.  This could result in
  bad data for processes that have multiple packages.
- There was a bug in resetting service stats, where we wouldn't
  update the overall run timestamp, allowing that time to sometimes
  be > 100%.
- There was a bug in computing pss data for processes with multiple
  packages, where the pss data was not distributed across all of the
  activity per-package process states.
- There was a bug in computing the zram information that would cause
  it to compute the wrong value, and then never be displayed.

Finally a little code refactoring so that ProcessState and ServiceState
can now share a common implementation for the table of duration values.

Change-Id: I5e0f4e9107829b81f395dad9419c33257b4f8902
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index a281f7c..a95bac8 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -47,6 +47,10 @@
 
     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
+
     public static final int STATE_NOTHING = -1;
     public static final int STATE_PERSISTENT = 0;
     public static final int STATE_TOP = 1;
@@ -1015,7 +1019,7 @@
                 pkgMap.removeAt(ip);
             }
         }
-        mStartTime = SystemClock.uptimeMillis();
+        mStartTime = now;
         if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
     }
 
@@ -1449,7 +1453,7 @@
                     mReadError = "bad uid: " + uid;
                     return;
                 }
-                PackageState pkgState = new PackageState(uid);
+                PackageState pkgState = new PackageState(pkgName, uid);
                 mPackages.put(pkgName, uid, pkgState);
                 int NPROCS = in.readInt();
                 if (NPROCS < 0) {
@@ -1620,7 +1624,7 @@
         if (as != null) {
             return as;
         }
-        as = new PackageState(uid);
+        as = new PackageState(packageName, uid);
         mPackages.put(packageName, uid, as);
         return as;
     }
@@ -1646,11 +1650,22 @@
                 // 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.
+                // 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();
-                pkgState.mProcesses.put(commonProc.mName, commonProc.clone(
-                        commonProc.mPackage, now));
+                // 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.mPackage, uid);
+                if (commonPkgState != null) {
+                    commonPkgState.mProcesses.put(commonProc.mName, commonProc.clone(
+                            commonProc.mPackage, now));
+                } else {
+                    Slog.w(TAG, "Cloning proc state: no package state " + commonProc.mPackage
+                            + "/" + uid + " for proc " + commonProc.mName);
+                }
+                // And now make a fresh new process state for the new package name.
                 ps = new ProcessState(commonProc, packageName, uid, processName, now);
             }
         } else {
@@ -1677,6 +1692,32 @@
         return ss;
     }
 
+    private void dumpProcessInternalLocked(PrintWriter pw, String prefix, ProcessState proc,
+            boolean dumpAll) {
+        if (dumpAll) {
+            pw.print(prefix); pw.print("myID=");
+                    pw.print(Integer.toHexString(System.identityHashCode(proc)));
+                    pw.print(" mCommonProcess=");
+                    pw.print(Integer.toHexString(System.identityHashCode(proc.mCommonProcess)));
+                    pw.print(" mPackage="); pw.println(proc.mPackage);
+            if (proc.mMultiPackage) {
+                pw.print(prefix); pw.print("mMultiPackage="); pw.println(proc.mMultiPackage);
+            }
+            if (proc != proc.mCommonProcess) {
+                pw.print(prefix); pw.print("Common Proc: "); pw.print(proc.mCommonProcess.mName);
+                        pw.print("/"); pw.print(proc.mCommonProcess.mUid);
+                        pw.print(" pkg="); pw.println(proc.mCommonProcess.mPackage);
+            }
+        }
+        pw.print(prefix); pw.print("mActive="); pw.println(proc.mActive);
+        if (proc.mDead) {
+            pw.print(prefix); pw.print("mDead="); pw.println(proc.mDead);
+        }
+        pw.print(prefix); pw.print("mNumActiveServices="); pw.print(proc.mNumActiveServices);
+                pw.print(" mNumStartedServices=");
+                pw.println(proc.mNumStartedServices);
+    }
+
     public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
             boolean dumpAll) {
         long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
@@ -1715,10 +1756,7 @@
                                 ALL_PROC_STATES, now);
                         dumpProcessPss(pw, "        ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
                                 ALL_PROC_STATES);
-                        pw.print("        mActive="); pw.println(proc.mActive);
-                        pw.print("        mNumActiveServices="); pw.print(proc.mNumActiveServices);
-                                pw.print(" mNumStartedServices=");
-                                pw.println(proc.mNumStartedServices);
+                        dumpProcessInternalLocked(pw, "        ", proc, dumpAll);
                     }
                 } else {
                     ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
@@ -1737,6 +1775,7 @@
                     pw.print(pkgState.mServices.keyAt(isvc));
                     pw.println(":");
                     ServiceState svc = pkgState.mServices.valueAt(isvc);
+                    pw.print("        Process: "); pw.println(svc.mProcessName);
                     dumpServiceStats(pw, "        ", "          ", "    ", "Running", svc,
                             svc.mRunCount, ServiceState.SERVICE_RUN, svc.mRunState,
                             svc.mRunStartTime, now, totalTime, !dumpSummary || dumpAll);
@@ -1759,16 +1798,19 @@
         if (reqPackage == null) {
             ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
             printedHeader = false;
+            int numShownProcs = 0, numTotalProcs = 0;
             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);
+                    numTotalProcs++;
                     ProcessState proc = uids.valueAt(iu);
                     if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING
                             && proc.mPssTableSize == 0) {
                         continue;
                     }
+                    numShownProcs++;
                     if (!printedHeader) {
                         pw.println();
                         pw.println("Per-Process Stats:");
@@ -1783,13 +1825,15 @@
                     dumpProcessPss(pw, "        ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
                             ALL_PROC_STATES);
                     if (dumpAll) {
-                        pw.print("        mActive="); pw.println(proc.mActive);
-                        pw.print("        mNumActiveServices="); pw.print(proc.mNumActiveServices);
-                                pw.print(" mNumStartedServices=");
-                                pw.println(proc.mNumStartedServices);
+                        dumpProcessInternalLocked(pw, "        ", proc, dumpAll);
                     }
                 }
             }
+            if (dumpAll) {
+                pw.println();
+                pw.print("  Total procs: "); pw.print(numShownProcs);
+                        pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
+            }
 
             pw.println();
             if (dumpSummary) {
@@ -1876,7 +1920,7 @@
         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);
+                ALL_PROC_STATES, NON_CACHED_PROC_STATES, now, totalTime, reqPackage);
         pw.println();
         dumpTotalsLocked(pw, now);
     }
@@ -1916,22 +1960,22 @@
     }
 
     void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix,
-            int[] screenStates, int[] memStates, int[] procStates, long now, long totalTime,
-            String reqPackage) {
+            int[] screenStates, int[] memStates, int[] procStates,
+            int[] sortProcStates, long now, long totalTime, String reqPackage) {
         ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
-                procStates, now, reqPackage);
+                procStates, sortProcStates, now, reqPackage);
         if (procs.size() > 0) {
             if (header != null) {
                 pw.println();
                 pw.println(header);
             }
-            dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates, procStates,
-                    now, totalTime);
+            dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates,
+                    sortProcStates, now, totalTime);
         }
     }
 
     public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates,
-            int[] procStates, long now, String reqPackage) {
+            int[] procStates, int sortProcStates[], 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++) {
@@ -1953,6 +1997,9 @@
             if (computeProcessTimeLocked(proc, screenStates, memStates,
                     procStates, now) > 0) {
                 outProcs.add(proc);
+                if (procStates != sortProcStates) {
+                    computeProcessTimeLocked(proc, screenStates, memStates, sortProcStates, now);
+                }
             }
         }
         Collections.sort(outProcs, new Comparator<ProcessState>() {
@@ -2134,15 +2181,97 @@
         pw.println();
     }
 
-    public static final class ProcessState {
+    public static class DurationsTable {
         public final ProcessStats mStats;
+        public final String mName;
+        public int[] mDurationsTable;
+        public int mDurationsTableSize;
+
+        public DurationsTable(ProcessStats stats, String name) {
+            mStats = stats;
+            mName = name;
+        }
+
+        void copyDurationsTo(DurationsTable other) {
+            if (mDurationsTable != null) {
+                mStats.mAddLongTable = new int[mDurationsTable.length];
+                mStats.mAddLongTableSize = 0;
+                for (int i=0; i<mDurationsTableSize; i++) {
+                    int origEnt = mDurationsTable[i];
+                    int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+                    int newOff = mStats.addLongData(i, type, 1);
+                    mStats.mAddLongTable[i] = newOff | type;
+                    mStats.setLong(newOff, 0, mStats.getLong(origEnt, 0));
+                }
+                other.mDurationsTable = mStats.mAddLongTable;
+                other.mDurationsTableSize = mStats.mAddLongTableSize;
+            } else {
+                other.mDurationsTable = null;
+                other.mDurationsTableSize = 0;
+            }
+        }
+
+        void addDurations(DurationsTable other) {
+            for (int i=0; i<other.mDurationsTableSize; i++) {
+                int ent = other.mDurationsTable[i];
+                int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+                if (DEBUG) Slog.d(TAG, "Adding state " + state + " duration "
+                        + other.mStats.getLong(ent, 0));
+                addDuration(state, other.mStats.getLong(ent, 0));
+            }
+        }
+
+        void resetDurationsSafely() {
+            mDurationsTable = null;
+            mDurationsTableSize = 0;
+        }
+
+        void writeDurationsToParcel(Parcel out) {
+            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]);
+            }
+        }
+
+        boolean readDurationsFromParcel(Parcel in) {
+            mDurationsTable = mStats.readTableFromParcel(in, mName, "durations");
+            if (mDurationsTable == BAD_TABLE) {
+                return false;
+            }
+            mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
+            return true;
+        }
+
+        void addDuration(int state, long dur) {
+            int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
+            int off;
+            if (idx >= 0) {
+                off = mDurationsTable[idx];
+            } else {
+                mStats.mAddLongTable = mDurationsTable;
+                mStats.mAddLongTableSize = mDurationsTableSize;
+                off = mStats.addLongData(~idx, state, 1);
+                mDurationsTable = mStats.mAddLongTable;
+                mDurationsTableSize = mStats.mAddLongTableSize;
+            }
+            long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+            if (DEBUG) Slog.d(TAG, "Duration of " + mName + " state " + state + " inc by " + dur
+                    + " from " + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK]);
+            longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
+        }
+
+        long getDuration(int state, long now) {
+            int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
+            return idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0;
+        }
+    }
+
+    public static final class ProcessState extends DurationsTable {
         public final ProcessState mCommonProcess;
         public final String mPackage;
         public final int mUid;
-        public final String mName;
-
-        int[] mDurationsTable;
-        int mDurationsTableSize;
 
         //final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT];
         int mCurState = STATE_NOTHING;
@@ -2175,11 +2304,10 @@
          * a single package running in a process.  The initial state is not running.
          */
         public ProcessState(ProcessStats processStats, String pkg, int uid, String name) {
-            mStats = processStats;
+            super(processStats, name);
             mCommonProcess = this;
             mPackage = pkg;
             mUid = uid;
-            mName = name;
         }
 
         /**
@@ -2189,30 +2317,17 @@
          */
         public ProcessState(ProcessState commonProcess, String pkg, int uid, String name,
                 long now) {
-            mStats = commonProcess.mStats;
+            super(commonProcess.mStats, name);
             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) {
-                mStats.mAddLongTable = new int[mDurationsTable.length];
-                mStats.mAddLongTableSize = 0;
-                for (int i=0; i<mDurationsTableSize; i++) {
-                    int origEnt = mDurationsTable[i];
-                    int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
-                    int newOff = mStats.addLongData(i, type, 1);
-                    mStats.mAddLongTable[i] = newOff | type;
-                    mStats.setLong(newOff, 0, mStats.getLong(origEnt, 0));
-                }
-                pnew.mDurationsTable = mStats.mAddLongTable;
-                pnew.mDurationsTableSize = mStats.mAddLongTableSize;
-            }
+            copyDurationsTo(pnew);
             if (mPssTable != null) {
                 mStats.mAddLongTable = new int[mPssTable.length];
                 mStats.mAddLongTableSize = 0;
@@ -2240,13 +2355,7 @@
         }
 
         void add(ProcessState other) {
-            for (int i=0; i<other.mDurationsTableSize; i++) {
-                int ent = other.mDurationsTable[i];
-                int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
-                if (DEBUG) Slog.d(TAG, "Adding state " + state + " duration "
-                        + other.mStats.getLong(ent, 0));
-                addDuration(state, other.mStats.getLong(ent, 0));
-            }
+            addDurations(other);
             for (int i=0; i<other.mPssTableSize; i++) {
                 int ent = other.mPssTable[i];
                 int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
@@ -2267,8 +2376,7 @@
         }
 
         void resetSafely(long now) {
-            mDurationsTable = null;
-            mDurationsTableSize = 0;
+            resetDurationsSafely();
             mStartTime = now;
             mLastPssState = STATE_NOTHING;
             mLastPssTime = 0;
@@ -2294,12 +2402,7 @@
 
         void writeToParcel(Parcel out, long 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]);
-            }
+            writeDurationsToParcel(out);
             out.writeInt(mPssTableSize);
             for (int i=0; i<mPssTableSize; i++) {
                 if (DEBUG) Slog.i(TAG, "Writing in " + mName + " pss #" + i + ": "
@@ -2322,11 +2425,9 @@
                 mMultiPackage = multiPackage;
             }
             if (DEBUG) Slog.d(TAG, "Reading durations table...");
-            mDurationsTable = mStats.readTableFromParcel(in, mName, "durations");
-            if (mDurationsTable == BAD_TABLE) {
+            if (!readDurationsFromParcel(in)) {
                 return false;
             }
-            mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
             if (DEBUG) Slog.d(TAG, "Reading pss table...");
             mPssTable = mStats.readTableFromParcel(in, mName, "pss");
             if (mPssTable == BAD_TABLE) {
@@ -2411,24 +2512,6 @@
             mStartTime = now;
         }
 
-        void addDuration(int state, long dur) {
-            int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
-            int off;
-            if (idx >= 0) {
-                off = mDurationsTable[idx];
-            } else {
-                mStats.mAddLongTable = mDurationsTable;
-                mStats.mAddLongTableSize = mDurationsTableSize;
-                off = mStats.addLongData(~idx, state, 1);
-                mDurationsTable = mStats.mAddLongTable;
-                mDurationsTableSize = mStats.mAddLongTableSize;
-            }
-            long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
-            if (DEBUG) Slog.d(TAG, "Duration of " + mName + " state " + state + " inc by " + dur
-                    + " from " + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK]);
-            longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
-        }
-
         void incActiveServices() {
             if (mCommonProcess != this) {
                 mCommonProcess.incActiveServices();
@@ -2470,7 +2553,8 @@
             }
         }
 
-        public void addPss(long pss, long uss, boolean always) {
+        public void addPss(long pss, long uss, boolean always,
+                ArrayMap<String, ProcessState> pkgList) {
             ensureNotDead();
             if (!always) {
                 if (mLastPssState == mCurState && SystemClock.uptimeMillis()
@@ -2481,7 +2565,20 @@
             mLastPssState = mCurState;
             mLastPssTime = SystemClock.uptimeMillis();
             if (mCurState != STATE_NOTHING) {
-                addPss(mCurState, 1, pss, pss, pss, uss, uss, uss);
+                // First update the common process.
+                mCommonProcess.addPss(mCurState, 1, pss, pss, pss, uss, uss, uss);
+
+                // 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).addPss(mCurState, 1,
+                                pss, pss, pss, uss, uss, uss);
+                    }
+                }
             }
         }
 
@@ -2632,8 +2729,7 @@
         }
 
         long getDuration(int state, long now) {
-            int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
-            long time = idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0;
+            long time = super.getDuration(state, now);
             if (mCurState == state) {
                 time += now - mStartTime;
             }
@@ -2676,10 +2772,8 @@
         }
     }
 
-    public static final class ServiceState {
-        final ProcessStats mStats;
+    public static final class ServiceState extends DurationsTable {
         public final String mPackage;
-        public final String mName;
         public final String mProcessName;
         ProcessState mProc;
 
@@ -2691,9 +2785,6 @@
         public static final int SERVICE_EXEC = 3;
         static final int SERVICE_COUNT = 4;
 
-        int[] mDurationsTable;
-        int mDurationsTableSize;
-
         int mRunCount;
         public int mRunState = STATE_NOTHING;
         long mRunStartTime;
@@ -2712,9 +2803,8 @@
 
         public ServiceState(ProcessStats processStats, String pkg, String name,
                 String processName, ProcessState proc) {
-            mStats = processStats;
+            super(processStats, name);
             mPackage = pkg;
-            mName = name;
             mProcessName = processName;
             mProc = proc;
         }
@@ -2743,11 +2833,7 @@
         }
 
         void add(ServiceState other) {
-            for (int i=0; i<other.mDurationsTableSize; i++) {
-                int ent = other.mDurationsTable[i];
-                int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
-                addStateTime(state, other.mStats.getLong(ent, 0));
-            }
+            addDurations(other);
             mRunCount += other.mRunCount;
             mStartedCount += other.mStartedCount;
             mBoundCount += other.mBoundCount;
@@ -2755,22 +2841,16 @@
         }
 
         void resetSafely(long now) {
-            mDurationsTable = null;
-            mDurationsTableSize = 0;
+            resetDurationsSafely();
             mRunCount = mRunState != STATE_NOTHING ? 1 : 0;
             mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0;
             mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0;
             mExecCount = mExecState != STATE_NOTHING ? 1 : 0;
-            mStartedStartTime = mBoundStartTime = mExecStartTime = now;
+            mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime = now;
         }
 
         void writeToParcel(Parcel out, long 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]);
-            }
+            writeDurationsToParcel(out);
             out.writeInt(mRunCount);
             out.writeInt(mStartedCount);
             out.writeInt(mBoundCount);
@@ -2778,12 +2858,9 @@
         }
 
         boolean readFromParcel(Parcel in) {
-            if (DEBUG) Slog.d(TAG, "Reading durations table...");
-            mDurationsTable = mStats.readTableFromParcel(in, mPackage, "service");
-            if (mDurationsTable == BAD_TABLE) {
+            if (!readDurationsFromParcel(in)) {
                 return false;
             }
-            mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
             mRunCount = in.readInt();
             mStartedCount = in.readInt();
             mBoundCount = in.readInt();
@@ -2791,40 +2868,22 @@
             return true;
         }
 
-        void addStateTime(int state, long time) {
-            if (time > 0) {
-                int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
-                int off;
-                if (idx >= 0) {
-                    off = mDurationsTable[idx];
-                } else {
-                    mStats.mAddLongTable = mDurationsTable;
-                    mStats.mAddLongTableSize = mDurationsTableSize;
-                    off = mStats.addLongData(~idx, state, 1);
-                    mDurationsTable = mStats.mAddLongTable;
-                    mDurationsTableSize = mStats.mAddLongTableSize;
-                }
-                long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
-                longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += time;
-            }
-        }
-
         void commitStateTime(long now) {
             if (mRunState != STATE_NOTHING) {
-                addStateTime(SERVICE_RUN + (mRunState*SERVICE_COUNT), now - mRunStartTime);
+                addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT), now - mRunStartTime);
                 mRunStartTime = now;
             }
             if (mStartedState != STATE_NOTHING) {
-                addStateTime(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
+                addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
                         now - mStartedStartTime);
                 mStartedStartTime = now;
             }
             if (mBoundState != STATE_NOTHING) {
-                addStateTime(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), now - mBoundStartTime);
+                addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), now - mBoundStartTime);
                 mBoundStartTime = now;
             }
             if (mExecState != STATE_NOTHING) {
-                addStateTime(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime);
+                addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime);
                 mExecStartTime = now;
             }
         }
@@ -2834,7 +2893,7 @@
                     || mExecState != STATE_NOTHING) ? memFactor : STATE_NOTHING;
             if (mRunState != state) {
                 if (mRunState != STATE_NOTHING) {
-                    addStateTime(SERVICE_RUN + (mRunState*SERVICE_COUNT),
+                    addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
                             now - mRunStartTime);
                 } else if (state != STATE_NOTHING) {
                     mRunCount++;
@@ -2852,7 +2911,7 @@
             final int state = started ? memFactor : STATE_NOTHING;
             if (mStartedState != state) {
                 if (mStartedState != STATE_NOTHING) {
-                    addStateTime(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
+                    addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
                             now - mStartedStartTime);
                 } else if (started) {
                     mStartedCount++;
@@ -2878,7 +2937,7 @@
             final int state = bound ? memFactor : STATE_NOTHING;
             if (mBoundState != state) {
                 if (mBoundState != STATE_NOTHING) {
-                    addStateTime(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
+                    addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
                             now - mBoundStartTime);
                 } else if (bound) {
                     mBoundCount++;
@@ -2896,7 +2955,7 @@
             final int state = executing ? memFactor : STATE_NOTHING;
             if (mExecState != state) {
                 if (mExecState != STATE_NOTHING) {
-                    addStateTime(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime);
+                    addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime);
                 } else if (executing) {
                     mExecCount++;
                 }
@@ -2909,8 +2968,7 @@
         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 ? mStats.getLong(mDurationsTable[idx], 0) : 0;
+            long time = getDuration(state, now);
             if (curState == memFactor) {
                 time += now - startTime;
             }
@@ -2923,10 +2981,12 @@
                 = new ArrayMap<String, ProcessState>();
         public final ArrayMap<String, ServiceState> mServices
                 = new ArrayMap<String, ServiceState>();
-        final int mUid;
+        public final String mPackageName;
+        public final int mUid;
 
-        public PackageState(int uid) {
+        public PackageState(String packageName, int uid) {
             mUid = uid;
+            mPackageName = packageName;
         }
     }
 
@@ -2951,6 +3011,9 @@
         }
 
         void print(PrintWriter pw, long overallTime, boolean full) {
+            if (totalTime > overallTime) {
+                pw.print("*");
+            }
             printPercent(pw, (double) totalTime / (double) overallTime);
             if (numPss > 0) {
                 pw.print(" (");
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 15792e8..62f057f 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -156,31 +156,32 @@
  * Uses libmemtrack to retrieve graphics memory that the process is using.
  * Any graphics memory reported in /proc/pid/smaps is not included here.
  */
-static int read_memtrack_memory(struct memtrack_proc* p, int pid, struct graphics_memory_pss* graphics_mem)
+static int read_memtrack_memory(struct memtrack_proc* p, int pid,
+        struct graphics_memory_pss* graphics_mem)
 {
     int err = memtrack_proc_get(p, pid);
     if (err != 0) {
-        ALOGE("failed to get memory consumption info: %d", err);
+        ALOGW("failed to get memory consumption info: %d", err);
         return err;
     }
 
     ssize_t pss = memtrack_proc_graphics_pss(p);
     if (pss < 0) {
-        ALOGE("failed to get graphics pss: %d", pss);
+        ALOGW("failed to get graphics pss: %d", pss);
         return pss;
     }
     graphics_mem->graphics = pss / 1024;
 
     pss = memtrack_proc_gl_pss(p);
     if (pss < 0) {
-        ALOGE("failed to get gl pss: %d", pss);
+        ALOGW("failed to get gl pss: %d", pss);
         return pss;
     }
     graphics_mem->gl = pss / 1024;
 
     pss = memtrack_proc_other_pss(p);
     if (pss < 0) {
-        ALOGE("failed to get other pss: %d", pss);
+        ALOGW("failed to get other pss: %d", pss);
         return pss;
     }
     graphics_mem->other = pss / 1024;
@@ -199,7 +200,7 @@
 
     struct memtrack_proc* p = memtrack_proc_new();
     if (p == NULL) {
-        ALOGE("failed to create memtrack_proc");
+        ALOGW("failed to create memtrack_proc");
         return -1;
     }
 
@@ -418,8 +419,6 @@
         stats[HEAP_GL].privateDirty = graphics_mem.gl;
         stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other;
         stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other;
-    } else {
-        ALOGE("Failed to read gpu memory");
     }
 
     for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) {
@@ -623,7 +622,7 @@
         close(fd);
         if (len > 0) {
             buffer[len] = 0;
-            mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer);
+            mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer)/1024;
         }
     }
 
@@ -633,7 +632,7 @@
     }
     jlong* outArray = env->GetLongArrayElements(out, 0);
     if (outArray != NULL) {
-        for (int i=0; i<maxNum && tags[i]; i++) {
+        for (int i=0; i<maxNum; i++) {
             outArray[i] = mem[i];
         }
     }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index a14d729..92ac30d 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1689,7 +1689,7 @@
                                     && proc.pid == pid) {
                                 num++;
                                 proc.lastPssTime = SystemClock.uptimeMillis();
-                                proc.baseProcessTracker.addPss(pss, tmp[0], true);
+                                proc.baseProcessTracker.addPss(pss, tmp[0], true, proc.pkgList);
                                 if (DEBUG_PSS) Slog.d(TAG, "PSS of " + proc.toShortString()
                                         + ": " + pss + " lastPss=" + proc.lastPss
                                         + " state=" + ProcessList.makeProcStateString(procState));
@@ -4278,7 +4278,7 @@
                     if (proc.thread != null && proc.setAdj == oomAdj) {
                         // Record this for posterity if the process has been stable.
                         proc.baseProcessTracker.addPss(infos[i].getTotalPss(),
-                                infos[i].getTotalUss(), false);
+                                infos[i].getTotalUss(), false, proc.pkgList);
                     }
                 }
             }
@@ -4305,7 +4305,7 @@
                 synchronized (this) {
                     if (proc.thread != null && proc.setAdj == oomAdj) {
                         // Record this for posterity if the process has been stable.
-                        proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false);
+                        proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false, proc.pkgList);
                     }
                 }
             }
@@ -11748,7 +11748,7 @@
                 synchronized (this) {
                     if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
                         // Record this for posterity if the process has been stable.
-                        r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true);
+                        r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true, r.pkgList);
                     }
                 }
 
@@ -11909,7 +11909,7 @@
                 if (memInfo.getZramTotalSizeKb() != 0) {
                     if (!isCompact) {
                         pw.print("     ZRAM: "); pw.print(memInfo.getZramTotalSizeKb());
-                                pw.print(" kB used for ");
+                                pw.print(" kB physical used for ");
                                 pw.print(memInfo.getSwapTotalSizeKb()
                                         - memInfo.getSwapFreeSizeKb());
                                 pw.print(" kB in swap (");
diff --git a/services/java/com/android/server/am/ProcessStatsService.java b/services/java/com/android/server/am/ProcessStatsService.java
index be08973..2c49bb9 100644
--- a/services/java/com/android/server/am/ProcessStatsService.java
+++ b/services/java/com/android/server/am/ProcessStatsService.java
@@ -61,7 +61,6 @@
     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 = 3*60*60*1000;  // Commit current stats every 3 hours.
 
     final ActivityManagerService mAm;
     final File mBaseDir;
@@ -160,7 +159,7 @@
     public boolean shouldWriteNowLocked(long now) {
         if (now > (mLastWriteTime+WRITE_PERIOD)) {
             if (SystemClock.elapsedRealtime()
-                    > (mProcessStats.mTimePeriodStartRealtime+COMMIT_PERIOD)) {
+                    > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD)) {
                 mCommitPending = true;
             }
             return true;
@@ -358,7 +357,7 @@
             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);
+                screenStates, memStates, procStates, procStates, now, reqPackage);
         if (procs.size() > 0) {
             if (header != null) {
                 pw.println(header);
@@ -457,7 +456,7 @@
             if (curTime < minTime) {
                 // Need to add in older stats to reach desired time.
                 ArrayList<String> files = getCommittedFiles(0, false, true);
-                if (files.size() > 0) {
+                if (files != null && files.size() > 0) {
                     current.setDataPosition(0);
                     ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current);
                     current.recycle();
@@ -520,7 +519,7 @@
     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] [--full-details] [--current] [--one-day]");
+        pw.println("    [--details] [--full-details] [--current] [--hours]");
         pw.println("    [--commit] [--reset] [--clear] [--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.");
@@ -532,7 +531,7 @@
         pw.println("  --details: dump all execution details, not just summary.");
         pw.println("  --full-details: dump only detail information, for all saved state.");
         pw.println("  --current: only dump current state.");
-        pw.println("  --one-day: dump stats aggregated across about one day.");
+        pw.println("  --hours: aggregate over about N last hours.");
         pw.println("  --commit: commit current stats to disk and reset to start new stats.");
         pw.println("  --reset: reset current stats, without committing.");
         pw.println("  --clear: clear all stats; does both --reset and deletes old stats.");
@@ -562,7 +561,7 @@
         boolean dumpDetails = false;
         boolean dumpFullDetails = false;
         boolean dumpAll = false;
-        boolean oneDay = false;
+        int aggregateHours = 0;
         String reqPackage = null;
         boolean csvSepScreenStats = false;
         int[] csvScreenStats = new int[] { ProcessStats.ADJ_SCREEN_OFF, ProcessStats.ADJ_SCREEN_ON};
@@ -632,8 +631,20 @@
                     dumpDetails = true;
                 } else if ("--full-details".equals(arg)) {
                     dumpFullDetails = true;
-                } else if ("--one-day".equals(arg)) {
-                    oneDay = true;
+                } else if ("--hours".equals(arg)) {
+                    i++;
+                    if (i >= args.length) {
+                        pw.println("Error: argument required for --hours");
+                        dumpHelp(pw);
+                        return;
+                    }
+                    try {
+                        aggregateHours = Integer.parseInt(args[i]);
+                    } catch (NumberFormatException e) {
+                        pw.println("Error: --hours argument not an int -- " + args[i]);
+                        dumpHelp(pw);
+                        return;
+                    }
                 } else if ("--current".equals(arg)) {
                     currentOnly = true;
                 } else if ("--commit".equals(arg)) {
@@ -750,8 +761,9 @@
                 */
             }
             return;
-        } else if (oneDay) {
-            ParcelFileDescriptor pfd = getStatsOverTime(24*60*60*1000);
+        } else if (aggregateHours != 0) {
+            ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000
+                    - (ProcessStats.COMMIT_PERIOD/2));
             if (pfd == null) {
                 pw.println("Unable to build stats!");
                 return;