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 <surenb@google.com>
This commit is contained in:
Suren Baghdasaryan 2018-10-12 11:07:40 -07:00
parent ac305c82d6
commit d4a29903c0
2 changed files with 141 additions and 1 deletions

View file

@ -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_ */

View file

@ -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, &params);
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;
}