OSDN Git Service

incidentd: dumping native process mem info to proto.
authorKweku Adams <kwekua@google.com>
Fri, 3 Nov 2017 00:12:20 +0000 (17:12 -0700)
committerKweku Adams <kwekua@google.com>
Thu, 30 Nov 2017 19:48:38 +0000 (11:48 -0800)
Bug: 65750823
Test: flash device and check proto dump output.
Change-Id: Ic584f78153fbbb507cdd41e699a541e3351467bb

core/java/android/app/ActivityThread.java
core/proto/android/os/incident.proto
core/proto/android/server/activitymanagerservice.proto
services/core/java/com/android/server/am/ActivityManagerService.java

index ffd012d..b84833b 100644 (file)
@@ -108,6 +108,7 @@ import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseIntArray;
 import android.util.SuperNotCalledException;
+import android.util.proto.ProtoOutputStream;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.ThreadedRenderer;
@@ -131,6 +132,7 @@ import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.org.conscrypt.OpenSSLSocketImpl;
 import com.android.org.conscrypt.TrustedCertificateStore;
+import com.android.server.am.proto.MemInfoProto;
 
 import dalvik.system.BaseDexClassLoader;
 import dalvik.system.CloseGuard;
@@ -2259,6 +2261,167 @@ public final class ActivityThread extends ClientTransactionHandler {
         }
     }
 
