lmkd: reroute atoms logging to AMS

- Added new lmkd message for clients to subscribe LMK_ASYNC_EVENT_STAT
- Added support to write kill & mem stats information via data socket
  to be read & parsed on the AMS Java side for future logging to statsd

Bug: 184698933
Test: lmkd_unit_test - test check_for_oom tests lmkd message send to AMS
Test: statsd_testdrive 51 54 to inspect statsd logged atoms data
Change-Id: Id682a438c87b3e4503261d26461f6cee641d86c4
Merged-In: Id682a438c87b3e4503261d26461f6cee641d86c4
diff --git a/Android.bp b/Android.bp
index 267cd48..fcf26ca 100644
--- a/Android.bp
+++ b/Android.bp
@@ -18,11 +18,9 @@
         "liblog",
         "libprocessgroup",
         "libpsi",
-        "libstatssocket",
     ],
     static_libs: [
         "libstatslogc",
-        "libstatslog_lmkd",
         "liblmkd_utils",
     ],
     local_include_dirs: ["include"],
@@ -40,6 +38,7 @@
 cc_library_static {
     name: "libstatslogc",
     srcs: ["statslog.cpp"],
+    local_include_dirs: ["include"],
     cflags: [
         "-Wall",
         "-Werror",
@@ -49,37 +48,6 @@
     shared_libs: [
         "liblog",
     ],
-    static_libs: [
-        "libstatslog_lmkd",
-    ],
-}
-
-genrule {
-    name: "statslog_lmkd.h",
-    tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_lmkd.h --module lmkd --namespace android,lmkd,stats",
-    out: [
-        "statslog_lmkd.h",
-    ],
-}
-
-genrule {
-    name: "statslog_lmkd.cpp",
-    tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_lmkd.cpp --module lmkd --namespace android,lmkd,stats --importHeader statslog_lmkd.h",
-    out: [
-        "statslog_lmkd.cpp",
-    ],
-}
-
-cc_library_static {
-    name: "libstatslog_lmkd",
-    generated_sources: ["statslog_lmkd.cpp"],
-    generated_headers: ["statslog_lmkd.h"],
-    export_generated_headers: ["statslog_lmkd.h"],
-    shared_libs: [
-        "libstatssocket",
-    ],
 }
 
 cc_library_static {
diff --git a/include/lmkd.h b/include/lmkd.h
index ad5dc75..c814d17 100644
--- a/include/lmkd.h
+++ b/include/lmkd.h
@@ -27,14 +27,16 @@
  * Supported LMKD commands
  */
 enum lmk_cmd {
-    LMK_TARGET = 0, /* Associate minfree with oom_adj_score */
-    LMK_PROCPRIO,   /* Register a process and set its oom_adj_score */
-    LMK_PROCREMOVE, /* Unregister a process */
-    LMK_PROCPURGE,  /* Purge all registered processes */
-    LMK_GETKILLCNT, /* Get number of kills */
-    LMK_SUBSCRIBE,  /* Subscribe for asynchronous events */
-    LMK_PROCKILL,   /* Unsolicited msg to subscribed clients on proc kills */
-    LMK_UPDATE_PROPS, /* Reinit properties */
+    LMK_TARGET = 0,         /* Associate minfree with oom_adj_score */
+    LMK_PROCPRIO,           /* Register a process and set its oom_adj_score */
+    LMK_PROCREMOVE,         /* Unregister a process */
+    LMK_PROCPURGE,          /* Purge all registered processes */
+    LMK_GETKILLCNT,         /* Get number of kills */
+    LMK_SUBSCRIBE,          /* Subscribe for asynchronous events */
+    LMK_PROCKILL,           /* Unsolicited msg to subscribed clients on proc kills */
+    LMK_UPDATE_PROPS,       /* Reinit properties */
+    LMK_STAT_KILL_OCCURRED, /* Unsolicited msg to subscribed clients on proc kills for statsd log */
+    LMK_STAT_STATE_CHANGED, /* Unsolicited msg to subscribed clients on state changed */
 };
 
 /*
@@ -204,10 +206,11 @@
     return 2 * sizeof(int);
 }
 
-/* Types of asyncronous events sent from lmkd to its clients */
+/* Types of asynchronous events sent from lmkd to its clients */
 enum async_event_type {
     LMK_ASYNC_EVENT_FIRST,
     LMK_ASYNC_EVENT_KILL = LMK_ASYNC_EVENT_FIRST,
+    LMK_ASYNC_EVENT_STAT,
     LMK_ASYNC_EVENT_COUNT,
 };
 
diff --git a/lmkd.cpp b/lmkd.cpp
index e1c5276..dc93ac4 100644
--- a/lmkd.cpp
+++ b/lmkd.cpp
@@ -22,7 +22,6 @@
 #include <pwd.h>
 #include <sched.h>
 #include <signal.h>
-#include <statslog_lmkd.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
@@ -758,6 +757,49 @@
     }
 }
 
