lmkd: Isolate statslog related code from lmkd code

Move statsd related code out of lmkd.c to minimize ifdefs sprinkled around
the code and make it more maintainable.

Bug: 74119935
Test: lmkd_unit_test
Change-Id: Ib22f90fd380b9a31e09ab18ef16787bc07415ddf
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 4c5ca03..e8e125b 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -1,3 +1,15 @@
+cc_defaults {
+    name: "stats_defaults",
+
+    product_variables: {
+        use_lmkd_stats_log: {
+            cflags: [
+                "-DLMKD_LOG_STATS"
+            ],
+        },
+    },
+}
+
 cc_binary {
     name: "lmkd",
 
@@ -15,13 +27,7 @@
     local_include_dirs: ["include"],
     cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
     init_rc: ["lmkd.rc"],
-    product_variables: {
-        use_lmkd_stats_log: {
-            cflags: [
-                "-DLMKD_LOG_STATS"
-            ],
-        },
-    },
+    defaults: ["stats_defaults"],
     logtags: ["event.logtags"],
 }
 
@@ -32,6 +38,7 @@
         "-Wall",
         "-Werror",
     ],
+    defaults: ["stats_defaults"],
     shared_libs: [
         "liblog",
     ],
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 661fd8b..449088a 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -47,9 +47,7 @@
 #include <psi/psi.h>
 #include <system/thread_defs.h>
 
-#ifdef LMKD_LOG_STATS
 #include "statslog.h"
-#endif
 
 /*
  * Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
@@ -186,6 +184,7 @@
 static int thrashing_limit_pct;
 static int thrashing_limit_decay_pct;
 static bool use_psi_monitors = false;
+static struct kernel_poll_info kpoll_info;
 static struct psi_threshold psi_thresholds[VMPRESS_LEVEL_COUNT] = {
     { PSI_SOME, 70 },    /* 70ms out of 1sec for partial stall */
     { PSI_SOME, 100 },   /* 100ms out of 1sec for partial stall */
@@ -477,11 +476,6 @@
     int fd;
 };
 
-#ifdef LMKD_LOG_STATS
-static bool enable_stats_log;
-static android_log_context log_ctx;
-#endif
-
 #define PIDHASH_SZ 1024
 static struct proc *pidhash[PIDHASH_SZ];
 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
@@ -505,9 +499,6 @@
 /* PAGE_SIZE / 1024 */
 static long page_k;
 
-static char* proc_get_name(int pid);
-static void poll_kernel();
-
 static int clamp(int low, int high, int value) {
     return max(min(value, high), low);
 }
@@ -772,6 +763,60 @@
     return (int)tgid;
 }
 
+static int proc_get_size(int pid) {
+    char path[PATH_MAX];
+    char line[LINE_MAX];
+    int fd;
+    int rss = 0;
+    int total;
+    ssize_t ret;
+
+    /* gid containing AID_READPROC required */
+    snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
+    fd = open(path, O_RDONLY | O_CLOEXEC);
+    if (fd == -1)
+        return -1;
+
+    ret = read_all(fd, line, sizeof(line) - 1);
+    if (ret < 0) {
+        close(fd);
+        return -1;
+    }
+
+    sscanf(line, "%d %d ", &total, &rss);
+    close(fd);
+    return rss;
+}
+
+static char *proc_get_name(int pid) {
+    char path[PATH_MAX];
+    static char line[LINE_MAX];
+    int fd;
+    char *cp;
+    ssize_t ret;
+
+    /* gid containing AID_READPROC required */
+    snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
+    fd = open(path, O_RDONLY | O_CLOEXEC);
+    if (fd == -1) {
+        return NULL;
+    }
+    ret = read_all(fd, line, sizeof(line) - 1);
+    close(fd);
+    if (ret < 0) {
+        return NULL;
+    }
+
+    cp = strchr(line, ' ');
+    if (cp) {
+        *cp = '\0';
+    } else {
+        line[ret] = '\0';
+    }
+
+    return line;
+}
+
 static void cmd_procprio(LMKD_CTRL_PACKET packet) {
     struct proc *procp;
     char path[80];
@@ -811,9 +856,7 @@
     }
 
     if (use_inkernel_interface) {
-#ifdef LMKD_LOG_STATS
-        stats_store_taskname(params.pid, proc_get_name(params.pid));
-#endif
+        stats_store_taskname(params.pid, proc_get_name(params.pid), kpoll_info.poll_fd);
         return;
     }
 