+    /**
+     * Dump heap info to proto.
+     *
+     * @param hasSwappedOutPss determines whether to use dirtySwap or dirtySwapPss
+     */
+    private static void dumpHeap(ProtoOutputStream proto, long fieldId, String name,
+            int pss, int cleanPss, int sharedDirty, int privateDirty,
+            int sharedClean, int privateClean,
+            boolean hasSwappedOutPss, int dirtySwap, int dirtySwapPss) {
+        final long token = proto.start(fieldId);
+
+        proto.write(MemInfoProto.NativeProcess.MemoryInfo.NAME, name);
+        proto.write(MemInfoProto.NativeProcess.MemoryInfo.TOTAL_PSS_KB, pss);
+        proto.write(MemInfoProto.NativeProcess.MemoryInfo.CLEAN_PSS_KB, cleanPss);
+        proto.write(MemInfoProto.NativeProcess.MemoryInfo.SHARED_DIRTY_KB, sharedDirty);
+        proto.write(MemInfoProto.NativeProcess.MemoryInfo.PRIVATE_DIRTY_KB, privateDirty);
+        proto.write(MemInfoProto.NativeProcess.MemoryInfo.SHARED_CLEAN_KB, sharedClean);
+        proto.write(MemInfoProto.NativeProcess.MemoryInfo.PRIVATE_CLEAN_KB, privateClean);
+        if (hasSwappedOutPss) {
+            proto.write(MemInfoProto.NativeProcess.MemoryInfo.DIRTY_SWAP_PSS_KB, dirtySwapPss);
+        } else {
+            proto.write(MemInfoProto.NativeProcess.MemoryInfo.DIRTY_SWAP_KB, dirtySwap);
+        }
+
+        proto.end(token);
+    }
+
+    /**
+     * Dump mem info data to proto.
+     */
+    public static void dumpMemInfoTable(ProtoOutputStream proto, Debug.MemoryInfo memInfo,
+            boolean dumpDalvik, boolean dumpSummaryOnly,
+            long nativeMax, long nativeAllocated, long nativeFree,
+            long dalvikMax, long dalvikAllocated, long dalvikFree) {
+
+        if (!dumpSummaryOnly) {
+            final long nhToken = proto.start(MemInfoProto.NativeProcess.NATIVE_HEAP);
+            dumpHeap(proto, MemInfoProto.NativeProcess.HeapInfo.MEM_INFO, "Native Heap",
+                    memInfo.nativePss, memInfo.nativeSwappablePss, memInfo.nativeSharedDirty,
+                    memInfo.nativePrivateDirty, memInfo.nativeSharedClean,
+                    memInfo.nativePrivateClean, memInfo.hasSwappedOutPss,
+                    memInfo.nativeSwappedOut, memInfo.nativeSwappedOutPss);
+            proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_SIZE_KB, nativeMax);
+            proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_ALLOC_KB, nativeAllocated);
+            proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_FREE_KB, nativeFree);
+            proto.end(nhToken);
+
+            final long dvToken = proto.start(MemInfoProto.NativeProcess.DALVIK_HEAP);
+            dumpHeap(proto, MemInfoProto.NativeProcess.HeapInfo.MEM_INFO, "Dalvik Heap",
+                    memInfo.dalvikPss, memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty,
+                    memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean,
+                    memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss,
+                    memInfo.dalvikSwappedOut, memInfo.dalvikSwappedOutPss);
+            proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_SIZE_KB, dalvikMax);
+            proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_ALLOC_KB, dalvikAllocated);
+            proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_FREE_KB, dalvikFree);
+            proto.end(dvToken);
+
+            int otherPss = memInfo.otherPss;
+            int otherSwappablePss = memInfo.otherSwappablePss;
+            int otherSharedDirty = memInfo.otherSharedDirty;
+            int otherPrivateDirty = memInfo.otherPrivateDirty;
+            int otherSharedClean = memInfo.otherSharedClean;
+            int otherPrivateClean = memInfo.otherPrivateClean;
+            int otherSwappedOut = memInfo.otherSwappedOut;
+            int otherSwappedOutPss = memInfo.otherSwappedOutPss;
+
+            for (int i = 0; i < Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
+                final int myPss = memInfo.getOtherPss(i);
+                final int mySwappablePss = memInfo.getOtherSwappablePss(i);
+                final int mySharedDirty = memInfo.getOtherSharedDirty(i);
+                final int myPrivateDirty = memInfo.getOtherPrivateDirty(i);
+                final int mySharedClean = memInfo.getOtherSharedClean(i);
+                final int myPrivateClean = memInfo.getOtherPrivateClean(i);
+                final int mySwappedOut = memInfo.getOtherSwappedOut(i);
+                final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i);
+                if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
+                        || mySharedClean != 0 || myPrivateClean != 0
+                        || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
+                    dumpHeap(proto, MemInfoProto.NativeProcess.OTHER_HEAPS,
+                            Debug.MemoryInfo.getOtherLabel(i),
+                            myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
+                            mySharedClean, myPrivateClean,
+                            memInfo.hasSwappedOutPss, mySwappedOut, mySwappedOutPss);
+
+                    otherPss -= myPss;
+                    otherSwappablePss -= mySwappablePss;
+                    otherSharedDirty -= mySharedDirty;
+                    otherPrivateDirty -= myPrivateDirty;
+                    otherSharedClean -= mySharedClean;
+                    otherPrivateClean -= myPrivateClean;
+                    otherSwappedOut -= mySwappedOut;
+                    otherSwappedOutPss -= mySwappedOutPss;
+                }
+            }
+
+            dumpHeap(proto, MemInfoProto.NativeProcess.UNKNOWN_HEAP, "Unknown",
+                    otherPss, otherSwappablePss,
+                    otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean,
+                    memInfo.hasSwappedOutPss, otherSwappedOut, otherSwappedOutPss);
+            final long tToken = proto.start(MemInfoProto.NativeProcess.TOTAL_HEAP);
+            dumpHeap(proto, MemInfoProto.NativeProcess.HeapInfo.MEM_INFO, "TOTAL",
+                    memInfo.getTotalPss(), memInfo.getTotalSwappablePss(),
+                    memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
+                    memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
+                    memInfo.hasSwappedOutPss, memInfo.getTotalSwappedOut(),
+                    memInfo.getTotalSwappedOutPss());
+            proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_SIZE_KB, nativeMax + dalvikMax);
+            proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_ALLOC_KB,
+                    nativeAllocated + dalvikAllocated);
+            proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_FREE_KB, nativeFree + dalvikFree);
+            proto.end(tToken);
+
+            if (dumpDalvik) {
+                for (int i = Debug.MemoryInfo.NUM_OTHER_STATS;
+                        i < Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_STATS;
+                        i++) {
+                    final int myPss = memInfo.getOtherPss(i);
+                    final int mySwappablePss = memInfo.getOtherSwappablePss(i);
+                    final int mySharedDirty = memInfo.getOtherSharedDirty(i);
+                    final int myPrivateDirty = memInfo.getOtherPrivateDirty(i);
+                    final int mySharedClean = memInfo.getOtherSharedClean(i);
+                    final int myPrivateClean = memInfo.getOtherPrivateClean(i);
+                    final int mySwappedOut = memInfo.getOtherSwappedOut(i);
+                    final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i);
+                    if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
+                            || mySharedClean != 0 || myPrivateClean != 0
+                            || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
+                        dumpHeap(proto, MemInfoProto.NativeProcess.DALVIK_DETAILS,
+                                Debug.MemoryInfo.getOtherLabel(i),
+                                myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
+                                mySharedClean, myPrivateClean,
+                                memInfo.hasSwappedOutPss, mySwappedOut, mySwappedOutPss);
+                    }
+                }
+            }
+        }
+
+        final long asToken = proto.start(MemInfoProto.NativeProcess.APP_SUMMARY);
+        proto.write(MemInfoProto.NativeProcess.AppSummary.JAVA_HEAP_PSS_KB,
+                memInfo.getSummaryJavaHeap());
+        proto.write(MemInfoProto.NativeProcess.AppSummary.NATIVE_HEAP_PSS_KB,
+                memInfo.getSummaryNativeHeap());
+        proto.write(MemInfoProto.NativeProcess.AppSummary.CODE_PSS_KB, memInfo.getSummaryCode());
+        proto.write(MemInfoProto.NativeProcess.AppSummary.STACK_PSS_KB, memInfo.getSummaryStack());
+        proto.write(MemInfoProto.NativeProcess.AppSummary.GRAPHICS_PSS_KB,
+                memInfo.getSummaryGraphics());
+        proto.write(MemInfoProto.NativeProcess.AppSummary.PRIVATE_OTHER_PSS_KB,
+                memInfo.getSummaryPrivateOther());
+        proto.write(MemInfoProto.NativeProcess.AppSummary.SYSTEM_PSS_KB,
+                memInfo.getSummarySystem());
+        if (memInfo.hasSwappedOutPss) {
+            proto.write(MemInfoProto.NativeProcess.AppSummary.TOTAL_SWAP_PSS,
+                    memInfo.getSummaryTotalSwapPss());
+        } else {
+            proto.write(MemInfoProto.NativeProcess.AppSummary.TOTAL_SWAP_PSS,
+                    memInfo.getSummaryTotalSwap());
+        }
+        proto.end(asToken);
+    }
+
     public void registerOnActivityPausedListener(Activity activity,
             OnActivityPausedListener listener) {
         synchronized (mOnPauseListeners) {
index ecdabcf..73a9fbb 100644 (file)
@@ -164,4 +164,9 @@ message IncidentProto {
         (section).type = SECTION_DUMPSYS,
         (section).args = "window --proto"
     ];
+
+    optional com.android.server.am.proto.MemInfoProto meminfo = 3018 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "meminfo --proto"
+    ];
 }
