Record process start time for native processes

Process start time is recorded to detect whether two memory samples
come from the same process and how long the process was alive.

Bug: 118249210
Test: atest MemoryStatUtilTest and manually verified that data is in
statsd report

Change-Id: I7f49cd8bfc81c5e7e70e4f8b49729632eeec5798
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 2f69ac1..bce1820 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -2404,25 +2404,28 @@
  * Logs the memory stats for a native process (from procfs).
  */
 message NativeProcessMemoryState {
-  // The uid if available. -1 means not available.
-  optional int32 uid = 1 [(is_uid) = true];
+    // The uid if available. -1 means not available.
+    optional int32 uid = 1 [(is_uid) = true];
 
-  // The process name.
-  optional string process_name = 2;
+    // The process name.
+    optional string process_name = 2;
 
-  // # of page-faults
-  optional int64 page_fault = 3;
+    // # of page-faults
+    optional int64 page_fault = 3;
 
-  // # of major page-faults
-  optional int64 page_major_fault = 4;
+    // # of major page-faults
+    optional int64 page_major_fault = 4;
 
-  // RSS
-  optional int64 rss_in_bytes = 5;
+    // RSS
+    optional int64 rss_in_bytes = 5;
 
-  // RSS high watermark.
-  // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or
-  // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups.
-  optional int64 rss_high_watermark_in_bytes = 6;
+    // RSS high watermark.
+    // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status.
+    optional int64 rss_high_watermark_in_bytes = 6;
+
+    // Elapsed real time when the process started.
+    // Value is read from /proc/PID/stat, field 22.
+    optional int64 start_time_nanos = 7;
 }
 
 /*
@@ -3117,4 +3120,4 @@
     optional int64 user_time_millis = 3;
     // Process cpu time in system space, cumulative from boot/process start
     optional int64 system_time_millis = 4;
-}
\ No newline at end of file
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index ba626f8..73a88c1 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -172,7 +172,7 @@
           new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
         // native_process_memory_state
         {android::util::NATIVE_PROCESS_MEMORY_STATE,
-         {{3, 4, 5, 6},
+         {{3, 4, 5, 6, 7},
           {2},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java
index 9bfdae0..d149243 100644
--- a/core/java/android/app/ProcessMemoryState.java
+++ b/core/java/android/app/ProcessMemoryState.java
@@ -33,10 +33,11 @@
     public final long cacheInBytes;
     public final long swapInBytes;
     public final long rssHighWatermarkInBytes;
+    public final long startTimeNanos;
 
     public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault,
                               long pgmajfault, long rssInBytes, long cacheInBytes,
-                              long swapInBytes, long rssHighWatermarkInBytes) {
+                              long swapInBytes, long rssHighWatermarkInBytes, long startTimeNanos) {
         this.uid = uid;
         this.processName = processName;
         this.oomScore = oomScore;
@@ -46,6 +47,7 @@
         this.cacheInBytes = cacheInBytes;
         this.swapInBytes = swapInBytes;
         this.rssHighWatermarkInBytes = rssHighWatermarkInBytes;
+        this.startTimeNanos = startTimeNanos;
     }
 
     private ProcessMemoryState(Parcel in) {
@@ -58,6 +60,7 @@
         cacheInBytes = in.readLong();
         swapInBytes = in.readLong();
         rssHighWatermarkInBytes = in.readLong();
+        startTimeNanos = in.readLong();
     }
 
     public static final Creator<ProcessMemoryState> CREATOR = new Creator<ProcessMemoryState>() {
@@ -88,5 +91,6 @@
         parcel.writeLong(cacheInBytes);
         parcel.writeLong(swapInBytes);
         parcel.writeLong(rssHighWatermarkInBytes);
+        parcel.writeLong(startTimeNanos);
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 88558b4..a0d46c6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18620,7 +18620,8 @@
                                     memoryStat.rssInBytes,
                                     memoryStat.cacheInBytes,
                                     memoryStat.swapInBytes,
-                                    memoryStat.rssHighWatermarkInBytes);
+                                    memoryStat.rssHighWatermarkInBytes,
+                                    memoryStat.startTimeNanos);
                     processMemoryStates.add(processMemoryState);
                 }
             }
@@ -18643,7 +18644,7 @@
                 ProcessMemoryState processMemoryState = new ProcessMemoryState(uid, processName,
                         oomScore, memoryStat.pgfault, memoryStat.pgmajfault,
                         memoryStat.rssInBytes, memoryStat.cacheInBytes, memoryStat.swapInBytes,
-                        memoryStat.rssHighWatermarkInBytes);
+                        memoryStat.rssHighWatermarkInBytes, memoryStat.startTimeNanos);
                 processMemoryStates.add(processMemoryState);
             }
             return processMemoryStates;
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index 2c4b338..f77be5b 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -23,6 +23,8 @@
 import android.annotation.Nullable;
 import android.os.FileUtils;
 import android.os.SystemProperties;
+import android.system.Os;
+import android.system.OsConstants;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -71,6 +73,7 @@
 
     static final int BYTES_IN_KILOBYTE = 1024;
     static final int PAGE_SIZE = 4096;
+    static final long JIFFY_NANOS = 1_000_000_000 / Os.sysconf(OsConstants._SC_CLK_TCK);
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
 
@@ -103,6 +106,7 @@
 
     private static final int PGFAULT_INDEX = 9;
     private static final int PGMAJFAULT_INDEX = 11;
+    private static final int START_TIME_INDEX = 21;
     private static final int RSS_IN_PAGES_INDEX = 23;
 
     private MemoryStatUtil() {}
@@ -238,6 +242,7 @@
             memoryStat.pgfault = Long.parseLong(splits[PGFAULT_INDEX]);
             memoryStat.pgmajfault = Long.parseLong(splits[PGMAJFAULT_INDEX]);
             memoryStat.rssInBytes = Long.parseLong(splits[RSS_IN_PAGES_INDEX]) * PAGE_SIZE;
+            memoryStat.startTimeNanos = Long.parseLong(splits[START_TIME_INDEX]) * JIFFY_NANOS;
             return memoryStat;
         } catch (NumberFormatException e) {
             Slog.e(TAG, "Failed to parse value", e);
@@ -279,5 +284,7 @@
         long swapInBytes;
         /** Number of bytes of peak anonymous and swap cache memory */
         long rssHighWatermarkInBytes;