+/*
+ * Write the kill_stat/memory_stat over the data socket to be propagated via AMS to statsd
+ */
+static void stats_write_lmk_kill_occurred(struct kill_stat *kill_st,
+                                          struct memory_stat *mem_st) {
+    LMK_KILL_OCCURRED_PACKET packet;
+    const size_t len = lmkd_pack_set_kill_occurred(packet, kill_st, mem_st);
+    if (len == 0) {
+        return;
+    }
+
+    for (int i = 0; i < MAX_DATA_CONN; i++) {
+        if (data_sock[i].sock >= 0 && data_sock[i].async_event_mask & 1 << LMK_ASYNC_EVENT_STAT) {
+            ctrl_data_write(i, packet, len);
+        }
+    }
+
+}
+
+static void stats_write_lmk_kill_occurred_pid(int pid, struct kill_stat *kill_st,
+                                              struct memory_stat *mem_st) {
+    kill_st->taskname = stats_get_task_name(pid);
+    if (kill_st->taskname != NULL) {
+        stats_write_lmk_kill_occurred(kill_st, mem_st);
+    }
+}
+
+/*
+ * Write the state_changed over the data socket to be propagated via AMS to statsd
+ */
+static void stats_write_lmk_state_changed(enum lmk_state state) {
+    LMKD_CTRL_PACKET packet_state_changed;
+    const size_t len = lmkd_pack_set_state_changed(packet_state_changed, state);
+    if (len == 0) {
+        return;
+    }
+    for (int i = 0; i < MAX_DATA_CONN; i++) {
+        if (data_sock[i].sock >= 0 && data_sock[i].async_event_mask & 1 << LMK_ASYNC_EVENT_STAT) {
+            ctrl_data_write(i, (char*)packet_state_changed, len);
+        }
+    }
+}
+
 static void poll_kernel(int poll_fd) {
     if (poll_fd == -1) {
         // not waiting
@@ -2183,8 +2225,7 @@
             if (killed_size >= 0) {
                 if (!lmk_state_change_start) {
                     lmk_state_change_start = true;
-                    stats_write_lmk_state_changed(
-                            android::lmkd::stats::LMK_STATE_CHANGED__STATE__START);
+                    stats_write_lmk_state_changed(STATE_START);
                 }
                 break;
             }
@@ -2195,7 +2236,7 @@
     }
 
     if (lmk_state_change_start) {
-        stats_write_lmk_state_changed(android::lmkd::stats::LMK_STATE_CHANGED__STATE__STOP);
+        stats_write_lmk_state_changed(STATE_STOP);
     }
 
     return killed_size;
diff --git a/statslog.cpp b/statslog.cpp
index 5560b6b..ba39f54 100644
--- a/statslog.cpp
+++ b/statslog.cpp
@@ -24,21 +24,27 @@
 #include <string.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/endian.h>
+#include <sys/param.h>
 #include <sys/uio.h>
 #include <time.h>
 #include <unistd.h>
 
 #ifdef LMKD_LOG_STATS
 
-#define LINE_MAX 128
 #define STRINGIFY(x) STRINGIFY_INTERNAL(x)
 #define STRINGIFY_INTERNAL(x) #x
 
+/**
+ * Used to make sure that the payload is always smaller than LMKD_REPLY_MAX_SIZE
+ */
+#define BUILD_BUG_ON(cond) ((void)sizeof(char[1 - 2 * !!(cond)]))
+
 static bool enable_stats_log = property_get_bool("ro.lmk.log_stats", true);
 
 struct proc {
     int pid;
-    char taskname[LINE_MAX];
+    char taskname[MAX_TASKNAME_LEN];
     struct proc* pidhash_next;
 };
 
@@ -46,20 +52,6 @@
 static struct proc** pidhash = NULL;
 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
 
-/**
- * Logs the change in LMKD state which is used as start/stop boundaries for logging
- * LMK_KILL_OCCURRED event.
- * Code: LMK_STATE_CHANGED = 54
- */
-int
-stats_write_lmk_state_changed(int32_t state) {
-    if (enable_stats_log) {
-        return android::lmkd::stats::stats_write(android::lmkd::stats::LMK_STATE_CHANGED, state);
-    } else {
-        return -EINVAL;
-    }
-}
-
 static struct proc* pid_lookup(int pid) {
     struct proc* procp;
 
@@ -71,68 +63,11 @@
     return procp;
 }
 
-inline int32_t map_kill_reason(enum kill_reasons reason) {
-    switch (reason) {
-        case PRESSURE_AFTER_KILL:
-            return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__PRESSURE_AFTER_KILL;
-        case NOT_RESPONDING:
-            return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__NOT_RESPONDING;
-        case LOW_SWAP_AND_THRASHING:
-            return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__LOW_SWAP_AND_THRASHING;
-        case LOW_MEM_AND_SWAP:
-            return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_SWAP;
-        case LOW_MEM_AND_THRASHING:
-            return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_THRASHING;
-        case DIRECT_RECL_AND_THRASHING:
-            return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__DIRECT_RECL_AND_THRASHING;
-        case LOW_MEM_AND_SWAP_UTIL:
-            return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_SWAP_UTIL;
-        default:
-            return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__UNKNOWN;
-    }
-}
-
-/**
- * Logs the event when LMKD kills a process to reduce memory pressure.
- * Code: LMK_KILL_OCCURRED = 51
- */
-int stats_write_lmk_kill_occurred(struct kill_stat *kill_st, struct memory_stat *mem_st) {
-    if (enable_stats_log) {
-        return android::lmkd::stats::stats_write(
-                android::lmkd::stats::LMK_KILL_OCCURRED,
-                kill_st->uid,
-                kill_st->taskname,
-                kill_st->oom_score,
-                mem_st ? mem_st->pgfault : -1,
-                mem_st ? mem_st->pgmajfault : -1,
-                mem_st ? mem_st->rss_in_bytes : -1,
-                mem_st ? mem_st->cache_in_bytes : -1,
-                mem_st ? mem_st->swap_in_bytes : -1,
-                mem_st ? mem_st->process_start_time_ns : -1,
-                kill_st->min_oom_score,
-                kill_st->free_mem_kb,
-                kill_st->free_swap_kb,
-                map_kill_reason(kill_st->kill_reason)
-        );
-    } else {
-        return -EINVAL;
-    }
-}
-
-int stats_write_lmk_kill_occurred_pid(int pid, struct kill_stat *kill_st,
-                                      struct memory_stat* mem_st) {
-    struct proc* proc = pid_lookup(pid);
-    if (!proc) return -EINVAL;
-
-    kill_st->taskname = proc->taskname;
-    return stats_write_lmk_kill_occurred(kill_st, mem_st);
-}
-
 static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
-    char key[LINE_MAX + 1];
+    char key[MAX_TASKNAME_LEN + 1];
     int64_t value;
 
-    sscanf(line, "%" STRINGIFY(LINE_MAX) "s  %" SCNd64 "", key, &value);
+    sscanf(line, "%" STRINGIFY(MAX_TASKNAME_LEN) "s  %" SCNd64 "", key, &value);
 
     if (strcmp(key, "total_") < 0) {
         return;
@@ -275,8 +210,8 @@
     }
     procp = static_cast<struct proc*>(malloc(sizeof(struct proc)));
     procp->pid = pid;
-    strncpy(procp->taskname, taskname, LINE_MAX - 1);
-    procp->taskname[LINE_MAX - 1] = '\0';
+    strncpy(procp->taskname, taskname, MAX_TASKNAME_LEN - 1);
+    procp->taskname[MAX_TASKNAME_LEN - 1] = '\0';
     proc_insert(procp);
 }
 