@@ -884,16 +927,7 @@
     struct lmk_procremove params;
 
     if (use_inkernel_interface) {
-#ifdef LMKD_LOG_STATS
-        /* Perform an extra check before the pid is removed, after which it
-         * will be impossible for poll_kernel to get the taskname. poll_kernel()
-         * is potentially a long-running blocking function; however this method
-         * handles AMS requests but does not block AMS.*/
-        if (enable_stats_log) {
-            poll_kernel();
-        }
-        stats_remove_taskname(params.pid);
-#endif
+        stats_remove_taskname(params.pid, kpoll_info.poll_fd);
         return;
     }
 
@@ -911,9 +945,7 @@
     struct proc *next;
 
     if (use_inkernel_interface) {
-#ifdef LMKD_LOG_STATS
         stats_purge_tasknames();
-#endif
         return;
     }
 
@@ -1231,89 +1263,6 @@
     maxevents++;
 }
 
-#ifdef LMKD_LOG_STATS
-static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
-    char key[LINE_MAX + 1];
-    int64_t value;
-
-    sscanf(line, "%" STRINGIFY(LINE_MAX) "s  %" SCNd64 "", key, &value);
-
-    if (strcmp(key, "total_") < 0) {
-        return;
-    }
-
-    if (!strcmp(key, "total_pgfault"))
-        mem_st->pgfault = value;
-    else if (!strcmp(key, "total_pgmajfault"))
-        mem_st->pgmajfault = value;
-    else if (!strcmp(key, "total_rss"))
-        mem_st->rss_in_bytes = value;
-    else if (!strcmp(key, "total_cache"))
-        mem_st->cache_in_bytes = value;
-    else if (!strcmp(key, "total_swap"))
-        mem_st->swap_in_bytes = value;
-}
-
-static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
-    FILE *fp;
-    char buf[PATH_MAX];
-
-    snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
-
-    fp = fopen(buf, "r");
-
-    if (fp == NULL) {
-        ALOGE("%s open failed: %s", buf, strerror(errno));
-        return -1;
-    }
-
-    while (fgets(buf, PAGE_SIZE, fp) != NULL) {
-        memory_stat_parse_line(buf, mem_st);
-    }
-    fclose(fp);
-
-    return 0;
-}
-
-static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
-    char path[PATH_MAX];
-    char buffer[PROC_STAT_BUFFER_SIZE];
-    int fd, ret;
-
-    snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
-    if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
-        ALOGE("%s open failed: %s", path, strerror(errno));
-        return -1;
-    }
-
-    ret = read(fd, buffer, sizeof(buffer));
-    if (ret < 0) {
-        ALOGE("%s read failed: %s", path, strerror(errno));
-        close(fd);
-        return -1;
-    }
-    close(fd);
-
-    // field 10 is pgfault
-    // field 12 is pgmajfault
-    // field 22 is starttime
-    // field 24 is rss_in_pages
-    int64_t pgfault = 0, pgmajfault = 0, starttime = 0, rss_in_pages = 0;
-    if (sscanf(buffer,
-               "%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
-               "%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
-               "%" SCNd64 " %*d %" SCNd64 "",
-               &pgfault, &pgmajfault, &starttime, &rss_in_pages) != 4) {
-        return -1;
-    }
-    mem_st->pgfault = pgfault;
-    mem_st->pgmajfault = pgmajfault;
-    mem_st->rss_in_bytes = (rss_in_pages * PAGE_SIZE);
-    mem_st->process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
-    return 0;
-}
-#endif
-
 /*
  * /proc/zoneinfo parsing routines
  * Expected file format is:
@@ -1626,60 +1575,6 @@
     android_log_reset(ctx);
 }
 
-static int proc_get_size(int pid) {
-    char path[PATH_MAX];
-    char line[LINE_MAX];
-    int fd;
-    int rss = 0;
-    int total;
-    ssize_t ret;
-
-    /* gid containing AID_READPROC required */
-    snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
-    fd = open(path, O_RDONLY | O_CLOEXEC);
-    if (fd == -1)
-        return -1;
-
-    ret = read_all(fd, line, sizeof(line) - 1);
-    if (ret < 0) {
-        close(fd);
-        return -1;
-    }
-
-    sscanf(line, "%d %d ", &total, &rss);
-    close(fd);
-    return rss;
-}
-
-static char *proc_get_name(int pid) {
-    char path[PATH_MAX];
-    static char line[LINE_MAX];
-    int fd;
-    char *cp;
-    ssize_t ret;
-
-    /* gid containing AID_READPROC required */
-    snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
-    fd = open(path, O_RDONLY | O_CLOEXEC);
-    if (fd == -1) {
-        return NULL;
-    }
-    ret = read_all(fd, line, sizeof(line) - 1);
-    close(fd);
-    if (ret < 0) {
-        return NULL;
-    }
-
-    cp = strchr(line, ' ');
-    if (cp) {
-        *cp = '\0';
-    } else {
-        line[ret] = '\0';
-    }
-
-    return line;
-}
-
 static struct proc *proc_adj_lru(int oomadj) {
     return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
 }
