From d4a29903c0aa25c978ff0d1fe6d454793c125c98 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Fri, 12 Oct 2018 11:07:40 -0700 Subject: [PATCH] lmkd: Add command to get number of kills Intrduce LMK_GETKILLCNT command for ActivityManager to get the number of kills from lmkd. Bug: 117126077 Test: used lmkd_unit_test to verify correct reporting Change-Id: I09c720a7176b4df95efc544177cd2694f8d791be Signed-off-by: Suren Baghdasaryan --- lmkd/include/lmkd.h | 39 +++++++++++++++++ lmkd/lmkd.c | 103 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h index e8f51da77..e72d15992 100644 --- a/lmkd/include/lmkd.h +++ b/lmkd/include/lmkd.h @@ -31,6 +31,7 @@ enum lmk_cmd { 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 */ }; /* @@ -152,6 +153,44 @@ inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) { return sizeof(int); } +/* LMK_GETKILLCNT packet payload */ +struct lmk_getkillcnt { + int min_oomadj; + int max_oomadj; +}; + +/* + * For LMK_GETKILLCNT packet get its payload. + * Warning: no checks performed, caller should ensure valid parameters. + */ +inline void lmkd_pack_get_getkillcnt(LMKD_CTRL_PACKET packet, + struct lmk_getkillcnt *params) { + params->min_oomadj = ntohl(packet[1]); + params->max_oomadj = ntohl(packet[2]); +} + +/* + * Prepare LMK_GETKILLCNT packet and return packet size in bytes. + * Warning: no checks performed, caller should ensure valid parameters. + */ +inline size_t lmkd_pack_set_getkillcnt(LMKD_CTRL_PACKET packet, + struct lmk_getkillcnt *params) { + packet[0] = htonl(LMK_GETKILLCNT); + packet[1] = htonl(params->min_oomadj); + packet[2] = htonl(params->max_oomadj); + return 3 * sizeof(int); +} + +/* + * Prepare LMK_GETKILLCNT reply packet and return packet size in bytes. + * Warning: no checks performed, caller should ensure valid parameters. + */ +inline size_t lmkd_pack_set_getkillcnt_repl(LMKD_CTRL_PACKET packet, int kill_cnt) { + packet[0] = htonl(LMK_GETKILLCNT); + packet[1] = htonl(kill_cnt); + return 2 * sizeof(int); +} + __END_DECLS #endif /* _LMKD_H_ */ diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c index 0a469e804..e3c4ccc99 100644 --- a/lmkd/lmkd.c +++ b/lmkd/lmkd.c @@ -313,7 +313,20 @@ static struct proc *pidhash[PIDHASH_SZ]; #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) #define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN) -static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1]; +#define ADJTOSLOT_COUNT (ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1) +static struct adjslot_list procadjslot_list[ADJTOSLOT_COUNT]; + +#define MAX_DISTINCT_OOM_ADJ 32 +#define KILLCNT_INVALID_IDX 0xFF +/* + * Because killcnt array is sparse a two-level indirection is used + * to keep the size small. killcnt_idx stores index of the element in + * killcnt array. Index KILLCNT_INVALID_IDX indicates an unused slot. + */ +static uint8_t killcnt_idx[ADJTOSLOT_COUNT]; +static uint16_t killcnt[MAX_DISTINCT_OOM_ADJ]; +static int killcnt_free_idx = 0; +static uint32_t killcnt_total = 0; /* PAGE_SIZE / 1024 */ static long page_k; @@ -644,6 +657,67 @@ static void cmd_procpurge() { memset(&pidhash[0], 0, sizeof(pidhash)); } +static void inc_killcnt(int oomadj) { + int slot = ADJTOSLOT(oomadj); + uint8_t idx = killcnt_idx[slot]; + + if (idx == KILLCNT_INVALID_IDX) { + /* index is not assigned for this oomadj */ + if (killcnt_free_idx < MAX_DISTINCT_OOM_ADJ) { + killcnt_idx[slot] = killcnt_free_idx; + killcnt[killcnt_free_idx] = 1; + killcnt_free_idx++; + } else { + ALOGW("Number of distinct oomadj levels exceeds %d", + MAX_DISTINCT_OOM_ADJ); + } + } else { + /* + * wraparound is highly unlikely and is detectable using total + * counter because it has to be equal to the sum of all counters + */ + killcnt[idx]++; + } + /* increment total kill counter */ + killcnt_total++; +} + +static int get_killcnt(int min_oomadj, int max_oomadj) { + int slot; + int count = 0; + + if (min_oomadj > max_oomadj) + return 0; + + /* special case to get total kill count */ + if (min_oomadj > OOM_SCORE_ADJ_MAX) + return killcnt_total; + + while (min_oomadj <= max_oomadj && + (slot = ADJTOSLOT(min_oomadj)) < ADJTOSLOT_COUNT) { + uint8_t idx = killcnt_idx[slot]; + if (idx != KILLCNT_INVALID_IDX) { + count += killcnt[idx]; + } + min_oomadj++; + } + + return count; +} + +static int cmd_getkillcnt(LMKD_CTRL_PACKET packet) { + struct lmk_getkillcnt params; + + if (use_inkernel_interface) { + /* kernel driver does not expose this information */ + return 0; + } + + lmkd_pack_get_getkillcnt(packet, ¶ms); + + return get_killcnt(params.min_oomadj, params.max_oomadj); +} + static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) { int i; struct lmk_target target; @@ -748,12 +822,28 @@ static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) { return ret; } +static int ctrl_data_write(int dsock_idx, char *buf, size_t bufsz) { + int ret = 0; + + ret = TEMP_FAILURE_RETRY(write(data_sock[dsock_idx].sock, buf, bufsz)); + + if (ret == -1) { + ALOGE("control data socket write failed; errno=%d", errno); + } else if (ret == 0) { + ALOGE("Got EOF on control data socket"); + ret = -1; + } + + return ret; +} + static void ctrl_command_handler(int dsock_idx) { LMKD_CTRL_PACKET packet; int len; enum lmk_cmd cmd; int nargs; int targets; + int kill_cnt; len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE); if (len <= 0) @@ -791,6 +881,14 @@ static void ctrl_command_handler(int dsock_idx) { goto wronglen; cmd_procpurge(); break; + case LMK_GETKILLCNT: + if (nargs != 2) + goto wronglen; + kill_cnt = cmd_getkillcnt(packet); + len = lmkd_pack_set_getkillcnt_repl(packet, kill_cnt); + if (ctrl_data_write(dsock_idx, (char *)packet, len) != len) + return; + break; default: ALOGE("Received unknown command code %d", cmd); return; @@ -1200,6 +1298,7 @@ static int kill_one_process(struct proc* procp) { /* CAP_KILL required */ r = kill(pid, SIGKILL); + inc_killcnt(procp->oomadj); ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB", taskname, pid, uid, procp->oomadj, tasksize * page_k); @@ -1700,6 +1799,8 @@ static int init(void) { procadjslot_list[i].prev = &procadjslot_list[i]; } + memset(killcnt_idx, KILLCNT_INVALID_IDX, sizeof(killcnt_idx)); + return 0; }