@@ -299,4 +234,110 @@
     memset(pidhash, 0, PIDHASH_SZ * sizeof(*pidhash));
 }
 
+const char* stats_get_task_name(int pid) {
+    struct proc* proc = pid_lookup(pid);
+    return proc ? proc->taskname : NULL;
+}
+
+/**
+ * Writes int32 in a machine independent way
+ * https://docs.oracle.com/javase/7/docs/api/java/io/DataOutput.html#writeInt(int)
+ */
+static inline size_t pack_int32(LMK_KILL_OCCURRED_PACKET packet,
+                                size_t index,
+                                int32_t value) {
+    int32_t* int_buffer = (int32_t*)(packet + index);
+
+    *int_buffer = htonl(value);
+
+    return index + sizeof(int32_t);
+}
+
+/**
+ * Writes int64 in a machine independent way
+ * https://docs.oracle.com/javase/7/docs/api/java/io/DataOutput.html#writeLong(long)
+ */
+static inline size_t pack_int64(LMK_KILL_OCCURRED_PACKET packet,
+                                size_t index,
+                                int64_t value) {
+    int64_t* int64_buffer = (int64_t*)(packet + index);
+
+    *int64_buffer = htonq(value);
+
+    return index + sizeof(int64_t);
+}
+
+/**
+ * Writes ANSI string in a machine independent way
+ * https://docs.oracle.com/javase/7/docs/api/java/io/DataOutput.html#writeShort(int)
+ * 2 bytes str len following n chars
+ * to be read on the Java side with
+ * https://docs.oracle.com/javase/7/docs/api/java/io/DataInput.html#readUTF()
+ * Truncates the value string & packs up to MAX_TASKNAME_LEN - 1 chars
+ */
+static inline size_t pack_string(LMK_KILL_OCCURRED_PACKET packet,
+                                 size_t index,
+                                 const char* value) {
+    const size_t len_proc_name = MIN(strlen(value), MAX_TASKNAME_LEN - 1);
+    int16_t* short_buffer = (int16_t*)(packet + index);
+    *short_buffer = htons((int16_t)len_proc_name);
+
+    char* byte_buffer = (char*)(short_buffer + 1);
+    strncpy(byte_buffer, value, MAX_TASKNAME_LEN - 1);
+    byte_buffer[MAX_TASKNAME_LEN - 1] = '\0';
+
+    return index + sizeof(int16_t) + len_proc_name + 1;
+}
+
+size_t lmkd_pack_set_kill_occurred(LMK_KILL_OCCURRED_PACKET packet,
+                                   struct kill_stat *kill_stat,
+                                   struct memory_stat *mem_stat) {
+    BUILD_BUG_ON(sizeof(LMK_KILL_OCCURRED_PACKET) > LMKD_REPLY_MAX_SIZE);
+
+    if (!enable_stats_log) {
+        return 0;
+    }
+
+    int32_t index = 0;
+    index = pack_int32(packet, index, LMK_STAT_KILL_OCCURRED);
+
+    if (mem_stat) {
+        index = pack_int64(packet, index, mem_stat->pgfault);
+        index = pack_int64(packet, index, mem_stat->pgmajfault);
+        index = pack_int64(packet, index, mem_stat->rss_in_bytes);
+        index = pack_int64(packet, index, mem_stat->cache_in_bytes);
+        index = pack_int64(packet, index, mem_stat->swap_in_bytes);
+        index = pack_int64(packet, index, mem_stat->process_start_time_ns);
+    } else {
+        index = pack_int64(packet, index, -1);
+        index = pack_int64(packet, index, -1);
+        index = pack_int64(packet, index, -1);
+        index = pack_int64(packet, index, -1);
+        index = pack_int64(packet, index, -1);
+        index = pack_int64(packet, index, -1);
+    }
+
+    index = pack_int32(packet, index, kill_stat->uid);
+    index = pack_int32(packet, index, kill_stat->oom_score);
+    index = pack_int32(packet, index, kill_stat->min_oom_score);
+    index = pack_int32(packet, index, (int)kill_stat->free_mem_kb);
+    index = pack_int32(packet, index, (int)kill_stat->free_swap_kb);
+    index = pack_int32(packet, index, (int)kill_stat->kill_reason);
+
+    index = pack_string(packet, index, kill_stat->taskname);
+    return index;
+}
+
+size_t lmkd_pack_set_state_changed(LMKD_CTRL_PACKET packet,
+                                   enum lmk_state state) {
+    if (!enable_stats_log) {
+        return 0;
+    }
+
+    packet[0] = htonl(LMK_STAT_STATE_CHANGED);
+    packet[1] = htonl(state);
+
+    return 2 * sizeof(int);
+}
+
 #endif /* LMKD_LOG_STATS */
