Improved memory use reporting.

Change-Id: I38e53e6228bba92a142bafeedb5af8df4e4e5724
diff --git a/api/current.txt b/api/current.txt
index 035da89..a70e8a2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14095,6 +14095,7 @@
     method public static long getNativeHeapAllocatedSize();
     method public static long getNativeHeapFreeSize();
     method public static long getNativeHeapSize();
+    method public static long getPss();
     method public static int getThreadAllocCount();
     method public static int getThreadAllocSize();
     method public static deprecated int getThreadExternalAllocCount();
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index b7cd829..a73e10a 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1523,6 +1523,15 @@
             return true;
         }
 
+        case GET_PROCESS_PSS_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int[] pids = data.createIntArray();
+            long[] pss = getProcessPss(pids);
+            reply.writeNoException();
+            reply.writeLongArray(pss);
+            return true;
+        }
+
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3432,5 +3441,18 @@
         reply.recycle();
     }
 
+    public long[] getProcessPss(int[] pids) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeIntArray(pids);
+        mRemote.transact(GET_PROCESS_PSS_TRANSACTION, data, reply, 0);
+        reply.readException();
+        long[] res = reply.createLongArray();
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c566104..d5f630a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -729,17 +729,19 @@
         }
 
         @Override
-        public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, String[] args) {
+        public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin,
+                boolean all, String[] args) {
             FileOutputStream fout = new FileOutputStream(fd);
             PrintWriter pw = new PrintWriter(fout);
             try {
-                return dumpMemInfo(pw, args);
+                return dumpMemInfo(pw, checkin, all, args);
             } finally {
                 pw.flush();
             }
         }
 
