aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
l---------.clang-format1
-rw-r--r--Android.bp52
-rw-r--r--event.logtags2
-rw-r--r--include/lmkd.h21
-rw-r--r--libpsi/Android.bp7
-rw-r--r--libpsi/psi.cpp5
-rw-r--r--lmkd.cpp505
-rw-r--r--statslog.cpp197
-rw-r--r--statslog.h112
-rw-r--r--tests/Android.bp4
-rw-r--r--tests/lmkd_test.cpp8
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
diff --git a/Android.bp b/Android.bp
index 4f53ded..f177b0d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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"
diff --git a/lmkd.cpp b/lmkd.cpp
index 2f0df91..882ae4a 100644
--- a/lmkd.cpp
+++ b/lmkd.cpp
@@ -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, &params);
@@ -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 */
diff --git a/statslog.h b/statslog.h
index 89e4d2e..9cba6b2 100644
--- a/statslog.h
+++ b/statslog.h
@@ -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, &params) < 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);