diff --git a/statslog.h b/statslog.h
index 628775f..e0859fa 100644
--- a/statslog.h
+++ b/statslog.h
@@ -17,9 +17,10 @@
 #ifndef _STATSLOG_H_
 #define _STATSLOG_H_
 
+#include <lmkd.h>
+
 #include <assert.h>
 #include <inttypes.h>
-#include <statslog_lmkd.h>
 #include <stdbool.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
@@ -28,6 +29,21 @@
 
 __BEGIN_DECLS
 
+#define MAX_TASKNAME_LEN 128
+
+/*
+ * Max LMKD reply packet length in bytes
+ * Notes about size calculation:
+ * 4 bytes for packet type
+ * 80 bytes for the LmkKillOccurred fields: memory_stat + kill_stat
+ * 2 bytes for process name string size
+ * MAX_TASKNAME_LEN bytes for the process name string
+ *
+ * Must be in sync with LmkdConnection.java
+ */
+#define LMKD_REPLY_MAX_SIZE 214
+
+/* LMK_MEMORY_STATS packet payload */
 struct memory_stat {
     int64_t pgfault;
     int64_t pgmajfault;
@@ -37,7 +53,7 @@
     int64_t process_start_time_ns;
 };
 
-// If you update this, also update the corresponding stats enum mapping.
+// If you update this, also update the corresponding stats enum mapping and LmkdStatsReporter.java
 enum kill_reasons {
     NONE = -1, /* To denote no kill condition */
     PRESSURE_AFTER_KILL = 0,
@@ -50,9 +66,10 @@
     KILL_REASON_COUNT
 };
 