@@ -1753,14 +1648,7 @@
     int tasksize;
     int r;
     int result = -1;
-
-#ifdef LMKD_LOG_STATS
-    struct memory_stat mem_st = {};
-    int memory_stat_parse_result = -1;
-#else
-    /* To prevent unused parameter warning */
-    (void)(min_oom_score);
-#endif
+    struct memory_stat *mem_st;
 
     tgid = proc_get_tgid(pid);
     if (tgid >= 0 && tgid != pid) {
@@ -1778,15 +1666,7 @@
         goto out;
     }
 
-#ifdef LMKD_LOG_STATS
-    if (enable_stats_log) {
-        if (per_app_memcg) {
-            memory_stat_parse_result = memory_stat_from_cgroup(&mem_st, pid, uid);
-        } else {
-            memory_stat_parse_result = memory_stat_from_procfs(&mem_st, pid);
-        }
-    }
-#endif
+    mem_st = stats_read_memory_stat(per_app_memcg, pid, uid);
 
     TRACE_KILL_START(pid);
 
@@ -1814,18 +1694,9 @@
 
     last_killed_pid = pid;
 
-#ifdef LMKD_LOG_STATS
-    if (memory_stat_parse_result == 0) {
-        stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
-                procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
-                mem_st.cache_in_bytes, mem_st.swap_in_bytes, mem_st.process_start_time_ns,
-                min_oom_score);
-    } else if (enable_stats_log) {
-        stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, procp->oomadj,
-                                      -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1, -1,
-                                      min_oom_score);
-    }
-#endif
+    stats_write_lmk_kill_occurred(LMK_KILL_OCCURRED, uid, taskname,
+            procp->oomadj, min_oom_score, tasksize, mem_st);
+
     result = tasksize;
 
 out:
@@ -1844,10 +1715,7 @@
 static int find_and_kill_process(int min_score_adj, const char *reason) {
     int i;
     int killed_size = 0;
-
-#ifdef LMKD_LOG_STATS
     bool lmk_state_change_start = false;
-#endif
 
     for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
         struct proc *procp;
@@ -1861,13 +1729,11 @@
 
             killed_size = kill_one_process(procp, min_score_adj, reason);
             if (killed_size >= 0) {
-#ifdef LMKD_LOG_STATS
-                if (enable_stats_log && !lmk_state_change_start) {
+                if (!lmk_state_change_start) {
                     lmk_state_change_start = true;
-                    stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
+                    stats_write_lmk_state_changed(LMK_STATE_CHANGED,
                                                   LMK_STATE_CHANGE_START);
                 }
-#endif
                 break;
             }
         }
@@ -1876,11 +1742,9 @@
         }
     }
 
-#ifdef LMKD_LOG_STATS
-    if (enable_stats_log && lmk_state_change_start) {
-        stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED, LMK_STATE_CHANGE_STOP);
+    if (lmk_state_change_start) {
+        stats_write_lmk_state_changed(LMK_STATE_CHANGED, LMK_STATE_CHANGE_STOP);
     }
-#endif
 
     return killed_size;
 }
@@ -2586,74 +2450,13 @@
     return false;
 }
 
