diff options
l--------- | .clang-format | 1 | ||||
-rw-r--r-- | Android.bp | 52 | ||||
-rw-r--r-- | event.logtags | 2 | ||||
-rw-r--r-- | include/lmkd.h | 21 | ||||
-rw-r--r-- | libpsi/Android.bp | 7 | ||||
-rw-r--r-- | libpsi/psi.cpp | 5 | ||||
-rw-r--r-- | lmkd.cpp | 505 | ||||
-rw-r--r-- | statslog.cpp | 197 | ||||
-rw-r--r-- | statslog.h | 112 | ||||
-rw-r--r-- | tests/Android.bp | 4 | ||||
-rw-r--r-- | tests/lmkd_test.cpp | 8 |
11 files changed, 282 insertions, 632 deletions
diff --git a/.clang-format b/.clang-format deleted file mode 120000 index 973b2fa..0000000 --- a/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../../../build/soong/scripts/system-clang-format
\ No newline at end of file @@ -1,12 +1,13 @@ -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - cc_defaults { name: "stats_defaults", - cflags: [ - "-DLMKD_LOG_STATS" - ], + + product_variables: { + use_lmkd_stats_log: { + cflags: [ + "-DLMKD_LOG_STATS" + ], + }, + }, } cc_binary { @@ -18,14 +19,13 @@ cc_binary { "liblog", "libprocessgroup", "libpsi", + "libstatssocket", ], static_libs: [ "libstatslogc", + "libstatslog_lmkd", "liblmkd_utils", ], - header_libs: [ - "bpf_syscall_wrappers", - ], local_include_dirs: ["include"], cflags: [ "-Wall", @@ -41,7 +41,6 @@ cc_binary { cc_library_static { name: "libstatslogc", srcs: ["statslog.cpp"], - local_include_dirs: ["include"], cflags: [ "-Wall", "-Werror", @@ -51,6 +50,37 @@ cc_library_static { 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/event.logtags b/event.logtags index 5382b49..452f411 100644 --- a/event.logtags +++ b/event.logtags @@ -35,4 +35,4 @@ # TODO: generate ".java" and ".h" files with integer constants from this file. # for killinfo logs -10195355 killinfo (Pid|1|5),(Uid|1|5),(OomAdj|1),(MinOomAdj|1),(TaskSize|1),(enum kill_reasons|1|5),(MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapTotal|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(IonHeap|1),(IonHeapPool|1),(CmaFree|1),(MsSinceEvent|1),(MsSincePrevWakeup|1),(WakeupsSinceEvent|1),(SkippedWakeups|1),(TaskSwapSize|1),(GPU|1) +10195355 killinfo (Pid|1|5),(Uid|1|5),(OomAdj|1),(MinOomAdj|1),(TaskSize|1),(enum kill_reasons|1|5),(MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapTotal|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(IonHeap|1),(IonHeapPool|1),(CmaFree|1) diff --git a/include/lmkd.h b/include/lmkd.h index c814d17..ad5dc75 100644 --- a/include/lmkd.h +++ b/include/lmkd.h @@ -27,16 +27,14 @@ __BEGIN_DECLS * 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_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 */ + 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 */ }; /* @@ -206,11 +204,10 @@ static inline size_t lmkd_pack_set_getkillcnt_repl(LMKD_CTRL_PACKET packet, int return 2 * sizeof(int); } -/* Types of asynchronous events sent from lmkd to its clients */ +/* Types of asyncronous 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/libpsi/Android.bp b/libpsi/Android.bp index eff9f38..da0da08 100644 --- a/libpsi/Android.bp +++ b/libpsi/Android.bp @@ -1,23 +1,16 @@ -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - cc_library_headers { name: "libpsi_headers", export_include_dirs: ["include"], - vendor_available: true, } cc_library { name: "libpsi", srcs: ["psi.cpp"], - vendor_available: true, shared_libs: [ "liblog" ], header_libs: [ "libpsi_headers", - "libcutils_headers", ], export_header_lib_headers: [ "libpsi_headers", diff --git a/libpsi/psi.cpp b/libpsi/psi.cpp index 89f07ed..f4d5d18 100644 --- a/libpsi/psi.cpp +++ b/libpsi/psi.cpp @@ -17,15 +17,10 @@ #define LOG_TAG "libpsi" #include <errno.h> -#include <fcntl.h> -#include <stdio.h> #include <string.h> #include <sys/epoll.h> -#include <unistd.h> #include <log/log.h> -#include <cutils/fs.h> -#include <stdio.h> #include "psi/psi.h" #define PSI_MON_FILE_MEMORY "/proc/pressure/memory" @@ -22,6 +22,7 @@ #include <pwd.h> #include <sched.h> #include <signal.h> +#include <statslog_lmkd.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> @@ -29,7 +30,6 @@ #include <sys/epoll.h> #include <sys/eventfd.h> #include <sys/mman.h> -#include <sys/pidfd.h> #include <sys/resource.h> #include <sys/socket.h> #include <sys/syscall.h> @@ -53,9 +53,6 @@ #include "statslog.h" -#define BPF_FD_JUST_USE_INT -#include "BpfSyscallWrappers.h" - /* * Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces * to profile and correlate with OOM kills @@ -86,8 +83,6 @@ #define MEMINFO_PATH "/proc/meminfo" #define VMSTAT_PATH "/proc/vmstat" #define PROC_STATUS_TGID_FIELD "Tgid:" -#define PROC_STATUS_RSS_FIELD "VmRSS:" -#define PROC_STATUS_SWAP_FIELD "VmSwap:" #define LINE_MAX 128 #define PERCEPTIBLE_APP_ADJ 200 @@ -103,7 +98,6 @@ #define EIGHT_MEGA (1 << 23) #define TARGET_UPDATE_MIN_INTERVAL_MS 1000 -#define THRASHING_RESET_INTERVAL_MS 1000 #define NS_PER_MS (NS_PER_SEC / MS_PER_SEC) #define US_PER_MS (US_PER_SEC / MS_PER_SEC) @@ -150,6 +144,15 @@ #define LMKD_REINIT_PROP "lmkd.reinit" +static inline int sys_pidfd_open(pid_t pid, unsigned int flags) { + return syscall(__NR_pidfd_open, pid, flags); +} + +static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info, + unsigned int flags) { + return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags); +} + /* default to old in-kernel interface if no memory pressure events */ static bool use_inkernel_interface = true; static bool has_inkernel_module; @@ -199,9 +202,6 @@ static int psi_partial_stall_ms; static int psi_complete_stall_ms; static int thrashing_limit_pct; static int thrashing_limit_decay_pct; -static int thrashing_critical_pct; -static int swap_util_max; -static int64_t filecache_min_kb; static bool use_psi_monitors = false; static int kpoll_fd; static struct psi_threshold psi_thresholds[VMPRESS_LEVEL_COUNT] = { @@ -332,18 +332,21 @@ struct zoneinfo_zone { enum zoneinfo_node_field { ZI_NODE_NR_INACTIVE_FILE = 0, ZI_NODE_NR_ACTIVE_FILE, + ZI_NODE_WORKINGSET_REFAULT, ZI_NODE_FIELD_COUNT }; static const char* const zoneinfo_node_field_names[ZI_NODE_FIELD_COUNT] = { "nr_inactive_file", "nr_active_file", + "workingset_refault", }; union zoneinfo_node_fields { struct { int64_t nr_inactive_file; int64_t nr_active_file; + int64_t workingset_refault; } field; int64_t arr[ZI_NODE_FIELD_COUNT]; }; @@ -364,6 +367,7 @@ struct zoneinfo { int64_t totalreserve_pages; int64_t total_inactive_file; int64_t total_active_file; + int64_t total_workingset_refault; }; /* Fields to parse in /proc/meminfo */ @@ -435,7 +439,6 @@ union meminfo { int64_t cma_free; /* fields below are calculated rather than read from the file */ int64_t nr_file_pages; - int64_t total_gpu_kb; } field; int64_t arr[MI_FIELD_COUNT]; }; @@ -446,7 +449,6 @@ enum vmstat_field { VS_INACTIVE_FILE, VS_ACTIVE_FILE, VS_WORKINGSET_REFAULT, - VS_WORKINGSET_REFAULT_FILE, VS_PGSCAN_KSWAPD, VS_PGSCAN_DIRECT, VS_PGSCAN_DIRECT_THROTTLE, @@ -458,7 +460,6 @@ static const char* const vmstat_field_names[MI_FIELD_COUNT] = { "nr_inactive_file", "nr_active_file", "workingset_refault", - "workingset_refault_file", "pgscan_kswapd", "pgscan_direct", "pgscan_direct_throttle", @@ -470,7 +471,6 @@ union vmstat { int64_t nr_inactive_file; int64_t nr_active_file; int64_t workingset_refault; - int64_t workingset_refault_file; int64_t pgscan_kswapd; int64_t pgscan_direct; int64_t pgscan_direct_throttle; @@ -762,49 +762,6 @@ static void ctrl_data_write_lmk_kill_occurred(pid_t pid, uid_t uid) { } } -/* - * 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 @@ -839,16 +796,8 @@ static void poll_kernel(int poll_fd) { ctrl_data_write_lmk_kill_occurred((pid_t)pid, (uid_t)uid); mem_st.process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK)); mem_st.rss_in_bytes = rss_in_pages * PAGE_SIZE; - - struct kill_stat kill_st = { - .uid = static_cast<int32_t>(uid), - .kill_reason = NONE, - .oom_score = oom_score_adj, - .min_oom_score = min_score_adj, - .free_mem_kb = 0, - .free_swap_kb = 0, - }; - stats_write_lmk_kill_occurred_pid(pid, &kill_st, &mem_st); + stats_write_lmk_kill_occurred_pid(uid, pid, oom_score_adj, + min_score_adj, 0, &mem_st); } free(taskname); @@ -980,33 +929,30 @@ static inline long get_time_diff_ms(struct timespec *from, (to->tv_nsec - from->tv_nsec) / (long)NS_PER_MS; } -/* Reads /proc/pid/status into buf. */ -static bool read_proc_status(int pid, char *buf, size_t buf_sz) { +static int proc_get_tgid(int pid) { char path[PATH_MAX]; + char buf[PAGE_SIZE]; int fd; ssize_t size; + char *pos; + int64_t tgid = -1; snprintf(path, PATH_MAX, "/proc/%d/status", pid); fd = open(path, O_RDONLY | O_CLOEXEC); if (fd < 0) { - return false; + return -1; } - size = read_all(fd, buf, buf_sz - 1); - close(fd); + size = read_all(fd, buf, sizeof(buf) - 1); if (size < 0) { - return false; + goto out; } buf[size] = 0; - return true; -} -/* Looks for tag in buf and parses the first integer */ -static bool parse_status_tag(char *buf, const char *tag, int64_t *out) { - char *pos = buf; + pos = buf; while (true) { - pos = strstr(pos, tag); - /* Stop if tag not found or found at the line beginning */ + pos = strstr(pos, PROC_STATUS_TGID_FIELD); + /* Stop if TGID tag not found or found at the line beginning */ if (pos == NULL || pos == buf || pos[-1] == '\n') { break; } @@ -1014,12 +960,16 @@ static bool parse_status_tag(char *buf, const char *tag, int64_t *out) { } if (pos == NULL) { - return false; + goto out; } - pos += strlen(tag); - while (*pos == ' ') ++pos; - return parse_int64(pos, out); + pos += strlen(PROC_STATUS_TGID_FIELD); + while (*pos == ' ') pos++; + parse_int64(pos, &tgid); + +out: + close(fd); + return (int)tgid; } static int proc_get_size(int pid) { @@ -1083,8 +1033,7 @@ static void cmd_procprio(LMKD_CTRL_PACKET packet, int field_count, struct ucred struct lmk_procprio params; bool is_system_server; struct passwd *pwdrec; - int64_t tgid; - char buf[PAGE_SIZE]; + int tgid; lmkd_pack_get_procprio(packet, field_count, ¶ms); @@ -1100,12 +1049,11 @@ static void cmd_procprio(LMKD_CTRL_PACKET packet, int field_count, struct ucred } /* Check if registered process is a thread group leader */ - if (read_proc_status(params.pid, buf, sizeof(buf))) { - if (parse_status_tag(buf, PROC_STATUS_TGID_FIELD, &tgid) && tgid != params.pid) { - ALOGE("Attempt to register a task that is not a thread group leader " - "(tid %d, tgid %" PRId64 ")", params.pid, tgid); - return; - } + tgid = proc_get_tgid(params.pid); + if (tgid >= 0 && tgid != params.pid) { + ALOGE("Attempt to register a task that is not a thread group leader (tid %d, tgid %d)", + params.pid, tgid); + return; } /* gid containing AID_READPROC required */ @@ -1175,7 +1123,7 @@ static void cmd_procprio(LMKD_CTRL_PACKET packet, int field_count, struct ucred int pidfd = -1; if (pidfd_supported) { - pidfd = TEMP_FAILURE_RETRY(pidfd_open(params.pid, 0)); + pidfd = TEMP_FAILURE_RETRY(sys_pidfd_open(params.pid, 0)); if (pidfd < 0) { ALOGE("pidfd_open for pid %d failed; errno=%d", params.pid, errno); return; @@ -1197,10 +1145,9 @@ static void cmd_procprio(LMKD_CTRL_PACKET packet, int field_count, struct ucred } else { if (!claim_record(procp, cred->pid)) { char buf[LINE_MAX]; - char *taskname = proc_get_name(cred->pid, buf, sizeof(buf)); /* Only registrant of the record can remove it */ ALOGE("%s (%d, %d) attempts to modify a process registered by another client", - taskname ? taskname : "A process ", cred->uid, cred->pid); + proc_get_name(cred->pid, buf, sizeof(buf)), cred->uid, cred->pid); return; } proc_unslot(procp); @@ -1235,10 +1182,9 @@ static void cmd_procremove(LMKD_CTRL_PACKET packet, struct ucred *cred) { if (!claim_record(procp, cred->pid)) { char buf[LINE_MAX]; - char *taskname = proc_get_name(cred->pid, buf, sizeof(buf)); /* Only registrant of the record can remove it */ ALOGE("%s (%d, %d) attempts to unregister a process registered by another client", - taskname ? taskname : "A process ", cred->uid, cred->pid); + proc_get_name(cred->pid, buf, sizeof(buf)), cred->uid, cred->pid); return; } @@ -1759,6 +1705,7 @@ static int zoneinfo_parse(struct zoneinfo *zi) { } zi->total_inactive_file += node->fields.field.nr_inactive_file; zi->total_active_file += node->fields.field.nr_active_file; + zi->total_workingset_refault += node->fields.field.workingset_refault; } return 0; } @@ -1790,21 +1737,6 @@ static bool meminfo_parse_line(char *line, union meminfo *mi) { return (match_res != PARSE_FAIL); } -static int64_t read_gpu_total_kb() { - static int fd = android::bpf::bpfFdGet( - "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map", BPF_F_RDONLY); - static constexpr uint64_t kBpfKeyGpuTotalUsage = 0; - uint64_t value; - - if (fd < 0) { - return 0; - } - - return android::bpf::findMapEntry(fd, &kBpfKeyGpuTotalUsage, &value) - ? 0 - : (int32_t)(value / 1024); -} - static int meminfo_parse(union meminfo *mi) { static struct reread_data file_data = { .filename = MEMINFO_PATH, @@ -1829,7 +1761,6 @@ static int meminfo_parse(union meminfo *mi) { } mi->field.nr_file_pages = mi->field.cached + mi->field.swap_cached + mi->field.buffers; - mi->field.total_gpu_kb = read_gpu_total_kb(); return 0; } @@ -1887,50 +1818,14 @@ static int vmstat_parse(union vmstat *vs) { return 0; } -enum wakeup_reason { - Event, - Polling -}; - -struct wakeup_info { - struct timespec wakeup_tm; - struct timespec prev_wakeup_tm; - struct timespec last_event_tm; - int wakeups_since_event; - int skipped_wakeups; -}; - -/* - * After the initial memory pressure event is received lmkd schedules periodic wakeups to check - * the memory conditions and kill if needed (polling). This is done because pressure events are - * rate-limited and memory conditions can change in between events. Therefore after the initial - * event there might be multiple wakeups. This function records the wakeup information such as the - * timestamps of the last event and the last wakeup, the number of wakeups since the last event - * and how many of those wakeups were skipped (some wakeups are skipped if previously killed - * process is still freeing its memory). - */ -static void record_wakeup_time(struct timespec *tm, enum wakeup_reason reason, - struct wakeup_info *wi) { - wi->prev_wakeup_tm = wi->wakeup_tm; - wi->wakeup_tm = *tm; - if (reason == Event) { - wi->last_event_tm = *tm; - wi->wakeups_since_event = 0; - wi->skipped_wakeups = 0; - } else { - wi->wakeups_since_event++; - } -} - -static void killinfo_log(struct proc* procp, int min_oom_score, int rss_kb, - int swap_kb, int kill_reason, union meminfo *mi, - struct wakeup_info *wi, struct timespec *tm) { +static void killinfo_log(struct proc* procp, int min_oom_score, int tasksize, + int kill_reason, union meminfo *mi) { /* log process information */ android_log_write_int32(ctx, procp->pid); android_log_write_int32(ctx, procp->uid); android_log_write_int32(ctx, procp->oomadj); android_log_write_int32(ctx, min_oom_score); - android_log_write_int32(ctx, (int32_t)min(rss_kb, INT32_MAX)); + android_log_write_int32(ctx, (int32_t)min(tasksize * page_k, INT32_MAX)); android_log_write_int32(ctx, kill_reason); /* log meminfo fields */ @@ -1938,14 +1833,6 @@ static void killinfo_log(struct proc* procp, int min_oom_score, int rss_kb, android_log_write_int32(ctx, (int32_t)min(mi->arr[field_idx] * page_k, INT32_MAX)); } - /* log lmkd wakeup information */ - android_log_write_int32(ctx, (int32_t)get_time_diff_ms(&wi->last_event_tm, tm)); - android_log_write_int32(ctx, (int32_t)get_time_diff_ms(&wi->prev_wakeup_tm, tm)); - android_log_write_int32(ctx, wi->wakeups_since_event); - android_log_write_int32(ctx, wi->skipped_wakeups); - android_log_write_int32(ctx, (int32_t)min(swap_kb, INT32_MAX)); - android_log_write_int32(ctx, (int32_t)mi->field.total_gpu_kb); - android_log_write_list(ctx, LOG_ID_EVENTS); android_log_reset(ctx); } @@ -1962,7 +1849,7 @@ static struct proc *proc_get_heaviest(int oomadj) { while (curr != head) { int pid = ((struct proc *)curr)->pid; int tasksize = proc_get_size(pid); - if (tasksize < 0) { + if (tasksize <= 0) { struct adjslot_list *next = curr->next; pid_remove(pid); curr = next; @@ -2110,55 +1997,37 @@ static void start_wait_for_proc_kill(int pid_or_fd) { maxevents++; } -struct kill_info { - enum kill_reasons kill_reason; - const char *kill_desc; - int thrashing; - int max_thrashing; -}; - -/* Kill one process specified by procp. Returns the size (in pages) of the process killed */ -static int kill_one_process(struct proc* procp, int min_oom_score, struct kill_info *ki, - union meminfo *mi, struct wakeup_info *wi, struct timespec *tm) { +/* Kill one process specified by procp. Returns the size of the process killed */ +static int kill_one_process(struct proc* procp, int min_oom_score, int kill_reason, + const char *kill_desc, union meminfo *mi, struct timespec *tm) { int pid = procp->pid; int pidfd = procp->pidfd; uid_t uid = procp->uid; + int tgid; char *taskname; + int tasksize; int r; int result = -1; struct memory_stat *mem_st; - struct kill_stat kill_st; - int64_t tgid; - int64_t rss_kb; - int64_t swap_kb; - char buf[PAGE_SIZE]; + char buf[LINE_MAX]; - if (!read_proc_status(pid, buf, sizeof(buf))) { - goto out; - } - if (!parse_status_tag(buf, PROC_STATUS_TGID_FIELD, &tgid)) { - ALOGE("Unable to parse tgid from /proc/%d/status", pid); - goto out; - } - if (tgid != pid) { - ALOGE("Possible pid reuse detected (pid %d, tgid %" PRId64 ")!", pid, tgid); - goto out; - } - // Zombie processes will not have RSS / Swap fields. - if (!parse_status_tag(buf, PROC_STATUS_RSS_FIELD, &rss_kb)) { - goto out; - } - if (!parse_status_tag(buf, PROC_STATUS_SWAP_FIELD, &swap_kb)) { + tgid = proc_get_tgid(pid); + if (tgid >= 0 && tgid != pid) { + ALOGE("Possible pid reuse detected (pid %d, tgid %d)!", pid, tgid); goto out; } taskname = proc_get_name(pid, buf, sizeof(buf)); - // taskname will point inside buf, do not reuse buf onwards. if (!taskname) { goto out; } - mem_st = stats_read_memory_stat(per_app_memcg, pid, uid, rss_kb * 1024, swap_kb * 1024); + tasksize = proc_get_size(pid); + if (tasksize <= 0) { + goto out; + } + + mem_st = stats_read_memory_stat(per_app_memcg, pid, uid); TRACE_KILL_START(pid); @@ -2168,7 +2037,7 @@ static int kill_one_process(struct proc* procp, int min_oom_score, struct kill_i r = kill(pid, SIGKILL); } else { start_wait_for_proc_kill(pidfd); - r = pidfd_send_signal(pidfd, SIGKILL, NULL, 0); + r = sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0); } TRACE_KILL_END(); @@ -2186,34 +2055,21 @@ static int kill_one_process(struct proc* procp, int min_oom_score, struct kill_i inc_killcnt(procp->oomadj); - if (ki) { - kill_st.kill_reason = ki->kill_reason; - kill_st.thrashing = ki->thrashing; - kill_st.max_thrashing = ki->max_thrashing; - killinfo_log(procp, min_oom_score, rss_kb, swap_kb, ki->kill_reason, mi, wi, tm); - ALOGI("Kill '%s' (%d), uid %d, oom_score_adj %d to free %" PRId64 "kB rss, %" PRId64 - "kB swap; reason: %s", taskname, pid, uid, procp->oomadj, rss_kb, swap_kb, - ki->kill_desc); + killinfo_log(procp, min_oom_score, tasksize, kill_reason, mi); + + if (kill_desc) { + ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB; reason: %s", taskname, pid, + uid, procp->oomadj, tasksize * page_k, kill_desc); } else { - kill_st.kill_reason = NONE; - kill_st.thrashing = 0; - kill_st.max_thrashing = 0; - killinfo_log(procp, min_oom_score, rss_kb, swap_kb, NONE, mi, wi, tm); - ALOGI("Kill '%s' (%d), uid %d, oom_score_adj %d to free %" PRId64 "kB rss, %" PRId64 - "kb swap", taskname, pid, uid, procp->oomadj, rss_kb, swap_kb); - } - - kill_st.uid = static_cast<int32_t>(uid); - kill_st.taskname = taskname; - kill_st.oom_score = procp->oomadj; - kill_st.min_oom_score = min_oom_score; - kill_st.free_mem_kb = mi->field.nr_free_pages * page_k; - kill_st.free_swap_kb = mi->field.free_swap * page_k; - stats_write_lmk_kill_occurred(&kill_st, mem_st); + ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB", taskname, pid, + uid, procp->oomadj, tasksize * page_k); + } + + stats_write_lmk_kill_occurred(uid, taskname, procp->oomadj, min_oom_score, tasksize, mem_st); ctrl_data_write_lmk_kill_occurred((pid_t)pid, uid); - result = rss_kb / page_k; + result = tasksize; out: /* @@ -2225,39 +2081,31 @@ out: } /* - * Find one process to kill at or above the given oom_score_adj level. + * Find one process to kill at or above the given oom_adj level. * Returns size of the killed process. */ -static int find_and_kill_process(int min_score_adj, struct kill_info *ki, union meminfo *mi, - struct wakeup_info *wi, struct timespec *tm) { +static int find_and_kill_process(int min_score_adj, int kill_reason, const char *kill_desc, + union meminfo *mi, struct timespec *tm) { int i; int killed_size = 0; bool lmk_state_change_start = false; - bool choose_heaviest_task = kill_heaviest_task; for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) { struct proc *procp; - if (!choose_heaviest_task && i <= PERCEPTIBLE_APP_ADJ) { - /* - * If we have to choose a perceptible process, choose the heaviest one to - * hopefully minimize the number of victims. - */ - choose_heaviest_task = true; - } - while (true) { - procp = choose_heaviest_task ? + procp = kill_heaviest_task ? proc_get_heaviest(i) : proc_adj_lru(i); if (!procp) break; - killed_size = kill_one_process(procp, min_score_adj, ki, mi, wi, tm); + killed_size = kill_one_process(procp, min_score_adj, kill_reason, kill_desc, mi, tm); if (killed_size >= 0) { if (!lmk_state_change_start) { lmk_state_change_start = true; - stats_write_lmk_state_changed(STATE_START); + stats_write_lmk_state_changed( + android::lmkd::stats::LMK_STATE_CHANGED__STATE__START); } break; } @@ -2268,7 +2116,7 @@ static int find_and_kill_process(int min_score_adj, struct kill_info *ki, union } if (lmk_state_change_start) { - stats_write_lmk_state_changed(STATE_STOP); + stats_write_lmk_state_changed(android::lmkd::stats::LMK_STATE_CHANGED__STATE__STOP); } return killed_size; @@ -2382,34 +2230,32 @@ void calc_zone_watermarks(struct zoneinfo *zi, struct zone_watermarks *watermark } } -static int calc_swap_utilization(union meminfo *mi) { - int64_t swap_used = mi->field.total_swap - mi->field.free_swap; - int64_t total_swappable = mi->field.active_anon + mi->field.inactive_anon + - mi->field.shmem + swap_used; - return total_swappable > 0 ? (swap_used * 100) / total_swappable : 0; -} - static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_params) { + enum kill_reasons { + NONE = -1, /* To denote no kill condition */ + PRESSURE_AFTER_KILL = 0, + NOT_RESPONDING, + LOW_SWAP_AND_THRASHING, + LOW_MEM_AND_SWAP, + LOW_MEM_AND_THRASHING, + DIRECT_RECL_AND_THRASHING, + KILL_REASON_COUNT + }; enum reclaim_state { NO_RECLAIM = 0, KSWAPD_RECLAIM, DIRECT_RECLAIM, }; static int64_t init_ws_refault; - static int64_t prev_workingset_refault; static int64_t base_file_lru; static int64_t init_pgscan_kswapd; static int64_t init_pgscan_direct; static int64_t swap_low_threshold; static bool killing; - static int thrashing_limit = thrashing_limit_pct; + static int thrashing_limit; + static bool in_reclaim; static struct zone_watermarks watermarks; static struct timespec wmark_update_tm; - static struct wakeup_info wi; - static struct timespec thrashing_reset_tm; - static int64_t prev_thrash_growth = 0; - static bool check_filecache = false; - static int max_thrashing = 0; union meminfo mi; union vmstat vs; @@ -2424,22 +2270,16 @@ static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_ char kill_desc[LINE_MAX]; bool cut_thrashing_limit = false; int min_score_adj = 0; - int swap_util = 0; - long since_thrashing_reset_ms; - int64_t workingset_refault_file; if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) { ALOGE("Failed to get current time"); return; } - record_wakeup_time(&curr_tm, events ? Event : Polling, &wi); - bool kill_pending = is_kill_pending(); if (kill_pending && (kill_timeout_ms == 0 || get_time_diff_ms(&last_kill_tm, &curr_tm) < static_cast<long>(kill_timeout_ms))) { /* Skip while still killing a process */ - wi.skipped_wakeups++; goto no_kill; } /* @@ -2452,8 +2292,6 @@ static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_ ALOGE("Failed to parse vmstat!"); return; } - /* Starting 5.9 kernel workingset_refault vmstat field was renamed workingset_refault_file */ - workingset_refault_file = vs.field.workingset_refault ? : vs.field.workingset_refault_file; if (meminfo_parse(&mi) < 0) { ALOGE("Failed to parse meminfo!"); @@ -2466,9 +2304,7 @@ static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_ cycle_after_kill = true; /* Reset file-backed pagecache size and refault amounts after a kill */ base_file_lru = vs.field.nr_inactive_file + vs.field.nr_active_file; - init_ws_refault = workingset_refault_file; - thrashing_reset_tm = curr_tm; - prev_thrash_growth = 0; + init_ws_refault = vs.field.workingset_refault; } /* Check free swap levels */ @@ -2487,57 +2323,22 @@ static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_ } else if (vs.field.pgscan_kswapd > init_pgscan_kswapd) { init_pgscan_kswapd = vs.field.pgscan_kswapd; reclaim = KSWAPD_RECLAIM; - } else if (workingset_refault_file == prev_workingset_refault) { - /* - * Device is not thrashing and not reclaiming, bail out early until we see these stats - * changing - */ + } else { + in_reclaim = false; + /* Skip if system is not reclaiming */ goto no_kill; } - prev_workingset_refault = workingset_refault_file; - - /* - * It's possible we fail to find an eligible process to kill (ex. no process is - * above oom_adj_min). When this happens, we should retry to find a new process - * for a kill whenever a new eligible process is available. This is especially - * important for a slow growing refault case. While retrying, we should keep - * monitoring new thrashing counter as someone could release the memory to mitigate - * the thrashing. Thus, when thrashing reset window comes, we decay the prev thrashing - * counter by window counts. If the counter is still greater than thrashing limit, - * we preserve the current prev_thrash counter so we will retry kill again. Otherwise, - * we reset the prev_thrash counter so we will stop retrying. - */ - since_thrashing_reset_ms = get_time_diff_ms(&thrashing_reset_tm, &curr_tm); - if (since_thrashing_reset_ms > THRASHING_RESET_INTERVAL_MS) { - long windows_passed; - /* Calculate prev_thrash_growth if we crossed THRASHING_RESET_INTERVAL_MS */ - prev_thrash_growth = (workingset_refault_file - init_ws_refault) * 100 - / (base_file_lru + 1); - windows_passed = (since_thrashing_reset_ms / THRASHING_RESET_INTERVAL_MS); - /* - * Decay prev_thrashing unless over-the-limit thrashing was registered in the window we - * just crossed, which means there were no eligible processes to kill. We preserve the - * counter in that case to ensure a kill if a new eligible process appears. - */ - if (windows_passed > 1 || prev_thrash_growth < thrashing_limit) { - prev_thrash_growth >>= windows_passed; - } - - /* Record file-backed pagecache size when crossing THRASHING_RESET_INTERVAL_MS */ + if (!in_reclaim) { + /* Record file-backed pagecache size when entering reclaim cycle */ base_file_lru = vs.field.nr_inactive_file + vs.field.nr_active_file; - init_ws_refault = workingset_refault_file; - thrashing_reset_tm = curr_tm; + init_ws_refault = vs.field.workingset_refault; thrashing_limit = thrashing_limit_pct; } else { /* Calculate what % of the file-backed pagecache refaulted so far */ - thrashing = (workingset_refault_file - init_ws_refault) * 100 / (base_file_lru + 1); - } - /* Add previous cycle's decayed thrashing amount */ - thrashing += prev_thrash_growth; - if (max_thrashing < thrashing) { - max_thrashing = thrashing; + thrashing = (vs.field.workingset_refault - init_ws_refault) * 100 / base_file_lru; } + in_reclaim = true; /* * Refresh watermarks once per min in case user updated one of the margins. @@ -2554,7 +2355,7 @@ static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_ calc_zone_watermarks(&zi, &watermarks); wmark_update_tm = curr_tm; - } + } /* Find out which watermark is breached if any */ wmark = get_lowest_watermark(&mi, &watermarks); @@ -2585,81 +2386,44 @@ static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_ snprintf(kill_desc, sizeof(kill_desc), "device is low on swap (%" PRId64 "kB < %" PRId64 "kB) and thrashing (%" PRId64 "%%)", mi.field.free_swap * page_k, swap_low_threshold * page_k, thrashing); - /* Do not kill perceptible apps unless below min watermark or heavily thrashing */ - if (wmark > WMARK_MIN && thrashing < thrashing_critical_pct) { + /* Do not kill perceptible apps unless below min watermark */ + if (wmark > WMARK_MIN) { min_score_adj = PERCEPTIBLE_APP_ADJ + 1; } - check_filecache = true; } else if (swap_is_low && wmark < WMARK_HIGH) { /* Both free memory and swap are low */ kill_reason = LOW_MEM_AND_SWAP; snprintf(kill_desc, sizeof(kill_desc), "%s watermark is breached and swap is low (%" - PRId64 "kB < %" PRId64 "kB)", wmark < WMARK_LOW ? "min" : "low", + PRId64 "kB < %" PRId64 "kB)", wmark > WMARK_LOW ? "min" : "low", mi.field.free_swap * page_k, swap_low_threshold * page_k); - /* Do not kill perceptible apps unless below min watermark or heavily thrashing */ - if (wmark > WMARK_MIN && thrashing < thrashing_critical_pct) { + /* Do not kill perceptible apps unless below min watermark */ + if (wmark > WMARK_MIN) { min_score_adj = PERCEPTIBLE_APP_ADJ + 1; } - } else if (wmark < WMARK_HIGH && swap_util_max < 100 && - (swap_util = calc_swap_utilization(&mi)) > swap_util_max) { - /* - * Too much anon memory is swapped out but swap is not low. - * Non-swappable allocations created memory pressure. - */ - kill_reason = LOW_MEM_AND_SWAP_UTIL; - snprintf(kill_desc, sizeof(kill_desc), "%s watermark is breached and swap utilization" - " is high (%d%% > %d%%)", wmark < WMARK_LOW ? "min" : "low", - swap_util, swap_util_max); } else if (wmark < WMARK_HIGH && thrashing > thrashing_limit) { /* Page cache is thrashing while memory is low */ kill_reason = LOW_MEM_AND_THRASHING; snprintf(kill_desc, sizeof(kill_desc), "%s watermark is breached and thrashing (%" - PRId64 "%%)", wmark < WMARK_LOW ? "min" : "low", thrashing); + PRId64 "%%)", wmark > WMARK_LOW ? "min" : "low", thrashing); cut_thrashing_limit = true; - /* Do not kill perceptible apps unless thrashing at critical levels */ - if (thrashing < thrashing_critical_pct) { - min_score_adj = PERCEPTIBLE_APP_ADJ + 1; - } - check_filecache = true; + /* Do not kill perceptible apps because of thrashing */ + min_score_adj = PERCEPTIBLE_APP_ADJ + 1; } else if (reclaim == DIRECT_RECLAIM && thrashing > thrashing_limit) { /* Page cache is thrashing while in direct reclaim (mostly happens on lowram devices) */ kill_reason = DIRECT_RECL_AND_THRASHING; snprintf(kill_desc, sizeof(kill_desc), "device is in direct reclaim and thrashing (%" PRId64 "%%)", thrashing); cut_thrashing_limit = true; - /* Do not kill perceptible apps unless thrashing at critical levels */ - if (thrashing < thrashing_critical_pct) { - min_score_adj = PERCEPTIBLE_APP_ADJ + 1; - } - check_filecache = true; - } else if (check_filecache) { - int64_t file_lru_kb = (vs.field.nr_inactive_file + vs.field.nr_active_file) * page_k; - - if (file_lru_kb < filecache_min_kb) { - /* File cache is too low after thrashing, keep killing background processes */ - kill_reason = LOW_FILECACHE_AFTER_THRASHING; - snprintf(kill_desc, sizeof(kill_desc), - "filecache is low (%" PRId64 "kB < %" PRId64 "kB) after thrashing", - file_lru_kb, filecache_min_kb); - min_score_adj = PERCEPTIBLE_APP_ADJ + 1; - } else { - /* File cache is big enough, stop checking */ - check_filecache = false; - } + /* Do not kill perceptible apps because of thrashing */ + min_score_adj = PERCEPTIBLE_APP_ADJ + 1; } /* Kill a process if necessary */ if (kill_reason != NONE) { - struct kill_info ki = { - .kill_reason = kill_reason, - .kill_desc = kill_desc, - .thrashing = (int)thrashing, - .max_thrashing = max_thrashing, - }; - int pages_freed = find_and_kill_process(min_score_adj, &ki, &mi, &wi, &curr_tm); + int pages_freed = find_and_kill_process(min_score_adj, kill_reason, kill_desc, &mi, + &curr_tm); if (pages_freed > 0) { killing = true; - max_thrashing = 0; if (cut_thrashing_limit) { /* * Cut thrasing limit by thrashing_limit_decay_pct percentage of the current @@ -2718,7 +2482,6 @@ static void mp_event_common(int data, uint32_t events, struct polling_params *po .filename = MEMCG_MEMORYSW_USAGE, .fd = -1, }; - static struct wakeup_info wi; if (debug_process_killing) { ALOGI("%s memory pressure event is triggered", level_name[level]); @@ -2754,8 +2517,6 @@ static void mp_event_common(int data, uint32_t events, struct polling_params *po return; } - record_wakeup_time(&curr_tm, events ? Event : Polling, &wi); - if (kill_timeout_ms && get_time_diff_ms(&last_kill_tm, &curr_tm) < static_cast<long>(kill_timeout_ms)) { /* @@ -2764,7 +2525,6 @@ static void mp_event_common(int data, uint32_t events, struct polling_params *po */ if (is_kill_pending()) { kill_skip_count++; - wi.skipped_wakeups++; return; } /* @@ -2876,7 +2636,7 @@ static void mp_event_common(int data, uint32_t events, struct polling_params *po do_kill: if (low_ram_device) { /* For Go devices kill only one task */ - if (find_and_kill_process(level_oomadj[level], NULL, &mi, &wi, &curr_tm) == 0) { + if (find_and_kill_process(level_oomadj[level], -1, NULL, &mi, &curr_tm) == 0) { if (debug_process_killing) { ALOGI("Nothing to kill"); } @@ -2899,7 +2659,7 @@ do_kill: min_score_adj = level_oomadj[level]; } - pages_freed = find_and_kill_process(min_score_adj, NULL, &mi, &wi, &curr_tm); + pages_freed = find_and_kill_process(min_score_adj, -1, NULL, &mi, &curr_tm); if (pages_freed == 0) { /* Rate limit kill reports when nothing was reclaimed */ @@ -2911,14 +2671,15 @@ do_kill: /* Log whenever we kill or when report rate limit allows */ if (use_minfree_levels) { - ALOGI("Reclaimed %ldkB, cache(%ldkB) and free(%" PRId64 "kB)-reserved(%" PRId64 "kB) " - "below min(%ldkB) for oom_score_adj %d", + ALOGI("Reclaimed %ldkB, cache(%ldkB) and " + "free(%" PRId64 "kB)-reserved(%" PRId64 "kB) below min(%ldkB) for oom_adj %d", pages_freed * page_k, other_file * page_k, mi.field.nr_free_pages * page_k, zi.totalreserve_pages * page_k, minfree * page_k, min_score_adj); } else { - ALOGI("Reclaimed %ldkB at oom_score_adj %d", pages_freed * page_k, min_score_adj); + ALOGI("Reclaimed %ldkB at oom_adj %d", + pages_freed * page_k, min_score_adj); } if (report_skip_count > 0) { @@ -3220,7 +2981,7 @@ static int init(void) { } /* check if kernel supports pidfd_open syscall */ - pidfd = TEMP_FAILURE_RETRY(pidfd_open(getpid(), 0)); + pidfd = TEMP_FAILURE_RETRY(sys_pidfd_open(getpid(), 0)); if (pidfd < 0) { pidfd_supported = (errno != ENOSYS); } else { @@ -3239,8 +3000,6 @@ static bool polling_paused(struct polling_params *poll_params) { static void resume_polling(struct polling_params *poll_params, struct timespec curr_tm) { poll_params->poll_start_tm = curr_tm; poll_params->poll_handler = poll_params->paused_handler; - poll_params->polling_interval_ms = PSI_POLL_PERIOD_SHORT_MS; - poll_params->paused_handler = NULL; } static void call_handler(struct event_handler_info* handler_info, @@ -3275,6 +3034,7 @@ static void call_handler(struct event_handler_info* handler_info, if (get_time_diff_ms(&poll_params->poll_start_tm, &curr_tm) > PSI_WINDOW_SIZE_MS) { /* Polled for the duration of PSI window, time to stop */ poll_params->poll_handler = NULL; + poll_params->paused_handler = NULL; } break; } @@ -3299,8 +3059,12 @@ static void mainloop(void) { bool poll_now; clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm); - if (poll_params.update == POLLING_RESUME) { - /* Just transitioned into POLLING_RESUME, poll immediately. */ + if (poll_params.poll_handler == poll_params.paused_handler) { + /* + * Just transitioned into POLLING_RESUME. Reset paused_handler + * and poll immediately + */ + poll_params.paused_handler = NULL; poll_now = true; nevents = 0; } else { @@ -3331,7 +3095,6 @@ static void mainloop(void) { stop_wait_for_proc_kill(false); if (polling_paused(&poll_params)) { clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm); - poll_params.update = POLLING_RESUME; resume_polling(&poll_params, curr_tm); } } @@ -3433,7 +3196,7 @@ static void update_props() { property_get_bool("ro.lmk.kill_heaviest_task", false); low_ram_device = property_get_bool("ro.config.low_ram", false); kill_timeout_ms = - (unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 100); + (unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0); use_minfree_levels = property_get_bool("ro.lmk.use_minfree_levels", false); per_app_memcg = @@ -3448,10 +3211,6 @@ static void update_props() { low_ram_device ? DEF_THRASHING_LOWRAM : DEF_THRASHING)); thrashing_limit_decay_pct = clamp(0, 100, property_get_int32("ro.lmk.thrashing_limit_decay", low_ram_device ? DEF_THRASHING_DECAY_LOWRAM : DEF_THRASHING_DECAY)); - thrashing_critical_pct = max(0, property_get_int32("ro.lmk.thrashing_limit_critical", - thrashing_limit_pct * 2)); - swap_util_max = clamp(0, 100, property_get_int32("ro.lmk.swap_util_max", 100)); - filecache_min_kb = property_get_int64("ro.lmk.filecache_min_kb", 0); } int main(int argc, char **argv) { diff --git a/statslog.cpp b/statslog.cpp index 6568f73..8fb441c 100644 --- a/statslog.cpp +++ b/statslog.cpp @@ -16,7 +16,6 @@ #include <assert.h> #include <errno.h> -#include <fcntl.h> #include <log/log.h> #include <log/log_id.h> #include <statslog.h> @@ -24,27 +23,20 @@ #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); +static bool enable_stats_log = property_get_bool("ro.lmk.log_stats", false); struct proc { int pid; - char taskname[MAX_TASKNAME_LEN]; + char taskname[LINE_MAX]; struct proc* pidhash_next; }; @@ -52,6 +44,20 @@ struct proc { 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; @@ -63,11 +69,48 @@ static struct proc* pid_lookup(int pid) { return procp; } +/** + * Logs the event when LMKD kills a process to reduce memory pressure. + * Code: LMK_KILL_OCCURRED = 51 + */ +int +stats_write_lmk_kill_occurred(int32_t uid, char const* process_name, + int32_t oom_score, int32_t min_oom_score, int tasksize, + struct memory_stat *mem_st) { + if (enable_stats_log) { + return android::lmkd::stats::stats_write( + android::lmkd::stats::LMK_KILL_OCCURRED, + uid, + process_name, + oom_score, + mem_st ? mem_st->pgfault : -1, + mem_st ? mem_st->pgmajfault : -1, + mem_st ? mem_st->rss_in_bytes : tasksize * BYTES_IN_KILOBYTE, + 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, + min_oom_score + ); + } else { + return -EINVAL; + } +} + +int stats_write_lmk_kill_occurred_pid(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(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[MAX_TASKNAME_LEN + 1]; + char key[LINE_MAX + 1]; int64_t value; - sscanf(line, "%" STRINGIFY(MAX_TASKNAME_LEN) "s %" SCNd64 "", key, &value); + sscanf(line, "%" STRINGIFY(LINE_MAX) "s %" SCNd64 "", key, &value); if (strcmp(key, "total_") < 0) { return; @@ -125,24 +168,26 @@ static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) { // field 10 is pgfault // field 12 is pgmajfault // field 22 is starttime - int64_t pgfault = 0, pgmajfault = 0, starttime = 0; + // 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 "", - &pgfault, &pgmajfault, &starttime) != 3) { + "%" 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, - int64_t rss_bytes, int64_t swap_bytes) { +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; } @@ -153,8 +198,6 @@ struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t ui } } else { if (memory_stat_from_procfs(&mem_st, pid) == 0) { - mem_st.rss_in_bytes = rss_bytes; - mem_st.swap_in_bytes = swap_bytes; return &mem_st; } } @@ -197,7 +240,7 @@ void stats_remove_taskname(int pid) { } void stats_store_taskname(int pid, const char* taskname) { - if (!enable_stats_log || !taskname) { + if (!enable_stats_log) { return; } @@ -210,8 +253,8 @@ void stats_store_taskname(int pid, const char* taskname) { } procp = static_cast<struct proc*>(malloc(sizeof(struct proc))); procp->pid = pid; - strncpy(procp->taskname, taskname, MAX_TASKNAME_LEN - 1); - procp->taskname[MAX_TASKNAME_LEN - 1] = '\0'; + strncpy(procp->taskname, taskname, LINE_MAX - 1); + procp->taskname[LINE_MAX - 1] = '\0'; proc_insert(procp); } @@ -234,112 +277,4 @@ void stats_purge_tasknames() { 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_int32(packet, index, kill_stat->thrashing); - index = pack_int32(packet, index, kill_stat->max_thrashing); - - 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 */ @@ -17,10 +17,9 @@ #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> @@ -29,21 +28,6 @@ __BEGIN_DECLS -#define MAX_TASKNAME_LEN 128 - -/* - * Max LMKD reply packet length in bytes - * Notes about size calculation: - * 4 bytes for packet type - * 88 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 222 - -/* LMK_MEMORY_STATS packet payload */ struct memory_stat { int64_t pgfault; int64_t pgmajfault; @@ -53,71 +37,39 @@ struct memory_stat { int64_t process_start_time_ns; }; -// 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, - NOT_RESPONDING, - LOW_SWAP_AND_THRASHING, - LOW_MEM_AND_SWAP, - LOW_MEM_AND_THRASHING, - DIRECT_RECL_AND_THRASHING, - LOW_MEM_AND_SWAP_UTIL, - LOW_FILECACHE_AFTER_THRASHING, - KILL_REASON_COUNT -}; - -/* LMK_KILL_STAT packet payload */ -struct kill_stat { - int32_t uid; - const char *taskname; - enum kill_reasons kill_reason; - int32_t oom_score; - int32_t min_oom_score; - int64_t free_mem_kb; - int64_t free_swap_kb; - int32_t thrashing; - int32_t max_thrashing; -}; - -/* 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" +#define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%u/memory.stat" #define PROC_STAT_FILE_PATH "/proc/%d/stat" #define PROC_STAT_BUFFER_SIZE 1024 #define BYTES_IN_KILOBYTE 1024 /** - * Produces packet with the change in LMKD state which is used as start/stop boundaries for logging + * Logs the change in LMKD state which is used as start/stop boundaries for logging * LMK_KILL_OCCURRED event. * Code: LMK_STATE_CHANGED = 54 */ -size_t lmkd_pack_set_state_changed(LMKD_CTRL_PACKET packet, - enum lmk_state state); +int +stats_write_lmk_state_changed(int32_t state); /** - * Produces packet with the event when LMKD kills a process to reduce memory pressure. + * Logs the event when LMKD kills a process to reduce memory pressure. * Code: LMK_KILL_OCCURRED = 51 */ -size_t lmkd_pack_set_kill_occurred(LMK_KILL_OCCURRED_PACKET packet, - struct kill_stat *kill_st, - struct memory_stat *mem_st); +int +stats_write_lmk_kill_occurred(int32_t uid, char const* process_name, + int32_t oom_score, int32_t min_oom_score, + int tasksize, struct memory_stat *mem_st); /** - * Reads memory stats used to log the statsd atom. Returns non-null ptr on success. + * Logs the event when LMKD kills a process to reduce memory pressure. + * Code: LMK_KILL_OCCURRED = 51 */ -struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t uid, - int64_t rss_bytes, int64_t swap_bytes); +int stats_write_lmk_kill_occurred_pid(int32_t uid, int pid, int32_t oom_score, + int32_t min_oom_score, int tasksize, + struct memory_stat* mem_st); + +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. @@ -134,27 +86,27 @@ void stats_purge_tasknames(); */ void stats_remove_taskname(int pid); -const char* stats_get_task_name(int pid); - #else /* LMKD_LOG_STATS */ -static inline size_t -lmkd_pack_set_state_changed(LMKD_CTRL_PACKET packet __unused, enum lmk_state state __unused) { - return -EINVAL; -} +static inline int +stats_write_lmk_state_changed(int32_t state __unused) { return -EINVAL; } + +static inline int +stats_write_lmk_kill_occurred(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 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) { +static inline int stats_write_lmk_kill_occurred_pid(int32_t uid __unused, + int pid __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, - int64_t rss_bytes __unused, int64_t swap_bytes __unused) { - return NULL; -} + int pid __unused, uid_t uid __unused) { return NULL; } static inline void stats_store_taskname(int pid __unused, const char* taskname __unused) {} @@ -162,8 +114,6 @@ static inline void stats_purge_tasknames() {} 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 diff --git a/tests/Android.bp b/tests/Android.bp index dfbe0c7..4e845fd 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -12,10 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - cc_test { name: "lmkd_unit_test", diff --git a/tests/lmkd_test.cpp b/tests/lmkd_test.cpp index 07d91bb..5dbf6db 100644 --- a/tests/lmkd_test.cpp +++ b/tests/lmkd_test.cpp @@ -27,7 +27,6 @@ #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> -#include <cutils/properties.h> #include <gtest/gtest.h> #include <lmkd.h> #include <liblmkd_utils.h> @@ -252,7 +251,6 @@ void runMemStressTest() { params.pid = pid; params.uid = uid; params.oomadj = data->oomadj; - params.ptype = PROC_TYPE_APP; ASSERT_FALSE(lmkd_register_proc(sock, ¶ms) < 0) << "Failed to communicate with lmkd, err=" << strerror(errno); // signal the child it can proceed @@ -275,10 +273,8 @@ void runMemStressTest() { << data->oomadj; data->allocated = 0; data->finished = false; - if (property_get_bool("ro.config.low_ram", false)) { - ASSERT_FALSE(create_memcg(uid, pid) != 0) - << "Child [pid=" << pid << "] failed to create a cgroup"; - } + ASSERT_FALSE(create_memcg(uid, pid) != 0) + << "Child [pid=" << pid << "] failed to create a cgroup"; signal_state(ssync, STATE_CHILD_READY); wait_for_state(ssync, STATE_PARENT_READY); add_pressure(data); |