index 889842c..3af6f51 100644 (file)
 
 syntax = "proto2";
 
+package com.android.server.am.proto;
+
 import "frameworks/base/core/proto/android/app/notification.proto";
 import "frameworks/base/core/proto/android/content/intent.proto";
-import "frameworks/base/core/proto/android/server/intentresolver.proto";
-import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
 import "frameworks/base/core/proto/android/graphics/rect.proto";
 import "frameworks/base/core/proto/android/os/looper.proto";
+import "frameworks/base/core/proto/android/server/intentresolver.proto";
+import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
 import "frameworks/base/core/proto/android/util/common.proto";
 
-package com.android.server.am.proto;
-
 option java_multiple_files = true;
 
 message ActivityManagerServiceProto {
@@ -159,6 +159,69 @@ message BroadcastQueueProto {
   repeated BroadcastSummary historical_broadcasts_summary = 6;
 }
 
+message MemInfoProto {
+  optional int64 uptime_duration_ms = 1;
+  optional int64 elapsed_realtime_ms = 2;
+
+  message NativeProcess {
+    optional int32 pid = 1;
+    optional string process_name = 2;
+
+    message MemoryInfo {
+      optional string name = 1;
+      // The proportional set size for the heap.
+      optional int32 total_pss_kb = 2;
+      // The proportional set size that is swappable for the heap.
+      optional int32 clean_pss_kb = 3;
+      // The private dirty pages used by the heap.
+      optional int32 shared_dirty_kb = 4;
+      // The shared dirty pages used by the heap.
+      optional int32 private_dirty_kb = 5;
+      // The shared clean pages used by the heap.
+      optional int32 shared_clean_kb = 6;
+      // The private clean pages used by the heap.
+      optional int32 private_clean_kb = 7;
+      oneof dirty_swap {
+        // The dirty the pages that have been swapped out.
+        int32 dirty_swap_kb = 8;
+        // The dirty the pages that have been swapped out, proportional.
+        int32 dirty_swap_pss_kb = 9;
+      }
+    }
+    message HeapInfo {
+      optional MemoryInfo mem_info = 1;
+      optional int32 heap_size_kb = 2;
+      optional int32 heap_alloc_kb = 3;
+      optional int32 heap_free_kb = 4;
+    }
+    optional HeapInfo native_heap = 3;
+    optional HeapInfo dalvik_heap = 4;
+    repeated MemoryInfo other_heaps = 5;
+    optional HeapInfo unknown_heap = 6;
+    // Summation of native_heap, dalvik_heap, and other_heaps.
+    optional HeapInfo total_heap = 7;
+
+    repeated MemoryInfo dalvik_details = 8;
+
+    message AppSummary {
+      optional int32 java_heap_pss_kb = 1;
+      optional int32 native_heap_pss_kb = 2;
+      optional int32 code_pss_kb = 3;
+      optional int32 stack_pss_kb = 4;
+      optional int32 graphics_pss_kb = 5;
+      optional int32 private_other_pss_kb = 6;
+      optional int32 system_pss_kb = 7;
+
+      oneof total_swap {
+        int32 total_swap_pss = 8;
+        int32 total_swap_kb = 9;
+      }
+    }
+    optional AppSummary app_summary = 9;
+  }
+  repeated NativeProcess native_processes = 3;
+}
+
 message StickyBroadcastProto {
   optional int32 user = 1;
 
index fe992da..e6d7047 100644 (file)
@@ -417,6 +417,7 @@ import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.am.proto.ActivityManagerServiceProto;
 import com.android.server.am.proto.BroadcastProto;
 import com.android.server.am.proto.GrantUriProto;
+import com.android.server.am.proto.MemInfoProto;
 import com.android.server.am.proto.NeededUriGrantsProto;
 import com.android.server.am.proto.StickyBroadcastProto;
 import com.android.server.firewall.IntentFirewall;
@@ -2569,14 +2570,13 @@ public class ActivityManagerService extends IActivityManager.Stub
             @Override
             public void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args,
                     boolean asProto) {
-                if (asProto) return;
-                mActivityManagerService.dumpApplicationMemoryUsage(fd,
-                        pw, "  ", new String[] {"-a"}, false, null);
+                dump(fd, pw, new String[] {"-a"}, asProto);
             }
+
             @Override
             public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
-                if (asProto) return;
-                mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, "  ", args, false, null);
+                mActivityManagerService.dumpApplicationMemoryUsage(
+                        fd, pw, "  ", args, false, null, asProto);
             }
         };
 