+        /** Device time when the processes started. */
+        long startTimeNanos;
     }
 }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 5600749..1948706 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1017,6 +1017,7 @@
             e.writeLong(processMemoryState.pgmajfault);
             e.writeLong(processMemoryState.rssInBytes);
             e.writeLong(processMemoryState.rssHighWatermarkInBytes);
+            e.writeLong(processMemoryState.startTimeNanos);
             pulledData.add(e);
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index 9a283fe..72c5b10 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static com.android.server.am.MemoryStatUtil.BYTES_IN_KILOBYTE;
+import static com.android.server.am.MemoryStatUtil.JIFFY_NANOS;
 import static com.android.server.am.MemoryStatUtil.MemoryStat;
 import static com.android.server.am.MemoryStatUtil.PAGE_SIZE;
 import static com.android.server.am.MemoryStatUtil.parseMemoryMaxUsageFromMemCg;
@@ -96,7 +97,7 @@
             "-2",
             "117",
             "0",
-            "2206",
+            "2222", // this in start time (in ticks per second)
             "1257177088",
             "3", // this is RSS (number of pages)
             "4294967295",
@@ -219,6 +220,7 @@
         assertEquals(3 * PAGE_SIZE, stat.rssInBytes);
         assertEquals(0, stat.cacheInBytes);
         assertEquals(0, stat.swapInBytes);
+        assertEquals(2222 * JIFFY_NANOS, stat.startTimeNanos);
     }
 
     @Test