diff options
author | Suren Baghdasaryan <surenb@google.com> | 2019-10-01 20:38:23 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2019-10-01 20:38:23 +0000 |
commit | 7718c75bc2a268d58b821105775be9e39a2f249b (patch) | |
tree | 451a7d687adf7121890ecb9ccc8bbdc24fece085 | |
parent | 46bc39ba23860b3db973751b9f6e80a1af4d0355 (diff) | |
parent | b72c66515b86c971bdb5df52965381795e1f917e (diff) |
Merge "lmkd: Isolate statslog related code from lmkd code"
-rw-r--r-- | lmkd/Android.bp | 21 | ||||
-rw-r--r-- | lmkd/lmkd.c | 359 | ||||
-rw-r--r-- | lmkd/statslog.c | 300 | ||||
-rw-r--r-- | lmkd/statslog.h | 103 |
4 files changed, 408 insertions, 375 deletions
diff --git a/lmkd/Android.bp b/lmkd/Android.bp index 4c5ca0348..e8e125b85 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 @@ cc_binary { 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 @@ cc_library_static { "-Wall", "-Werror", ], + defaults: ["stats_defaults"], shared_libs: [ "liblog", ], diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c index 661fd8b0c..449088afb 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 psi_complete_stall_ms; 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 @@ struct reread_data { 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 @@ static uint32_t killcnt_total = 0; /* 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 @@ out: 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 @@ static void cmd_procprio(LMKD_CTRL_PACKET packet) { } 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 @@ static void cmd_procremove(LMKD_CTRL_PACKET packet) { 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 @@ static void cmd_procpurge() { struct proc *next; if (use_inkernel_interface) { -#ifdef LMKD_LOG_STATS stats_purge_tasknames(); -#endif return; } @@ -1231,89 +1263,6 @@ static void ctrl_connect_handler(int data __unused, uint32_t events __unused, 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 @@ static void meminfo_log(union meminfo *mi) { 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 @@ static int kill_one_process(struct proc* procp, int min_oom_score, const char *r 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 @@ static int kill_one_process(struct proc* procp, int min_oom_score, const char *r 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 @@ static int kill_one_process(struct proc* procp, int min_oom_score, const char *r 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 @@ out: 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 @@ static int find_and_kill_process(int min_score_adj, const char *reason) { 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 @@ static int find_and_kill_process(int min_score_adj, const char *reason) { } } -#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 @@ err_open_mpfd: 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 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++; - } +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); } -#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 @@ static int init(void) { 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 @@ int main(int argc __unused, char **argv __unused) { 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 @@ int main(int argc __unused, char **argv __unused) { 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 f3a6e55d8..c0fd6df83 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 @@ static int64_t getElapsedRealTimeNs() { 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; + } + + assert(log_ctx != NULL); + if (!log_ctx) { return ret; } - reset_log_context(ctx); + 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, state)) < 0) { + if ((ret = android_log_write_int32(log_ctx, state)) < 0) { return ret; } - return write_to_logger(ctx, LOG_ID_STATS); + return write_to_logger(log_ctx, LOG_ID_STATS); } static struct proc* pid_lookup(int pid) { @@ -87,92 +116,261 @@ static struct proc* pid_lookup(int pid) { * 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 @@ void stats_remove_taskname(int pid) { 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_store_taskname(int pid, const char* taskname) { } 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 @@ void stats_purge_tasknames() { } memset(pidhash, 0, PIDHASH_SZ * sizeof(struct proc)); } + +#endif /* LMKD_LOG_STATS */ diff --git a/lmkd/statslog.h b/lmkd/statslog.h index 50d69f739..a09c7dd91 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 @@ __BEGIN_DECLS #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); - - if (*enable_stats_log) { - *log_ctx = create_android_logger(kStatsEventTag); - } -} +void statslog_init(); -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 @@ struct memory_stat { * 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 |