@@ -17207,8 +17207,8 @@ public class ActivityManagerService extends IActivityManager.Stub
         boolean dumpProto;
     }
 
-    final void dumpApplicationMemoryUsage(FileDescriptor fd,
-            PrintWriter pw, String prefix, String[] args, boolean brief, PrintWriter categoryPw) {
+    final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
+            String[] args, boolean brief, PrintWriter categoryPw, boolean asProto) {
         MemoryUsageDumpOptions opts = new MemoryUsageDumpOptions();
         opts.dumpDetails = false;
         opts.dumpFullDetails = false;
@@ -17221,7 +17221,7 @@ public class ActivityManagerService extends IActivityManager.Stub
         opts.packages = false;
         opts.isCheckinRequest = false;
         opts.dumpSwapPss = false;
-        opts.dumpProto = false;
+        opts.dumpProto = asProto;
 
         int opti = 0;
         while (opti < args.length) {
@@ -17289,7 +17289,7 @@ public class ActivityManagerService extends IActivityManager.Stub
         }
     }
 
-    final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
+    private final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
             MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief,
             ArrayList<ProcessRecord> procs, PrintWriter categoryPw) {
         long uptime = SystemClock.uptimeMillis();
@@ -17298,54 +17298,59 @@ public class ActivityManagerService extends IActivityManager.Stub
 
         if (procs == null) {
             // No Java processes.  Maybe they want to print a native process.
-            if (innerArgs.length > 0 && innerArgs[0].charAt(0) != '-') {
-                ArrayList<ProcessCpuTracker.Stats> nativeProcs
-                        = new ArrayList<ProcessCpuTracker.Stats>();
-                updateCpuStatsNow();
-                int findPid = -1;
-                try {
-                    findPid = Integer.parseInt(innerArgs[0]);
-                } catch (NumberFormatException e) {
-                }
-                synchronized (mProcessCpuTracker) {
-                    final int N = mProcessCpuTracker.countStats();
-                    for (int i=0; i<N; i++) {
-                        ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
-                        if (st.pid == findPid || (st.baseName != null
-                                && st.baseName.equals(innerArgs[0]))) {
-                            nativeProcs.add(st);
-                        }
+            String proc = "N/A";
+            if (innerArgs.length > 0) {
+                proc = innerArgs[0];
+                if (proc.charAt(0) != '-') {
+                    ArrayList<ProcessCpuTracker.Stats> nativeProcs
+                            = new ArrayList<ProcessCpuTracker.Stats>();
+                    updateCpuStatsNow();
+                    int findPid = -1;
+                    try {
+                        findPid = Integer.parseInt(innerArgs[0]);
+                    } catch (NumberFormatException e) {
                     }
-                }
-                if (nativeProcs.size() > 0) {
-                    dumpApplicationMemoryUsageHeader(pw, uptime, realtime, opts.isCheckinRequest,
-                            opts.isCompact);
-                    Debug.MemoryInfo mi = null;
-                    for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) {
-                        final ProcessCpuTracker.Stats r = nativeProcs.get(i);
-                        final int pid = r.pid;
-                        if (!opts.isCheckinRequest && opts.dumpDetails) {
-                            pw.println("\n** MEMINFO in pid " + pid + " [" + r.baseName + "] **");
-                        }
-                        if (mi == null) {
-                            mi = new Debug.MemoryInfo();
-                        }
-                        if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
-                            Debug.getMemoryInfo(pid, mi);
-                        } else {
-                            mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
-                            mi.dalvikPrivateDirty = (int)tmpLong[0];
+                    synchronized (mProcessCpuTracker) {
+                        final int N = mProcessCpuTracker.countStats();
+                        for (int i=0; i<N; i++) {
+                            ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
+                            if (st.pid == findPid || (st.baseName != null
+                                    && st.baseName.equals(innerArgs[0]))) {
+                                nativeProcs.add(st);
+                            }
                         }
-                        ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest, opts.dumpFullDetails,
-                                opts.dumpDalvik, opts.dumpSummaryOnly, pid, r.baseName, 0, 0, 0, 0, 0, 0);
-                        if (opts.isCheckinRequest) {
-                            pw.println();
+                    }
+                    if (nativeProcs.size() > 0) {
+                        dumpApplicationMemoryUsageHeader(pw, uptime, realtime,
+                                opts.isCheckinRequest, opts.isCompact);
+                        Debug.MemoryInfo mi = null;
+                        for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) {
+                            final ProcessCpuTracker.Stats r = nativeProcs.get(i);
+                            final int pid = r.pid;
+                            if (!opts.isCheckinRequest && opts.dumpDetails) {
+                                pw.println("\n** MEMINFO in pid " + pid + " [" + r.baseName + "] **");
+                            }
+                            if (mi == null) {
+                                mi = new Debug.MemoryInfo();
+                            }
+                            if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
+                                Debug.getMemoryInfo(pid, mi);
+                            } else {
+                                mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
+                                mi.dalvikPrivateDirty = (int)tmpLong[0];
+                            }
+                            ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest,
+                                    opts.dumpFullDetails, opts.dumpDalvik, opts.dumpSummaryOnly,
+                                    pid, r.baseName, 0, 0, 0, 0, 0, 0);
+                            if (opts.isCheckinRequest) {
+                                pw.println();
+                            }
                         }
+                        return;
                     }
-                    return;
                 }
             }
-            pw.println("No process found for: " + innerArgs[0]);
+            pw.println("No process found for: " + proc);
             return;
         }
 
@@ -17768,15 +17773,77 @@ public class ActivityManagerService extends IActivityManager.Stub
         }
     }
 