-#ifdef LMKD_LOG_STATS
-static int kernel_poll_fd = -1;
-static void poll_kernel() {
-    if (kernel_poll_fd == -1) {
-        // not waiting
-        return;
-    }
-
-    while (1) {
-        char rd_buf[256];
-        int bytes_read =
-                TEMP_FAILURE_RETRY(pread(kernel_poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
-        if (bytes_read <= 0) break;
-        rd_buf[bytes_read] = '\0';
-
-        int64_t pid;
-        int64_t uid;
-        int64_t group_leader_pid;
-        int64_t min_flt;
-        int64_t maj_flt;
-        int64_t rss_in_pages;
-        int16_t oom_score_adj;
-        int16_t min_score_adj;
-        int64_t starttime;
-        char* taskname = 0;
-        int fields_read = sscanf(rd_buf,
-                                 "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
-                                 " %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
-                                 &pid, &uid, &group_leader_pid, &min_flt, &maj_flt, &rss_in_pages,
-                                 &oom_score_adj, &min_score_adj, &starttime, &taskname);
-
-        /* only the death of the group leader process is logged */
-        if (fields_read == 10 && group_leader_pid == pid) {
-            int64_t process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
-            stats_write_lmk_kill_occurred_pid(log_ctx, LMK_KILL_OCCURRED, uid, pid, oom_score_adj,
-                                              min_flt, maj_flt, rss_in_pages * PAGE_SIZE, 0, 0,
-                                              process_start_time_ns, min_score_adj);
-        }
-
-        free(taskname);
-    }
+static void kernel_event_handler(int data __unused, uint32_t events __unused,
+                                 struct polling_params *poll_params __unused) {
+    kpoll_info.handler(kpoll_info.poll_fd);
 }
 
-static struct event_handler_info kernel_poll_hinfo = {0, poll_kernel};
-
-static void init_poll_kernel() {
-    struct epoll_event epev;
-    kernel_poll_fd =
-            TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
-
-    if (kernel_poll_fd < 0) {
-        ALOGE("kernel lmk event file could not be opened; errno=%d", kernel_poll_fd);
-        return;
-    }
-
-    epev.events = EPOLLIN;
-    epev.data.ptr = (void*)&kernel_poll_hinfo;
-    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kernel_poll_fd, &epev) != 0) {
-        ALOGE("epoll_ctl for lmk events failed; errno=%d", errno);
-        close(kernel_poll_fd);
-        kernel_poll_fd = -1;
-    } else {
-        maxevents++;
-    }
-}
-#endif
-
 static int init(void) {
+    static struct event_handler_info kernel_poll_hinfo = { 0, kernel_event_handler };
     struct reread_data file_data = {
         .filename = ZONEINFO_PATH,
         .fd = -1,
@@ -2704,11 +2507,17 @@
 
     if (use_inkernel_interface) {
         ALOGI("Using in-kernel low memory killer interface");
-#ifdef LMKD_LOG_STATS
-        if (enable_stats_log) {
-            init_poll_kernel();
+        if (init_poll_kernel(&kpoll_info)) {
+            epev.events = EPOLLIN;
+            epev.data.ptr = (void*)&kernel_poll_hinfo;
+            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kpoll_info.poll_fd, &epev) != 0) {
+                ALOGE("epoll_ctl for lmk events failed (errno=%d)", errno);
+                close(kpoll_info.poll_fd);
+                kpoll_info.poll_fd = -1;
+            } else {
+                maxevents++;
+            }
         }
-#endif
     } else {
         /* Try to use psi monitor first if kernel has it */
         use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
@@ -2907,9 +2716,7 @@
 
     ctx = create_android_logger(MEMINFO_LOG_TAG);
 
-#ifdef LMKD_LOG_STATS
-    statslog_init(&log_ctx, &enable_stats_log);
-#endif
+    statslog_init();
 
     if (!init()) {
         if (!use_inkernel_interface) {
@@ -2938,9 +2745,7 @@
         mainloop();
     }
 
-#ifdef LMKD_LOG_STATS
-    statslog_destroy(&log_ctx);
-#endif
+    statslog_destroy();
 
     android_log_destroy(&ctx);
 
diff --git a/lmkd/statslog.c b/lmkd/statslog.c
index f3a6e55..c0fd6df 100644
--- a/lmkd/statslog.c
+++ b/lmkd/statslog.c
@@ -18,11 +18,21 @@
 #include <errno.h>
 #include <log/log_id.h>
 #include <stats_event_list.h>
+#include <statslog.h>
+#include <stdlib.h>
+#include <string.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 
+#ifdef LMKD_LOG_STATS
+
 #define LINE_MAX 128
+#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
+#define STRINGIFY_INTERNAL(x) #x
+
+static bool enable_stats_log;
+static android_log_context log_ctx;
 
 struct proc {
     int pid;
@@ -41,34 +51,53 @@
     return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
 }
 
+void statslog_init() {
+    enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
+
+    if (enable_stats_log) {
+        log_ctx = create_android_logger(kStatsEventTag);
+    }
+}
+
+void statslog_destroy() {
+    if (log_ctx) {
+        android_log_destroy(&log_ctx);
+    }
+}
+
 /**
  * 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(android_log_context ctx, int32_t code, int32_t state) {
-    assert(ctx != NULL);
+stats_write_lmk_state_changed(int32_t code, int32_t state) {
     int ret = -EINVAL;
-    if (!ctx) {
+
+    if (!enable_stats_log) {
         return ret;
     }
 
-    reset_log_context(ctx);
-
-    if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
+    assert(log_ctx != NULL);
+    if (!log_ctx) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(ctx, code)) < 0) {
+    reset_log_context(log_ctx);
+
+    if ((ret = android_log_write_int64(log_ctx, getElapsedRealTimeNs())) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(ctx, state)) < 0) {
+    if ((ret = android_log_write_int32(log_ctx, code)) < 0) {
         return ret;
     }
 
-    return write_to_logger(ctx, LOG_ID_STATS);
+    if ((ret = android_log_write_int32(log_ctx, state)) < 0) {
+        return ret;
+    }
+
+    return write_to_logger(log_ctx, LOG_ID_STATS);
 }
 
 static struct proc* pid_lookup(int pid) {
@@ -87,92 +116,261 @@
  * Code: LMK_KILL_OCCURRED = 51
  */
 int
-stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
-                              char const* process_name, int32_t oom_score, int64_t pgfault,
-                              int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
-                              int64_t swap_in_bytes, int64_t process_start_time_ns,
-                              int32_t min_oom_score) {
-    assert(ctx != NULL);
+stats_write_lmk_kill_occurred(int32_t code, int32_t uid, char const* process_name,
+                              int32_t oom_score, int32_t min_oom_score, int tasksize,
+                              struct memory_stat *mem_st) {
     int ret = -EINVAL;
-    if (!ctx) {
+    if (!enable_stats_log) {
         return ret;
     }
-    reset_log_context(ctx);
+    if (!log_ctx) {
+        return ret;
+    }
+    reset_log_context(log_ctx);
 
-    if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, getElapsedRealTimeNs())) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(ctx, code)) < 0) {
+    if ((ret = android_log_write_int32(log_ctx, code)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(ctx, uid)) < 0) {
+    if ((ret = android_log_write_int32(log_ctx, uid)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_string8(ctx, (process_name == NULL) ? "" : process_name)) < 0) {
+    if ((ret = android_log_write_string8(log_ctx, (process_name == NULL) ? "" : process_name)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(ctx, oom_score)) < 0) {
+    if ((ret = android_log_write_int32(log_ctx, oom_score)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(ctx, pgfault)) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->pgfault : -1)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(ctx, pgmajfault)) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->pgmajfault : -1)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(ctx, rss_in_bytes)) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->rss_in_bytes
+                                                       : tasksize * BYTES_IN_KILOBYTE)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(ctx, cache_in_bytes)) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->cache_in_bytes : -1)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(ctx, swap_in_bytes)) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->swap_in_bytes : -1)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(ctx, process_start_time_ns)) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->process_start_time_ns
+                                                       : -1)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(ctx, min_oom_score)) < 0) {
+    if ((ret = android_log_write_int32(log_ctx, min_oom_score)) < 0) {
         return ret;
     }
 
-    return write_to_logger(ctx, LOG_ID_STATS);
+    return write_to_logger(log_ctx, LOG_ID_STATS);
 }
 