+/* LMK_KILL_STAT packet payload */
 struct kill_stat {
     int32_t uid;
-    char *taskname;
+    const char *taskname;
     enum kill_reasons kill_reason;
     int32_t oom_score;
     int32_t min_oom_score;
@@ -60,6 +77,16 @@
     int64_t free_swap_kb;
 };
 
+/* LMKD reply packet to hold data for the LmkKillOccurred statsd atom */
+typedef char LMK_KILL_OCCURRED_PACKET[LMKD_REPLY_MAX_SIZE];
+
+// If you update this, also update the corresponding stats enum mapping.
+enum lmk_state {
+    STATE_UNKNOWN = 0,
+    STATE_START,
+    STATE_STOP,
+};
+
 #ifdef LMKD_LOG_STATS
 
 #define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%d/memory.stat"
@@ -68,25 +95,20 @@
 #define BYTES_IN_KILOBYTE 1024
 
 /**
- * Logs the change in LMKD state which is used as start/stop boundaries for logging
+ * Produces packet with the change in LMKD state which is used as start/stop boundaries for logging
  * LMK_KILL_OCCURRED event.
  * Code: LMK_STATE_CHANGED = 54
  */
-int
-stats_write_lmk_state_changed(int32_t state);
+size_t lmkd_pack_set_state_changed(LMKD_CTRL_PACKET packet,
+                                   enum lmk_state state);
 
 /**
- * Logs the event when LMKD kills a process to reduce memory pressure.
+ * Produces packet with the event when LMKD kills a process to reduce memory pressure.
  * Code: LMK_KILL_OCCURRED = 51
  */
-int stats_write_lmk_kill_occurred(struct kill_stat *kill_st, struct memory_stat *mem_st);
-
-/**
- * Logs the event when LMKD kills a process to reduce memory pressure.
- * Code: LMK_KILL_OCCURRED = 51
- */
-int stats_write_lmk_kill_occurred_pid(int pid, struct kill_stat *kill_st,
-                                      struct memory_stat* mem_st);
+size_t lmkd_pack_set_kill_occurred(LMK_KILL_OCCURRED_PACKET packet,
+                                   struct kill_stat *kill_st,
+                                   struct memory_stat *mem_st);
 
 /**
  * Reads memory stats used to log the statsd atom. Returns non-null ptr on success.
@@ -109,19 +131,19 @@
  */
 void stats_remove_taskname(int pid);
 
+const char* stats_get_task_name(int pid);
+
 #else /* LMKD_LOG_STATS */
 
-static inline int
-stats_write_lmk_state_changed(int32_t state __unused) { return -EINVAL; }
-
-static inline int
-stats_write_lmk_kill_occurred(struct kill_stat *kill_st __unused,
-                              struct memory_stat *mem_st __unused) {
+static inline size_t
+lmkd_pack_set_state_changed(LMKD_CTRL_PACKET packet __unused, enum lmk_state state __unused) {
     return -EINVAL;
 }
 
-int stats_write_lmk_kill_occurred_pid(int pid __unused, struct kill_stat *kill_st __unused,
-                                      struct memory_stat* mem_st __unused) {
+static inline size_t
+lmkd_pack_set_kill_occurred(LMK_KILL_OCCURRED_PACKET packet __unused,
+                            struct kill_stat *kill_st __unused,
+                            struct memory_stat *mem_st __unused) {
     return -EINVAL;
 }
 
@@ -137,6 +159,8 @@
 
 static inline void stats_remove_taskname(int pid __unused) {}
 
+static inline const char* stats_get_task_name(int pid __unused) { return NULL; }
+
 #endif /* LMKD_LOG_STATS */
 
 __END_DECLS