-    final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw,
+    private final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw,
             MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief,
             ArrayList<ProcessRecord> procs) {
-        ProtoOutputStream proto = new ProtoOutputStream(fd);
+        final long uptimeMs = SystemClock.uptimeMillis();
+        final long realtimeMs = SystemClock.elapsedRealtime();
+        final long[] tmpLong = new long[1];
+
+        if (procs == null) {
+            // No Java processes.  Maybe they want to print a native process.
+            String proc = "N/A";
+            if (innerArgs.length > 0) {
+                proc = innerArgs[0];
+                if (proc.charAt(0) != '-') {
+                    ArrayList<ProcessCpuTracker.Stats> nativeProcs
+                            = new ArrayList<ProcessCpuTracker.Stats>();
+                    updateCpuStatsNow();
+                    int findPid = -1;
+                    try {
+                        findPid = Integer.parseInt(innerArgs[0]);
+                    } catch (NumberFormatException e) {
+                    }
+                    synchronized (mProcessCpuTracker) {
+                        final int N = mProcessCpuTracker.countStats();
+                        for (int i=0; i<N; i++) {
+                            ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
+                            if (st.pid == findPid || (st.baseName != null
+                                    && st.baseName.equals(innerArgs[0]))) {
+                                nativeProcs.add(st);
+                            }
+                        }
+                    }
+                    if (nativeProcs.size() > 0) {
+                        ProtoOutputStream proto = new ProtoOutputStream(fd);
 
-        // TODO: implement
-        pw.println("Not yet implemented. Have a cookie instead! :]");
+                        proto.write(MemInfoProto.UPTIME_DURATION_MS, uptimeMs);
+                        proto.write(MemInfoProto.ELAPSED_REALTIME_MS, realtimeMs);
+                        Debug.MemoryInfo mi = null;
+                        for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) {
+                            final ProcessCpuTracker.Stats r = nativeProcs.get(i);
+                            final int pid = r.pid;
+                            final long nToken = proto.start(MemInfoProto.NATIVE_PROCESSES);
+
+                            proto.write(MemInfoProto.NativeProcess.PID, pid);
+                            proto.write(MemInfoProto.NativeProcess.PROCESS_NAME, r.baseName);
+
+                            if (mi == null) {
+                                mi = new Debug.MemoryInfo();
+                            }
+                            if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
+                                Debug.getMemoryInfo(pid, mi);
+                            } else {
+                                mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
+                                mi.dalvikPrivateDirty = (int)tmpLong[0];
+                            }
+                            ActivityThread.dumpMemInfoTable(proto, mi, opts.dumpDalvik,
+                                    opts.dumpSummaryOnly, 0, 0, 0, 0, 0, 0);
+
+                            proto.end(nToken);
+                        }
+
+                        proto.flush();
+                        return;
+                    }
+                }
+            }
+            Log.d(TAG, "No process found for: " + innerArgs[0]);
+            return;
+        }
 
-        proto.flush();
+        // TODO: finish
+        pw.println("Java processes aren't implemented yet. Have a coffee instead! :]");
     }
 
     private void appendBasicMemEntry(StringBuilder sb, int oomAdj, int procState, long pss,