-int stats_write_lmk_kill_occurred_pid(android_log_context ctx, int32_t code, int32_t uid, int pid,
-                                      int32_t oom_score, int64_t pgfault, int64_t pgmajfault,
-                                      int64_t rss_in_bytes, int64_t cache_in_bytes,
-                                      int64_t swap_in_bytes, int64_t process_start_time_ns,
-                                      int32_t min_oom_score) {
+static int stats_write_lmk_kill_occurred_pid(int32_t code, int32_t uid, int pid,
+                                      int32_t oom_score, int32_t min_oom_score, int tasksize,
+                                      struct memory_stat *mem_st) {
     struct proc* proc = pid_lookup(pid);
     if (!proc) return -EINVAL;
 
-    return stats_write_lmk_kill_occurred(ctx, code, uid, proc->taskname, oom_score, pgfault,
-                                         pgmajfault, rss_in_bytes, cache_in_bytes, swap_in_bytes,
-                                         process_start_time_ns, min_oom_score);
+    return stats_write_lmk_kill_occurred(code, uid, proc->taskname, oom_score, min_oom_score,
+                                         tasksize, mem_st);
+}
+
+static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
+    char key[LINE_MAX + 1];
+    int64_t value;
+
+    sscanf(line, "%" STRINGIFY(LINE_MAX) "s  %" SCNd64 "", key, &value);
+
+    if (strcmp(key, "total_") < 0) {
+        return;
+    }
+
+    if (!strcmp(key, "total_pgfault"))
+        mem_st->pgfault = value;
+    else if (!strcmp(key, "total_pgmajfault"))
+        mem_st->pgmajfault = value;
+    else if (!strcmp(key, "total_rss"))
+        mem_st->rss_in_bytes = value;
+    else if (!strcmp(key, "total_cache"))
+        mem_st->cache_in_bytes = value;
+    else if (!strcmp(key, "total_swap"))
+        mem_st->swap_in_bytes = value;
+}
+
+static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
+    FILE *fp;
+    char buf[PATH_MAX];
+
+    snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
+
+    fp = fopen(buf, "r");
+
+    if (fp == NULL) {
+        return -1;
+    }
+
+    while (fgets(buf, PAGE_SIZE, fp) != NULL) {
+        memory_stat_parse_line(buf, mem_st);
+    }
+    fclose(fp);
+
+    return 0;
+}
+
+static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
+    char path[PATH_MAX];
+    char buffer[PROC_STAT_BUFFER_SIZE];
+    int fd, ret;
+
+    snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
+    if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
+        return -1;
+    }
+
+    ret = read(fd, buffer, sizeof(buffer));
+    if (ret < 0) {
+        close(fd);
+        return -1;
+    }
+    close(fd);
+
+    // field 10 is pgfault
+    // field 12 is pgmajfault
+    // field 22 is starttime
+    // field 24 is rss_in_pages
+    int64_t pgfault = 0, pgmajfault = 0, starttime = 0, rss_in_pages = 0;
+    if (sscanf(buffer,
+               "%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
+               "%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
+               "%" SCNd64 " %*d %" SCNd64 "",
+               &pgfault, &pgmajfault, &starttime, &rss_in_pages) != 4) {
+        return -1;
+    }
+    mem_st->pgfault = pgfault;
+    mem_st->pgmajfault = pgmajfault;
+    mem_st->rss_in_bytes = (rss_in_pages * PAGE_SIZE);
+    mem_st->process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
+
+    return 0;
+}
+
+struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t uid) {
+    static struct memory_stat mem_st = {};
+
+    if (!enable_stats_log) {
+        return NULL;
+    }
+
+    if (per_app_memcg) {
+        if (memory_stat_from_cgroup(&mem_st, pid, uid) == 0) {
+            return &mem_st;
+        }
+    } else {
+        if (memory_stat_from_procfs(&mem_st, pid) == 0) {
+            return &mem_st;
+        }
+    }
+
+    return NULL;
+}
+
+static void poll_kernel(int poll_fd) {
+    if (poll_fd == -1) {
+        // not waiting
+        return;
+    }
+
+    while (1) {
+        char rd_buf[256];
+        int bytes_read =
+                TEMP_FAILURE_RETRY(pread(poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
+        if (bytes_read <= 0) break;
+        rd_buf[bytes_read] = '\0';
+
+        int64_t pid;
+        int64_t uid;
+        int64_t group_leader_pid;
+        int64_t rss_in_pages;
+        struct memory_stat mem_st = {};
+        int16_t oom_score_adj;
+        int16_t min_score_adj;
+        int64_t starttime;
+        char* taskname = 0;
+
+        int fields_read = sscanf(rd_buf,
+                                 "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
+                                 " %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
+                                 &pid, &uid, &group_leader_pid, &mem_st.pgfault,
+                                 &mem_st.pgmajfault, &rss_in_pages, &oom_score_adj,
+                                 &min_score_adj, &starttime, &taskname);
+
+        /* only the death of the group leader process is logged */
+        if (fields_read == 10 && group_leader_pid == pid) {
+            mem_st.process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
+            mem_st.rss_in_bytes = rss_in_pages * PAGE_SIZE;
+            stats_write_lmk_kill_occurred_pid(LMK_KILL_OCCURRED, uid, pid, oom_score_adj,
+                                              min_score_adj, 0, &mem_st);
+        }
+
+        free(taskname);
+    }
+}
+
+bool init_poll_kernel(struct kernel_poll_info *poll_info) {
+    if (!enable_stats_log) {
+        return false;
+    }
+
+    poll_info->poll_fd =
+            TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+
+    if (poll_info->poll_fd < 0) {
+        ALOGE("kernel lmk event file could not be opened; errno=%d", errno);
+        return false;
+    }
+    poll_info->handler = poll_kernel;
+
+    return true;
 }
 
 static void proc_insert(struct proc* procp) {
-    if (!pidhash)
+    if (!pidhash) {
         pidhash = calloc(PIDHASH_SZ, sizeof(struct proc));
+    }
+
     int hval = pid_hashfn(procp->pid);
     procp->pidhash_next = pidhash[hval];
     pidhash[hval] = procp;
 }
 
-void stats_remove_taskname(int pid) {
-    if (!pidhash) return;
+void stats_remove_taskname(int pid, int poll_fd) {
+    if (!enable_stats_log || !pidhash) {
+        return;
+    }
+
+    /*
+     * Perform an extra check before the pid is removed, after which it
+     * will be impossible for poll_kernel to get the taskname. poll_kernel()
+     * is potentially a long-running blocking function; however this method
+     * handles AMS requests but does not block AMS.
+     */
+    poll_kernel(poll_fd);
 
     int hval = pid_hashfn(pid);
     struct proc* procp;
@@ -193,12 +391,19 @@
     free(procp);
 }
 
-void stats_store_taskname(int pid, const char* taskname) {
-    struct proc* procp = pid_lookup(pid);
-    if (procp != NULL && strcmp(procp->taskname, taskname) == 0)
+void stats_store_taskname(int pid, const char* taskname, int poll_fd) {
+    if (!enable_stats_log) {
         return;
+    }
+
+    struct proc* procp = pid_lookup(pid);
+    if (procp != NULL) {
+        if (strcmp(procp->taskname, taskname) == 0) {
+            return;
+        }
+        stats_remove_taskname(pid, poll_fd);
+    }
     procp = malloc(sizeof(struct proc));
-    stats_remove_taskname(pid);
     procp->pid = pid;
     strncpy(procp->taskname, taskname, LINE_MAX - 1);
     procp->taskname[LINE_MAX - 1] = '\0';
@@ -206,7 +411,10 @@
 }
 
 void stats_purge_tasknames() {
-    if (!pidhash) return;
+    if (!enable_stats_log || !pidhash) {
+        return;
+    }
+
     struct proc* procp;
     struct proc* next;
     int i;
@@ -220,3 +428,5 @@
     }
     memset(pidhash, 0, PIDHASH_SZ * sizeof(struct proc));
 }
+
+#endif /* LMKD_LOG_STATS */
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
index 50d69f7..a09c7dd 100644
--- a/lmkd/statslog.h
+++ b/lmkd/statslog.h
@@ -18,6 +18,7 @@
 #define _STATSLOG_H_
 
 #include <assert.h>
+#include <inttypes.h>
 #include <stats_event_list.h>
 #include <stdbool.h>
 #include <sys/cdefs.h>
@@ -26,6 +27,20 @@
 
 __BEGIN_DECLS
 
+struct memory_stat {
+    int64_t pgfault;
+    int64_t pgmajfault;
+    int64_t rss_in_bytes;
+    int64_t cache_in_bytes;
+    int64_t swap_in_bytes;
+    int64_t process_start_time_ns;
+};
+
+struct kernel_poll_info {
+    int poll_fd;
+    void (*handler)(int poll_fd);
+};
+
 /*
  * These are defined in
  * http://cs/android/frameworks/base/cmds/statsd/src/atoms.proto
@@ -35,37 +50,17 @@
 #define LMK_STATE_CHANGE_START 1
 #define LMK_STATE_CHANGE_STOP 2
 
+#ifdef LMKD_LOG_STATS
+
 /*
  * The single event tag id for all stats logs.
  * Keep this in sync with system/core/logcat/event.logtags
  */
 const static int kStatsEventTag = 1937006964;
 
-static inline void statslog_init(android_log_context* log_ctx, bool* enable_stats_log) {
-    assert(log_ctx != NULL);
-    assert(enable_stats_log != NULL);
-    *enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
+void statslog_init();
 
-    if (*enable_stats_log) {
-        *log_ctx = create_android_logger(kStatsEventTag);
-    }
-}
-
-static inline void statslog_destroy(android_log_context* log_ctx) {
-    assert(log_ctx != NULL);
-    if (*log_ctx) {
-        android_log_destroy(log_ctx);
-    }
-}
-
-struct memory_stat {
-    int64_t pgfault;
-    int64_t pgmajfault;
-    int64_t rss_in_bytes;
-    int64_t cache_in_bytes;
-    int64_t swap_in_bytes;
-    int64_t process_start_time_ns;
-};
+void statslog_destroy();
 
 #define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%u/memory.stat"
 #define PROC_STAT_FILE_PATH "/proc/%d/stat"
@@ -78,47 +73,63 @@
  * Code: LMK_STATE_CHANGED = 54
  */
 int
-stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state);
+stats_write_lmk_state_changed(int32_t code, int32_t state);
 
 /**
  * Logs the event when LMKD kills a process to reduce memory pressure.
  * Code: LMK_KILL_OCCURRED = 51
  */
 int
-stats_write_lmk_kill_occurred_pid(android_log_context ctx, int32_t code, int32_t uid, int pid,
-                                  int32_t oom_score, int64_t pgfault, int64_t pgmajfault,
-                                  int64_t rss_in_bytes, int64_t cache_in_bytes,
-                                  int64_t swap_in_bytes, int64_t process_start_time_ns,
-                                  int32_t min_oom_score);
+stats_write_lmk_kill_occurred(int32_t code, int32_t uid,
+                              char const* process_name, int32_t oom_score, int32_t min_oom_score,
+                              int tasksize, 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(android_log_context ctx, int32_t code, int32_t uid,
-                              char const* process_name, int32_t oom_score, int64_t pgfault,
-                              int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
-                              int64_t swap_in_bytes, int64_t process_start_time_ns,
-                              int32_t min_oom_score);
+struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t uid);
 
 /**
  * Registers a process taskname by pid, while it is still alive.
  */
-void
-stats_store_taskname(int pid, const char* taskname);
+void stats_store_taskname(int pid, const char* taskname, int poll_fd);
 
 /**
  * Unregister all process tasknames.
  */
-void
-stats_purge_tasknames();
+void stats_purge_tasknames();
 
 /**
  * Unregister a process taskname, e.g. after it has been killed.
  */
-void
-stats_remove_taskname(int pid);
+void stats_remove_taskname(int pid, int poll_fd);
+
+bool init_poll_kernel(struct kernel_poll_info *poll_info);
+
+#else /* LMKD_LOG_STATS */
+
+static inline void statslog_init() {}
+static inline void statslog_destroy() {}
+
+static inline int
+stats_write_lmk_state_changed(int32_t code __unused, int32_t state __unused) { return -EINVAL; }
+
+static inline int
+stats_write_lmk_kill_occurred(int32_t code __unused, int32_t uid __unused,
+                              char const* process_name __unused, int32_t oom_score __unused,
+                              int32_t min_oom_score __unused, int tasksize __unused,
+                              struct memory_stat *mem_st __unused) { return -EINVAL; }
+
+static inline struct memory_stat *stats_read_memory_stat(bool per_app_memcg __unused,
+                                    int pid __unused, uid_t uid __unused) { return NULL; }
+
+static inline void stats_store_taskname(int pid __unused, const char* taskname __unused,
+                                        int poll_fd __unused) {}
+
+static inline void stats_purge_tasknames() {}
+
+static inline void stats_remove_taskname(int pid __unused, int poll_fd __unused) {}
+
+static inline bool init_poll_kernel(struct kernel_poll_info *poll_info __unused) { return false; }
+
+#endif /* LMKD_LOG_STATS */
 
 __END_DECLS