-        private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, String[] args) {
+        private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, boolean checkin, boolean all,
+                String[] args) {
             long nativeMax = Debug.getNativeHeapSize() / 1024;
             long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
             long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
@@ -747,6 +749,10 @@
             Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
             Debug.getMemoryInfo(memInfo);
 
+            if (!all) {
+                return memInfo;
+            }
+
             Runtime runtime = Runtime.getRuntime();
 
             long dalvikMax = runtime.totalMemory() / 1024;
@@ -765,16 +771,8 @@
             long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024;
             SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo();
 
-            // Check to see if we were called by checkin server. If so, print terse format.
-            boolean doCheckinFormat = false;
-            if (args != null) {
-                for (String arg : args) {
-                    if ("-c".equals(arg)) doCheckinFormat = true;
-                }
-            }
-
             // For checkin, we print one long comma-separated list of values
-            if (doCheckinFormat) {
+            if (checkin) {
                 // NOTE: if you change anything significant below, also consider changing
                 // ACTIVITY_THREAD_CHECKIN_VERSION.
                 String processName = (mBoundApplication != null)
@@ -841,13 +839,17 @@
                 pw.print(sqliteAllocated); pw.print(',');
                 pw.print(stats.memoryUsed / 1024); pw.print(',');
                 pw.print(stats.pageCacheOverflo / 1024); pw.print(',');
-                pw.print(stats.largestMemAlloc / 1024); pw.print(',');
+                pw.print(stats.largestMemAlloc / 1024);
                 for (int i = 0; i < stats.dbStats.size(); i++) {
                     DbStats dbStats = stats.dbStats.get(i);
-                    printRow(pw, DB_INFO_FORMAT, dbStats.pageSize, dbStats.dbSize,
-                            dbStats.lookaside, dbStats.cache, dbStats.dbName);
-                    pw.print(',');
+                    pw.print(','); pw.print(dbStats.dbName);
+                    pw.print(','); pw.print(dbStats.pageSize);
+                    pw.print(','); pw.print(dbStats.dbSize);
+                    pw.print(','); pw.print(dbStats.lookaside);
+                    pw.print(','); pw.print(dbStats.cache);
+                    pw.print(','); pw.print(dbStats.cache);
                 }
+                pw.println();
 
                 return memInfo;
             }
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 9a5b527..bea057e 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -491,11 +491,13 @@
         {
             data.enforceInterface(IApplicationThread.descriptor);
             ParcelFileDescriptor fd = data.readFileDescriptor();
+            boolean checkin = data.readInt() != 0;
+            boolean all = data.readInt() != 0;
             String[] args = data.readStringArray();
             Debug.MemoryInfo mi = null;
             if (fd != null) {
                 try {
-                    mi = dumpMemInfo(fd.getFileDescriptor(), args);
+                    mi = dumpMemInfo(fd.getFileDescriptor(), checkin, all, args);
                 } finally {
                     try {
                         fd.close();
@@ -1049,11 +1051,14 @@
                 IBinder.FLAG_ONEWAY);
     }
 
-    public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, String[] args) throws RemoteException {
+    public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, boolean all,
+            String[] args) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeFileDescriptor(fd);
+        data.writeInt(checkin ? 1 : 0);
+        data.writeInt(all ? 1 : 0);
         data.writeStringArray(args);
         mRemote.transact(DUMP_MEM_INFO_TRANSACTION, data, reply, 0);
         reply.readException();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 64d77e8..b1b0583 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -364,7 +364,9 @@
     public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException;
 
     public void updatePersistentConfiguration(Configuration values) throws RemoteException;
-    
+
+    public long[] getProcessPss(int[] pids) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -593,4 +595,5 @@
     int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+133;
     int IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+134;
     int UPDATE_PERSISTENT_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+135;
+    int GET_PROCESS_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+136;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index d0607d0..3a8eb28 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -120,7 +120,8 @@
     void setCoreSettings(Bundle coreSettings) throws RemoteException;
     void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) throws RemoteException;
     void scheduleTrimMemory(int level) throws RemoteException;
-    Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, String[] args) throws RemoteException;
+    Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, boolean all,
+            String[] args) throws RemoteException;
     void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException;
 
     String descriptor = "android.app.IApplicationThread";
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index da2afb6..20a731e 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -818,6 +818,18 @@
     public static native void getMemoryInfo(int pid, MemoryInfo memoryInfo);
 
     /**
+     * Retrieves the PSS memory used by the process as given by the
+     * smaps.
+     */
+    public static native long getPss();
+
+    /**
+     * Retrieves the PSS memory used by the process as given by the
+     * smaps. @hide
+     */
+    public static native long getPss(int pid);
+
+    /**
      * Establish an object allocation limit in the current thread.
      * This feature was never enabled in release builds.  The
      * allocation limits feature was removed in Honeycomb.  This
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 3a3f07e..85fac5f 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -279,6 +279,39 @@
     android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
 }
 
+static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid)
+{
+    char line[1024];
+    jlong pss = 0;
+    unsigned temp;
+
+    char tmp[128];
+    FILE *fp;
+
+    sprintf(tmp, "/proc/%d/smaps", pid);
+    fp = fopen(tmp, "r");
+    if (fp == 0) return 0;
+
+    while (true) {
+        if (fgets(line, 1024, fp) == 0) {
+            break;
+        }
+
+        if (sscanf(line, "Pss: %d kB", &temp) == 1) {
+            pss += temp;
+        }
+    }
+
+    fclose(fp);
+
+    return pss;
+}
+
+static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz)
+{
+    return android_os_Debug_getPssPid(env, clazz, getpid());
+}
+
 static jint read_binder_stat(const char* stat)
 {
     FILE* fp = fopen(BINDER_STATS, "r");
@@ -520,6 +553,10 @@
             (void*) android_os_Debug_getDirtyPages },
     { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)V",
             (void*) android_os_Debug_getDirtyPagesPid },
+    { "getPss",                 "()J",
+            (void*) android_os_Debug_getPss },
+    { "getPss",                 "(I)J",
+            (void*) android_os_Debug_getPssPid },
     { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
             (void*) android_os_Debug_dumpNativeHeap },
     { "getBinderSentTransactions", "()I",
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 14c6306..ba8b58b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3317,6 +3317,14 @@
         return infos;
     }
 
+    public long[] getProcessPss(int[] pids) throws RemoteException {
+        long[] pss = new long[pids.length];
+        for (int i=pids.length-1; i>=0; i--) {
+            pss[i] = Debug.getPss(pids[i]);
+        }
+        return pss;
+    }
+
     public void killApplicationProcess(String processName, int uid) {
         if (processName == null) {
             return;
@@ -8868,15 +8876,15 @@
         }
     }
 
-    ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, String[] args) {
+    ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, int start, String[] args) {
         ArrayList<ProcessRecord> procs;
         synchronized (this) {
-            if (args != null && args.length > 0
-                    && args[0].charAt(0) != '-') {
+            if (args != null && args.length > start
+                    && args[start].charAt(0) != '-') {
                 procs = new ArrayList<ProcessRecord>();
                 int pid = -1;
                 try {
-                    pid = Integer.parseInt(args[0]);
+                    pid = Integer.parseInt(args[start]);
                 } catch (NumberFormatException e) {
 
                 }
@@ -8884,12 +8892,12 @@
                     ProcessRecord proc = mLruProcesses.get(i);
                     if (proc.pid == pid) {
                         procs.add(proc);
-                    } else if (proc.processName.equals(args[0])) {
+                    } else if (proc.processName.equals(args[start])) {
                         procs.add(proc);
                     }
                 }
                 if (procs.size() <= 0) {
-                    pw.println("No process found for: " + args[0]);
+                    pw.println("No process found for: " + args[start]);
                     return null;
                 }
             } else {
@@ -8901,7 +8909,7 @@
 
     final void dumpGraphicsHardwareUsage(FileDescriptor fd,
             PrintWriter pw, String[] args) {
-        ArrayList<ProcessRecord> procs = collectProcesses(pw, args);
+        ArrayList<ProcessRecord> procs = collectProcesses(pw, 0, args);
         if (procs == null) {
             return;
         }
@@ -8945,18 +8953,21 @@
         }
     }
 
-    final void dumpMemItems(PrintWriter pw, String prefix, ArrayList<MemItem> items) {
-        Collections.sort(items, new Comparator<MemItem>() {
-            @Override
-            public int compare(MemItem lhs, MemItem rhs) {
-                if (lhs.pss < rhs.pss) {
-                    return 1;
-                } else if (lhs.pss > rhs.pss) {
-                    return -1;
+    final void dumpMemItems(PrintWriter pw, String prefix, ArrayList<MemItem> items,
+            boolean sort) {
+        if (sort) {
+            Collections.sort(items, new Comparator<MemItem>() {
+                @Override
+                public int compare(MemItem lhs, MemItem rhs) {
+                    if (lhs.pss < rhs.pss) {
+                        return 1;
+                    } else if (lhs.pss > rhs.pss) {
+                        return -1;
+                    }
+                    return 0;
                 }
-                return 0;
-            }
-        });
+            });
+        }
 
         for (int i=0; i<items.size(); i++) {
             MemItem mi = items.get(i);
@@ -8966,7 +8977,29 @@
 
     final void dumpApplicationMemoryUsage(FileDescriptor fd,
             PrintWriter pw, String prefix, String[] args) {
-        ArrayList<ProcessRecord> procs = collectProcesses(pw, args);
+        boolean dumpAll = false;
+        
+        int opti = 0;
+        while (opti < args.length) {
+            String opt = args[opti];
+            if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
+                break;
+            }
+            opti++;
+            if ("-a".equals(opt)) {
+                dumpAll = true;
+            } else if ("-h".equals(opt)) {
+                pw.println("meminfo dump options: [-a] [process]");
+                pw.println("  -a: include all available information for each process.");
+                pw.println("If [process] is specified it can be the name or ");
+                pw.println("pid of a specific process to dump.");
+                return;
+            } else {
+                pw.println("Unknown argument: " + opt + "; use -h for help");
+            }
+        }
+        
+        ArrayList<ProcessRecord> procs = collectProcesses(pw, opti, args);
         if (procs == null) {
             return;
         }
@@ -8974,7 +9007,11 @@
         final boolean isCheckinRequest = scanArgs(args, "--checkin");
         long uptime = SystemClock.uptimeMillis();
         long realtime = SystemClock.elapsedRealtime();
-        
+
+        if (procs.size() == 1 || isCheckinRequest) {
+            dumpAll = true;
+        }
+
         if (isCheckinRequest) {
             // short checkin version
             pw.println(uptime + "," + realtime);
@@ -8984,29 +9021,53 @@
             pw.println("Uptime: " + uptime + " Realtime: " + realtime);
         }
 
+        String[] innerArgs = new String[args.length-opti];
+        System.arraycopy(args, opti, innerArgs, 0, args.length-opti);
+
         ArrayList<MemItem> procMems = new ArrayList<MemItem>();
         long nativePss=0, dalvikPss=0, otherPss=0;
         long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
 
+        final int[] oomAdj = new int[] {
+            SYSTEM_ADJ, CORE_SERVER_ADJ, FOREGROUND_APP_ADJ,
+            VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ, HEAVY_WEIGHT_APP_ADJ,
+            BACKUP_APP_ADJ, SECONDARY_SERVER_ADJ, HOME_APP_ADJ, EMPTY_APP_ADJ
+        };
+        final String[] oomLabel = new String[] {
+                "System", "Persistent", "Foreground",
+                "Visible", "Perceptible", "Heavy Weight",
+                "Backup", "Services", "Home", "Background"
+        };
+        long oomPss[] = new long[oomLabel.length];
+
+        long totalPss = 0;
+
         for (int i = procs.size() - 1 ; i >= 0 ; i--) {
             ProcessRecord r = procs.get(i);
             if (r.thread != null) {
-                if (!isCheckinRequest) {
+                if (!isCheckinRequest && dumpAll) {
                     pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **");
                     pw.flush();
                 }
                 Debug.MemoryInfo mi = null;
-                try {
-                    mi = r.thread.dumpMemInfo(fd, args);
-                } catch (RemoteException e) {
-                    if (!isCheckinRequest) {
-                        pw.println("Got RemoteException!");
-                        pw.flush();
+                if (dumpAll) {
+                    try {
+                        mi = r.thread.dumpMemInfo(fd, isCheckinRequest, dumpAll, innerArgs);
+                    } catch (RemoteException e) {
+                        if (!isCheckinRequest) {
+                            pw.println("Got RemoteException!");
+                            pw.flush();
+                        }
                     }
+                } else {
+                    mi = new Debug.MemoryInfo();
+                    Debug.getMemoryInfo(r.pid, mi);
                 }
+
                 if (!isCheckinRequest && mi != null) {
-                    procMems.add(new MemItem(r.processName + " (pid " + r.pid + ")",
-                            mi.getTotalPss()));
+                    long myTotalPss = mi.getTotalPss();
+                    totalPss += myTotalPss;
+                    procMems.add(new MemItem(r.processName + " (pid " + r.pid + ")", myTotalPss));
 
                     nativePss += mi.nativePss;
                     dalvikPss += mi.dalvikPss;
@@ -9016,6 +9077,13 @@
                         miscPss[j] += mem;
                         otherPss -= mem;
                     }
+
+                    for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) {
+                        if (r.setAdj <= oomAdj[oomIndex] || oomIndex == (oomPss.length-1)) {
+                            oomPss[oomIndex] += myTotalPss;
+                            break;
+                        }
+                    }
                 }
             }
         }
@@ -9030,12 +9098,24 @@
                 catMems.add(new MemItem(Debug.MemoryInfo.getOtherLabel(j), miscPss[j]));
             }
 
+            ArrayList<MemItem> oomMems = new ArrayList<MemItem>();
+            for (int j=0; j<oomPss.length; j++) {
+                if (oomPss[j] != 0) {
+                    oomMems.add(new MemItem(oomLabel[j], oomPss[j]));
+                }
+            }
+
             pw.println();
             pw.println("Total PSS by process:");
-            dumpMemItems(pw, "  ", procMems);
+            dumpMemItems(pw, "  ", procMems, true);
+            pw.println();
+            pw.println("Total PSS by OOM adjustment:");
+            dumpMemItems(pw, "  ", oomMems, false);
             pw.println();
             pw.println("Total PSS by category:");
-            dumpMemItems(pw, "  ", catMems);
+            dumpMemItems(pw, "  ", catMems, true);
+            pw.println();
+            pw.print("Total PSS: "); pw.print(totalPss); pw.println(" Kb");
         }
     }