lmkd: Isolate statslog related code from lmkd code
Move statsd related code out of lmkd.c to minimize ifdefs sprinkled around the code and make it more maintainable. Bug: 74119935 Test: lmkd_unit_test Change-Id: Ib22f90fd380b9a31e09ab18ef16787bc07415ddf Signed-off-by: Suren Baghdasaryan <surenb@google.com>
This commit is contained in:
parent
c2e05b6ffa
commit
8b016be930
21
Android.bp
21
Android.bp
|
|
@ -1,3 +1,15 @@
|
||||||
|
cc_defaults {
|
||||||
|
name: "stats_defaults",
|
||||||
|
|
||||||
|
product_variables: {
|
||||||
|
use_lmkd_stats_log: {
|
||||||
|
cflags: [
|
||||||
|
"-DLMKD_LOG_STATS"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
cc_binary {
|
cc_binary {
|
||||||
name: "lmkd",
|
name: "lmkd",
|
||||||
|
|
||||||
|
|
@ -15,13 +27,7 @@ cc_binary {
|
||||||
local_include_dirs: ["include"],
|
local_include_dirs: ["include"],
|
||||||
cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
|
cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
|
||||||
init_rc: ["lmkd.rc"],
|
init_rc: ["lmkd.rc"],
|
||||||
product_variables: {
|
defaults: ["stats_defaults"],
|
||||||
use_lmkd_stats_log: {
|
|
||||||
cflags: [
|
|
||||||
"-DLMKD_LOG_STATS"
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
logtags: ["event.logtags"],
|
logtags: ["event.logtags"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,6 +38,7 @@ cc_library_static {
|
||||||
"-Wall",
|
"-Wall",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
],
|
],
|
||||||
|
defaults: ["stats_defaults"],
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
"liblog",
|
"liblog",
|
||||||
],
|
],
|
||||||
|
|
|
||||||
359
lmkd.c
359
lmkd.c
|
|
@ -47,9 +47,7 @@
|
||||||
#include <psi/psi.h>
|
#include <psi/psi.h>
|
||||||
#include <system/thread_defs.h>
|
#include <system/thread_defs.h>
|
||||||
|
|
||||||
#ifdef LMKD_LOG_STATS
|
|
||||||
#include "statslog.h"
|
#include "statslog.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
|
* Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
|
||||||
|
|
@ -186,6 +184,7 @@ static int psi_complete_stall_ms;
|
||||||
static int thrashing_limit_pct;
|
static int thrashing_limit_pct;
|
||||||
static int thrashing_limit_decay_pct;
|
static int thrashing_limit_decay_pct;
|
||||||
static bool use_psi_monitors = false;
|
static bool use_psi_monitors = false;
|
||||||
|
static struct kernel_poll_info kpoll_info;
|
||||||
static struct psi_threshold psi_thresholds[VMPRESS_LEVEL_COUNT] = {
|
static struct psi_threshold psi_thresholds[VMPRESS_LEVEL_COUNT] = {
|
||||||
{ PSI_SOME, 70 }, /* 70ms out of 1sec for partial stall */
|
{ PSI_SOME, 70 }, /* 70ms out of 1sec for partial stall */
|
||||||
{ PSI_SOME, 100 }, /* 100ms out of 1sec for partial stall */
|
{ PSI_SOME, 100 }, /* 100ms out of 1sec for partial stall */
|
||||||
|
|
@ -477,11 +476,6 @@ struct reread_data {
|
||||||
int fd;
|
int fd;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef LMKD_LOG_STATS
|
|
||||||
static bool enable_stats_log;
|
|
||||||
static android_log_context log_ctx;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define PIDHASH_SZ 1024
|
#define PIDHASH_SZ 1024
|
||||||
static struct proc *pidhash[PIDHASH_SZ];
|
static struct proc *pidhash[PIDHASH_SZ];
|
||||||
#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
|
#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
|
||||||
|
|
@ -505,9 +499,6 @@ static uint32_t killcnt_total = 0;
|
||||||
/* PAGE_SIZE / 1024 */
|
/* PAGE_SIZE / 1024 */
|
||||||
static long page_k;
|
static long page_k;
|
||||||
|
|
||||||
static char* proc_get_name(int pid);
|
|
||||||
static void poll_kernel();
|
|
||||||
|
|
||||||
static int clamp(int low, int high, int value) {
|
static int clamp(int low, int high, int value) {
|
||||||
return max(min(value, high), low);
|
return max(min(value, high), low);
|
||||||
}
|
}
|
||||||
|
|
@ -772,6 +763,60 @@ out:
|
||||||
return (int)tgid;
|
return (int)tgid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int proc_get_size(int pid) {
|
||||||
|
char path[PATH_MAX];
|
||||||
|
char line[LINE_MAX];
|
||||||
|
int fd;
|
||||||
|
int rss = 0;
|
||||||
|
int total;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
/* gid containing AID_READPROC required */
|
||||||
|
snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
|
||||||
|
fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||||
|
if (fd == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ret = read_all(fd, line, sizeof(line) - 1);
|
||||||
|
if (ret < 0) {
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sscanf(line, "%d %d ", &total, &rss);
|
||||||
|
close(fd);
|
||||||
|
return rss;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *proc_get_name(int pid) {
|
||||||
|
char path[PATH_MAX];
|
||||||
|
static char line[LINE_MAX];
|
||||||
|
int fd;
|
||||||
|
char *cp;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
/* gid containing AID_READPROC required */
|
||||||
|
snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
|
||||||
|
fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||||
|
if (fd == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ret = read_all(fd, line, sizeof(line) - 1);
|
||||||
|
close(fd);
|
||||||
|
if (ret < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = strchr(line, ' ');
|
||||||
|
if (cp) {
|
||||||
|
*cp = '\0';
|
||||||
|
} else {
|
||||||
|
line[ret] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
static void cmd_procprio(LMKD_CTRL_PACKET packet) {
|
static void cmd_procprio(LMKD_CTRL_PACKET packet) {
|
||||||
struct proc *procp;
|
struct proc *procp;
|
||||||
char path[80];
|
char path[80];
|
||||||
|
|
@ -811,9 +856,7 @@ static void cmd_procprio(LMKD_CTRL_PACKET packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_inkernel_interface) {
|
if (use_inkernel_interface) {
|
||||||
#ifdef LMKD_LOG_STATS
|
stats_store_taskname(params.pid, proc_get_name(params.pid), kpoll_info.poll_fd);
|
||||||
stats_store_taskname(params.pid, proc_get_name(params.pid));
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -884,16 +927,7 @@ static void cmd_procremove(LMKD_CTRL_PACKET packet) {
|
||||||
struct lmk_procremove params;
|
struct lmk_procremove params;
|
||||||
|
|
||||||
if (use_inkernel_interface) {
|
if (use_inkernel_interface) {
|
||||||
#ifdef LMKD_LOG_STATS
|
stats_remove_taskname(params.pid, kpoll_info.poll_fd);
|
||||||
/* Perform an extra check before the pid is removed, after which it
|
|
||||||
* will be impossible for poll_kernel to get the taskname. poll_kernel()
|
|
||||||
* is potentially a long-running blocking function; however this method
|
|
||||||
* handles AMS requests but does not block AMS.*/
|
|
||||||
if (enable_stats_log) {
|
|
||||||
poll_kernel();
|
|
||||||
}
|
|
||||||
stats_remove_taskname(params.pid);
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -911,9 +945,7 @@ static void cmd_procpurge() {
|
||||||
struct proc *next;
|
struct proc *next;
|
||||||
|
|
||||||
if (use_inkernel_interface) {
|
if (use_inkernel_interface) {
|
||||||
#ifdef LMKD_LOG_STATS
|
|
||||||
stats_purge_tasknames();
|
stats_purge_tasknames();
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1231,89 +1263,6 @@ static void ctrl_connect_handler(int data __unused, uint32_t events __unused,
|
||||||
maxevents++;
|
maxevents++;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LMKD_LOG_STATS
|
|
||||||
static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
|
|
||||||
char key[LINE_MAX + 1];
|
|
||||||
int64_t value;
|
|
||||||
|
|
||||||
sscanf(line, "%" STRINGIFY(LINE_MAX) "s %" SCNd64 "", key, &value);
|
|
||||||
|
|
||||||
if (strcmp(key, "total_") < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(key, "total_pgfault"))
|
|
||||||
mem_st->pgfault = value;
|
|
||||||
else if (!strcmp(key, "total_pgmajfault"))
|
|
||||||
mem_st->pgmajfault = value;
|
|
||||||
else if (!strcmp(key, "total_rss"))
|
|
||||||
mem_st->rss_in_bytes = value;
|
|
||||||
else if (!strcmp(key, "total_cache"))
|
|
||||||
mem_st->cache_in_bytes = value;
|
|
||||||
else if (!strcmp(key, "total_swap"))
|
|
||||||
mem_st->swap_in_bytes = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
|
|
||||||
FILE *fp;
|
|
||||||
char buf[PATH_MAX];
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
|
|
||||||
|
|
||||||
fp = fopen(buf, "r");
|
|
||||||
|
|
||||||
if (fp == NULL) {
|
|
||||||
ALOGE("%s open failed: %s", buf, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (fgets(buf, PAGE_SIZE, fp) != NULL) {
|
|
||||||
memory_stat_parse_line(buf, mem_st);
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
|
|
||||||
char path[PATH_MAX];
|
|
||||||
char buffer[PROC_STAT_BUFFER_SIZE];
|
|
||||||
int fd, ret;
|
|
||||||
|
|
||||||
snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
|
|
||||||
if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
|
|
||||||
ALOGE("%s open failed: %s", path, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = read(fd, buffer, sizeof(buffer));
|
|
||||||
if (ret < 0) {
|
|
||||||
ALOGE("%s read failed: %s", path, strerror(errno));
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
// field 10 is pgfault
|
|
||||||
// field 12 is pgmajfault
|
|
||||||
// field 22 is starttime
|
|
||||||
// field 24 is rss_in_pages
|
|
||||||
int64_t pgfault = 0, pgmajfault = 0, starttime = 0, rss_in_pages = 0;
|
|
||||||
if (sscanf(buffer,
|
|
||||||
"%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
|
|
||||||
"%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
|
|
||||||
"%" SCNd64 " %*d %" SCNd64 "",
|
|
||||||
&pgfault, &pgmajfault, &starttime, &rss_in_pages) != 4) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
mem_st->pgfault = pgfault;
|
|
||||||
mem_st->pgmajfault = pgmajfault;
|
|
||||||
mem_st->rss_in_bytes = (rss_in_pages * PAGE_SIZE);
|
|
||||||
mem_st->process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* /proc/zoneinfo parsing routines
|
* /proc/zoneinfo parsing routines
|
||||||
* Expected file format is:
|
* Expected file format is:
|
||||||
|
|
@ -1626,60 +1575,6 @@ static void meminfo_log(union meminfo *mi) {
|
||||||
android_log_reset(ctx);
|
android_log_reset(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int proc_get_size(int pid) {
|
|
||||||
char path[PATH_MAX];
|
|
||||||
char line[LINE_MAX];
|
|
||||||
int fd;
|
|
||||||
int rss = 0;
|
|
||||||
int total;
|
|
||||||
ssize_t ret;
|
|
||||||
|
|
||||||
/* gid containing AID_READPROC required */
|
|
||||||
snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
|
|
||||||
fd = open(path, O_RDONLY | O_CLOEXEC);
|
|
||||||
if (fd == -1)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
ret = read_all(fd, line, sizeof(line) - 1);
|
|
||||||
if (ret < 0) {
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sscanf(line, "%d %d ", &total, &rss);
|
|
||||||
close(fd);
|
|
||||||
return rss;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *proc_get_name(int pid) {
|
|
||||||
char path[PATH_MAX];
|
|
||||||
static char line[LINE_MAX];
|
|
||||||
int fd;
|
|
||||||
char *cp;
|
|
||||||
ssize_t ret;
|
|
||||||
|
|
||||||
/* gid containing AID_READPROC required */
|
|
||||||
snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
|
|
||||||
fd = open(path, O_RDONLY | O_CLOEXEC);
|
|
||||||
if (fd == -1) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
ret = read_all(fd, line, sizeof(line) - 1);
|
|
||||||
close(fd);
|
|
||||||
if (ret < 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
cp = strchr(line, ' ');
|
|
||||||
if (cp) {
|
|
||||||
*cp = '\0';
|
|
||||||
} else {
|
|
||||||
line[ret] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct proc *proc_adj_lru(int oomadj) {
|
static struct proc *proc_adj_lru(int oomadj) {
|
||||||
return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
|
return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
|
||||||
}
|
}
|
||||||
|
|
@ -1753,14 +1648,7 @@ static int kill_one_process(struct proc* procp, int min_oom_score, const char *r
|
||||||
int tasksize;
|
int tasksize;
|
||||||
int r;
|
int r;
|
||||||
int result = -1;
|
int result = -1;
|
||||||
|
struct memory_stat *mem_st;
|
||||||
#ifdef LMKD_LOG_STATS
|
|
||||||
struct memory_stat mem_st = {};
|
|
||||||
int memory_stat_parse_result = -1;
|
|
||||||
#else
|
|
||||||
/* To prevent unused parameter warning */
|
|
||||||
(void)(min_oom_score);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
tgid = proc_get_tgid(pid);
|
tgid = proc_get_tgid(pid);
|
||||||
if (tgid >= 0 && tgid != pid) {
|
if (tgid >= 0 && tgid != pid) {
|
||||||
|
|
@ -1778,15 +1666,7 @@ static int kill_one_process(struct proc* procp, int min_oom_score, const char *r
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LMKD_LOG_STATS
|
mem_st = stats_read_memory_stat(per_app_memcg, pid, uid);
|
||||||
if (enable_stats_log) {
|
|
||||||
if (per_app_memcg) {
|
|
||||||
memory_stat_parse_result = memory_stat_from_cgroup(&mem_st, pid, uid);
|
|
||||||
} else {
|
|
||||||
memory_stat_parse_result = memory_stat_from_procfs(&mem_st, pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TRACE_KILL_START(pid);
|
TRACE_KILL_START(pid);
|
||||||
|
|
||||||
|
|
@ -1814,18 +1694,9 @@ static int kill_one_process(struct proc* procp, int min_oom_score, const char *r
|
||||||
|
|
||||||
last_killed_pid = pid;
|
last_killed_pid = pid;
|
||||||
|
|
||||||
#ifdef LMKD_LOG_STATS
|
stats_write_lmk_kill_occurred(LMK_KILL_OCCURRED, uid, taskname,
|
||||||
if (memory_stat_parse_result == 0) {
|
procp->oomadj, min_oom_score, tasksize, mem_st);
|
||||||
stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
|
|
||||||
procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
|
|
||||||
mem_st.cache_in_bytes, mem_st.swap_in_bytes, mem_st.process_start_time_ns,
|
|
||||||
min_oom_score);
|
|
||||||
} else if (enable_stats_log) {
|
|
||||||
stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, procp->oomadj,
|
|
||||||
-1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1, -1,
|
|
||||||
min_oom_score);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
result = tasksize;
|
result = tasksize;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
@ -1844,10 +1715,7 @@ out:
|
||||||
static int find_and_kill_process(int min_score_adj, const char *reason) {
|
static int find_and_kill_process(int min_score_adj, const char *reason) {
|
||||||
int i;
|
int i;
|
||||||
int killed_size = 0;
|
int killed_size = 0;
|
||||||
|
|
||||||
#ifdef LMKD_LOG_STATS
|
|
||||||
bool lmk_state_change_start = false;
|
bool lmk_state_change_start = false;
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
|
for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
|
||||||
struct proc *procp;
|
struct proc *procp;
|
||||||
|
|
@ -1861,13 +1729,11 @@ static int find_and_kill_process(int min_score_adj, const char *reason) {
|
||||||
|
|
||||||
killed_size = kill_one_process(procp, min_score_adj, reason);
|
killed_size = kill_one_process(procp, min_score_adj, reason);
|
||||||
if (killed_size >= 0) {
|
if (killed_size >= 0) {
|
||||||
#ifdef LMKD_LOG_STATS
|
if (!lmk_state_change_start) {
|
||||||
if (enable_stats_log && !lmk_state_change_start) {
|
|
||||||
lmk_state_change_start = true;
|
lmk_state_change_start = true;
|
||||||
stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
|
stats_write_lmk_state_changed(LMK_STATE_CHANGED,
|
||||||
LMK_STATE_CHANGE_START);
|
LMK_STATE_CHANGE_START);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1876,11 +1742,9 @@ static int find_and_kill_process(int min_score_adj, const char *reason) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LMKD_LOG_STATS
|
if (lmk_state_change_start) {
|
||||||
if (enable_stats_log && lmk_state_change_start) {
|
stats_write_lmk_state_changed(LMK_STATE_CHANGED, LMK_STATE_CHANGE_STOP);
|
||||||
stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED, LMK_STATE_CHANGE_STOP);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
return killed_size;
|
return killed_size;
|
||||||
}
|
}
|
||||||
|
|
@ -2586,74 +2450,13 @@ err_open_mpfd:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LMKD_LOG_STATS
|
static void kernel_event_handler(int data __unused, uint32_t events __unused,
|
||||||
static int kernel_poll_fd = -1;
|
struct polling_params *poll_params __unused) {
|
||||||
static void poll_kernel() {
|
kpoll_info.handler(kpoll_info.poll_fd);
|
||||||
if (kernel_poll_fd == -1) {
|
|
||||||
// not waiting
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
char rd_buf[256];
|
|
||||||
int bytes_read =
|
|
||||||
TEMP_FAILURE_RETRY(pread(kernel_poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
|
|
||||||
if (bytes_read <= 0) break;
|
|
||||||
rd_buf[bytes_read] = '\0';
|
|
||||||
|
|
||||||
int64_t pid;
|
|
||||||
int64_t uid;
|
|
||||||
int64_t group_leader_pid;
|
|
||||||
int64_t min_flt;
|
|
||||||
int64_t maj_flt;
|
|
||||||
int64_t rss_in_pages;
|
|
||||||
int16_t oom_score_adj;
|
|
||||||
int16_t min_score_adj;
|
|
||||||
int64_t starttime;
|
|
||||||
char* taskname = 0;
|
|
||||||
int fields_read = sscanf(rd_buf,
|
|
||||||
"%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
|
|
||||||
" %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
|
|
||||||
&pid, &uid, &group_leader_pid, &min_flt, &maj_flt, &rss_in_pages,
|
|
||||||
&oom_score_adj, &min_score_adj, &starttime, &taskname);
|
|
||||||
|
|
||||||
/* only the death of the group leader process is logged */
|
|
||||||
if (fields_read == 10 && group_leader_pid == pid) {
|
|
||||||
int64_t process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
|
|
||||||
stats_write_lmk_kill_occurred_pid(log_ctx, LMK_KILL_OCCURRED, uid, pid, oom_score_adj,
|
|
||||||
min_flt, maj_flt, rss_in_pages * PAGE_SIZE, 0, 0,
|
|
||||||
process_start_time_ns, min_score_adj);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(taskname);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct event_handler_info kernel_poll_hinfo = {0, poll_kernel};
|
|
||||||
|
|
||||||
static void init_poll_kernel() {
|
|
||||||
struct epoll_event epev;
|
|
||||||
kernel_poll_fd =
|
|
||||||
TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
|
|
||||||
|
|
||||||
if (kernel_poll_fd < 0) {
|
|
||||||
ALOGE("kernel lmk event file could not be opened; errno=%d", kernel_poll_fd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
epev.events = EPOLLIN;
|
|
||||||
epev.data.ptr = (void*)&kernel_poll_hinfo;
|
|
||||||
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kernel_poll_fd, &epev) != 0) {
|
|
||||||
ALOGE("epoll_ctl for lmk events failed; errno=%d", errno);
|
|
||||||
close(kernel_poll_fd);
|
|
||||||
kernel_poll_fd = -1;
|
|
||||||
} else {
|
|
||||||
maxevents++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int init(void) {
|
static int init(void) {
|
||||||
|
static struct event_handler_info kernel_poll_hinfo = { 0, kernel_event_handler };
|
||||||
struct reread_data file_data = {
|
struct reread_data file_data = {
|
||||||
.filename = ZONEINFO_PATH,
|
.filename = ZONEINFO_PATH,
|
||||||
.fd = -1,
|
.fd = -1,
|
||||||
|
|
@ -2704,11 +2507,17 @@ static int init(void) {
|
||||||
|
|
||||||
if (use_inkernel_interface) {
|
if (use_inkernel_interface) {
|
||||||
ALOGI("Using in-kernel low memory killer interface");
|
ALOGI("Using in-kernel low memory killer interface");
|
||||||
#ifdef LMKD_LOG_STATS
|
if (init_poll_kernel(&kpoll_info)) {
|
||||||
if (enable_stats_log) {
|
epev.events = EPOLLIN;
|
||||||
init_poll_kernel();
|
epev.data.ptr = (void*)&kernel_poll_hinfo;
|
||||||
|
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kpoll_info.poll_fd, &epev) != 0) {
|
||||||
|
ALOGE("epoll_ctl for lmk events failed (errno=%d)", errno);
|
||||||
|
close(kpoll_info.poll_fd);
|
||||||
|
kpoll_info.poll_fd = -1;
|
||||||
|
} else {
|
||||||
|
maxevents++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
/* Try to use psi monitor first if kernel has it */
|
/* Try to use psi monitor first if kernel has it */
|
||||||
use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
|
use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
|
||||||
|
|
@ -2907,9 +2716,7 @@ int main(int argc __unused, char **argv __unused) {
|
||||||
|
|
||||||
ctx = create_android_logger(MEMINFO_LOG_TAG);
|
ctx = create_android_logger(MEMINFO_LOG_TAG);
|
||||||
|
|
||||||
#ifdef LMKD_LOG_STATS
|
statslog_init();
|
||||||
statslog_init(&log_ctx, &enable_stats_log);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!init()) {
|
if (!init()) {
|
||||||
if (!use_inkernel_interface) {
|
if (!use_inkernel_interface) {
|
||||||
|
|
@ -2938,9 +2745,7 @@ int main(int argc __unused, char **argv __unused) {
|
||||||
mainloop();
|
mainloop();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LMKD_LOG_STATS
|
statslog_destroy();
|
||||||
statslog_destroy(&log_ctx);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
android_log_destroy(&ctx);
|
android_log_destroy(&ctx);
|
||||||
|
|
||||||
|
|
|
||||||
302
statslog.c
302
statslog.c
|
|
@ -18,11 +18,21 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <log/log_id.h>
|
#include <log/log_id.h>
|
||||||
#include <stats_event_list.h>
|
#include <stats_event_list.h>
|
||||||
|
#include <statslog.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#ifdef LMKD_LOG_STATS
|
||||||
|
|
||||||
#define LINE_MAX 128
|
#define LINE_MAX 128
|
||||||
|
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
|
||||||
|
#define STRINGIFY_INTERNAL(x) #x
|
||||||
|
|
||||||
|
static bool enable_stats_log;
|
||||||
|
static android_log_context log_ctx;
|
||||||
|
|
||||||
struct proc {
|
struct proc {
|
||||||
int pid;
|
int pid;
|
||||||
|
|
@ -41,34 +51,53 @@ static int64_t getElapsedRealTimeNs() {
|
||||||
return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
|
return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void statslog_init() {
|
||||||
|
enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
|
||||||
|
|
||||||
|
if (enable_stats_log) {
|
||||||
|
log_ctx = create_android_logger(kStatsEventTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void statslog_destroy() {
|
||||||
|
if (log_ctx) {
|
||||||
|
android_log_destroy(&log_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs the change in LMKD state which is used as start/stop boundaries for logging
|
* Logs the change in LMKD state which is used as start/stop boundaries for logging
|
||||||
* LMK_KILL_OCCURRED event.
|
* LMK_KILL_OCCURRED event.
|
||||||
* Code: LMK_STATE_CHANGED = 54
|
* Code: LMK_STATE_CHANGED = 54
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state) {
|
stats_write_lmk_state_changed(int32_t code, int32_t state) {
|
||||||
assert(ctx != NULL);
|
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
if (!ctx) {
|
|
||||||
|
if (!enable_stats_log) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
reset_log_context(ctx);
|
assert(log_ctx != NULL);
|
||||||
|
if (!log_ctx) {
|
||||||
if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_int32(ctx, code)) < 0) {
|
reset_log_context(log_ctx);
|
||||||
|
|
||||||
|
if ((ret = android_log_write_int64(log_ctx, getElapsedRealTimeNs())) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_int32(ctx, state)) < 0) {
|
if ((ret = android_log_write_int32(log_ctx, code)) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return write_to_logger(ctx, LOG_ID_STATS);
|
if ((ret = android_log_write_int32(log_ctx, state)) < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return write_to_logger(log_ctx, LOG_ID_STATS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct proc* pid_lookup(int pid) {
|
static struct proc* pid_lookup(int pid) {
|
||||||
|
|
@ -87,92 +116,261 @@ static struct proc* pid_lookup(int pid) {
|
||||||
* Code: LMK_KILL_OCCURRED = 51
|
* Code: LMK_KILL_OCCURRED = 51
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
|
stats_write_lmk_kill_occurred(int32_t code, int32_t uid, char const* process_name,
|
||||||
char const* process_name, int32_t oom_score, int64_t pgfault,
|
int32_t oom_score, int32_t min_oom_score, int tasksize,
|
||||||
int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
|
struct memory_stat *mem_st) {
|
||||||
int64_t swap_in_bytes, int64_t process_start_time_ns,
|
|
||||||
int32_t min_oom_score) {
|
|
||||||
assert(ctx != NULL);
|
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
if (!ctx) {
|
if (!enable_stats_log) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
reset_log_context(ctx);
|
if (!log_ctx) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
reset_log_context(log_ctx);
|
||||||
|
|
||||||
if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
|
if ((ret = android_log_write_int64(log_ctx, getElapsedRealTimeNs())) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_int32(ctx, code)) < 0) {
|
if ((ret = android_log_write_int32(log_ctx, code)) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_int32(ctx, uid)) < 0) {
|
if ((ret = android_log_write_int32(log_ctx, uid)) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_string8(ctx, (process_name == NULL) ? "" : process_name)) < 0) {
|
if ((ret = android_log_write_string8(log_ctx, (process_name == NULL) ? "" : process_name)) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_int32(ctx, oom_score)) < 0) {
|
if ((ret = android_log_write_int32(log_ctx, oom_score)) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_int64(ctx, pgfault)) < 0) {
|
if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->pgfault : -1)) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_int64(ctx, pgmajfault)) < 0) {
|
if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->pgmajfault : -1)) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_int64(ctx, rss_in_bytes)) < 0) {
|
if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->rss_in_bytes
|
||||||
|
: tasksize * BYTES_IN_KILOBYTE)) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_int64(ctx, cache_in_bytes)) < 0) {
|
if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->cache_in_bytes : -1)) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_int64(ctx, swap_in_bytes)) < 0) {
|
if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->swap_in_bytes : -1)) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_int64(ctx, process_start_time_ns)) < 0) {
|
if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->process_start_time_ns
|
||||||
|
: -1)) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = android_log_write_int32(ctx, min_oom_score)) < 0) {
|
if ((ret = android_log_write_int32(log_ctx, min_oom_score)) < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return write_to_logger(ctx, LOG_ID_STATS);
|
return write_to_logger(log_ctx, LOG_ID_STATS);
|
||||||
}
|
}
|
||||||
|
|
||||||
int stats_write_lmk_kill_occurred_pid(android_log_context ctx, int32_t code, int32_t uid, int pid,
|
static int stats_write_lmk_kill_occurred_pid(int32_t code, int32_t uid, int pid,
|
||||||
int32_t oom_score, int64_t pgfault, int64_t pgmajfault,
|
int32_t oom_score, int32_t min_oom_score, int tasksize,
|
||||||
int64_t rss_in_bytes, int64_t cache_in_bytes,
|
struct memory_stat *mem_st) {
|
||||||
int64_t swap_in_bytes, int64_t process_start_time_ns,
|
|
||||||
int32_t min_oom_score) {
|
|
||||||
struct proc* proc = pid_lookup(pid);
|
struct proc* proc = pid_lookup(pid);
|
||||||
if (!proc) return -EINVAL;
|
if (!proc) return -EINVAL;
|
||||||
|
|
||||||
return stats_write_lmk_kill_occurred(ctx, code, uid, proc->taskname, oom_score, pgfault,
|
return stats_write_lmk_kill_occurred(code, uid, proc->taskname, oom_score, min_oom_score,
|
||||||
pgmajfault, rss_in_bytes, cache_in_bytes, swap_in_bytes,
|
tasksize, mem_st);
|
||||||
process_start_time_ns, min_oom_score);
|
}
|
||||||
|
|
||||||
|
static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
|
||||||
|
char key[LINE_MAX + 1];
|
||||||
|
int64_t value;
|
||||||
|
|
||||||
|
sscanf(line, "%" STRINGIFY(LINE_MAX) "s %" SCNd64 "", key, &value);
|
||||||
|
|
||||||
|
if (strcmp(key, "total_") < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(key, "total_pgfault"))
|
||||||
|
mem_st->pgfault = value;
|
||||||
|
else if (!strcmp(key, "total_pgmajfault"))
|
||||||
|
mem_st->pgmajfault = value;
|
||||||
|
else if (!strcmp(key, "total_rss"))
|
||||||
|
mem_st->rss_in_bytes = value;
|
||||||
|
else if (!strcmp(key, "total_cache"))
|
||||||
|
mem_st->cache_in_bytes = value;
|
||||||
|
else if (!strcmp(key, "total_swap"))
|
||||||
|
mem_st->swap_in_bytes = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
|
||||||
|
FILE *fp;
|
||||||
|
char buf[PATH_MAX];
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
|
||||||
|
|
||||||
|
fp = fopen(buf, "r");
|
||||||
|
|
||||||
|
if (fp == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(buf, PAGE_SIZE, fp) != NULL) {
|
||||||
|
memory_stat_parse_line(buf, mem_st);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
|
||||||
|
char path[PATH_MAX];
|
||||||
|
char buffer[PROC_STAT_BUFFER_SIZE];
|
||||||
|
int fd, ret;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
|
||||||
|
if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = read(fd, buffer, sizeof(buffer));
|
||||||
|
if (ret < 0) {
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
// field 10 is pgfault
|
||||||
|
// field 12 is pgmajfault
|
||||||
|
// field 22 is starttime
|
||||||
|
// field 24 is rss_in_pages
|
||||||
|
int64_t pgfault = 0, pgmajfault = 0, starttime = 0, rss_in_pages = 0;
|
||||||
|
if (sscanf(buffer,
|
||||||
|
"%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
|
||||||
|
"%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
|
||||||
|
"%" SCNd64 " %*d %" SCNd64 "",
|
||||||
|
&pgfault, &pgmajfault, &starttime, &rss_in_pages) != 4) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
mem_st->pgfault = pgfault;
|
||||||
|
mem_st->pgmajfault = pgmajfault;
|
||||||
|
mem_st->rss_in_bytes = (rss_in_pages * PAGE_SIZE);
|
||||||
|
mem_st->process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t uid) {
|
||||||
|
static struct memory_stat mem_st = {};
|
||||||
|
|
||||||
|
if (!enable_stats_log) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (per_app_memcg) {
|
||||||
|
if (memory_stat_from_cgroup(&mem_st, pid, uid) == 0) {
|
||||||
|
return &mem_st;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (memory_stat_from_procfs(&mem_st, pid) == 0) {
|
||||||
|
return &mem_st;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void poll_kernel(int poll_fd) {
|
||||||
|
if (poll_fd == -1) {
|
||||||
|
// not waiting
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
char rd_buf[256];
|
||||||
|
int bytes_read =
|
||||||
|
TEMP_FAILURE_RETRY(pread(poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
|
||||||
|
if (bytes_read <= 0) break;
|
||||||
|
rd_buf[bytes_read] = '\0';
|
||||||
|
|
||||||
|
int64_t pid;
|
||||||
|
int64_t uid;
|
||||||
|
int64_t group_leader_pid;
|
||||||
|
int64_t rss_in_pages;
|
||||||
|
struct memory_stat mem_st = {};
|
||||||
|
int16_t oom_score_adj;
|
||||||
|
int16_t min_score_adj;
|
||||||
|
int64_t starttime;
|
||||||
|
char* taskname = 0;
|
||||||
|
|
||||||
|
int fields_read = sscanf(rd_buf,
|
||||||
|
"%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
|
||||||
|
" %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
|
||||||
|
&pid, &uid, &group_leader_pid, &mem_st.pgfault,
|
||||||
|
&mem_st.pgmajfault, &rss_in_pages, &oom_score_adj,
|
||||||
|
&min_score_adj, &starttime, &taskname);
|
||||||
|
|
||||||
|
/* only the death of the group leader process is logged */
|
||||||
|
if (fields_read == 10 && group_leader_pid == pid) {
|
||||||
|
mem_st.process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
|
||||||
|
mem_st.rss_in_bytes = rss_in_pages * PAGE_SIZE;
|
||||||
|
stats_write_lmk_kill_occurred_pid(LMK_KILL_OCCURRED, uid, pid, oom_score_adj,
|
||||||
|
min_score_adj, 0, &mem_st);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(taskname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init_poll_kernel(struct kernel_poll_info *poll_info) {
|
||||||
|
if (!enable_stats_log) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
poll_info->poll_fd =
|
||||||
|
TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
|
||||||
|
|
||||||
|
if (poll_info->poll_fd < 0) {
|
||||||
|
ALOGE("kernel lmk event file could not be opened; errno=%d", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
poll_info->handler = poll_kernel;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void proc_insert(struct proc* procp) {
|
static void proc_insert(struct proc* procp) {
|
||||||
if (!pidhash)
|
if (!pidhash) {
|
||||||
pidhash = calloc(PIDHASH_SZ, sizeof(struct proc));
|
pidhash = calloc(PIDHASH_SZ, sizeof(struct proc));
|
||||||
|
}
|
||||||
|
|
||||||
int hval = pid_hashfn(procp->pid);
|
int hval = pid_hashfn(procp->pid);
|
||||||
procp->pidhash_next = pidhash[hval];
|
procp->pidhash_next = pidhash[hval];
|
||||||
pidhash[hval] = procp;
|
pidhash[hval] = procp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void stats_remove_taskname(int pid) {
|
void stats_remove_taskname(int pid, int poll_fd) {
|
||||||
if (!pidhash) return;
|
if (!enable_stats_log || !pidhash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform an extra check before the pid is removed, after which it
|
||||||
|
* will be impossible for poll_kernel to get the taskname. poll_kernel()
|
||||||
|
* is potentially a long-running blocking function; however this method
|
||||||
|
* handles AMS requests but does not block AMS.
|
||||||
|
*/
|
||||||
|
poll_kernel(poll_fd);
|
||||||
|
|
||||||
int hval = pid_hashfn(pid);
|
int hval = pid_hashfn(pid);
|
||||||
struct proc* procp;
|
struct proc* procp;
|
||||||
|
|
@ -193,12 +391,19 @@ void stats_remove_taskname(int pid) {
|
||||||
free(procp);
|
free(procp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stats_store_taskname(int pid, const char* taskname) {
|
void stats_store_taskname(int pid, const char* taskname, int poll_fd) {
|
||||||
struct proc* procp = pid_lookup(pid);
|
if (!enable_stats_log) {
|
||||||
if (procp != NULL && strcmp(procp->taskname, taskname) == 0)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct proc* procp = pid_lookup(pid);
|
||||||
|
if (procp != NULL) {
|
||||||
|
if (strcmp(procp->taskname, taskname) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stats_remove_taskname(pid, poll_fd);
|
||||||
|
}
|
||||||
procp = malloc(sizeof(struct proc));
|
procp = malloc(sizeof(struct proc));
|
||||||
stats_remove_taskname(pid);
|
|
||||||
procp->pid = pid;
|
procp->pid = pid;
|
||||||
strncpy(procp->taskname, taskname, LINE_MAX - 1);
|
strncpy(procp->taskname, taskname, LINE_MAX - 1);
|
||||||
procp->taskname[LINE_MAX - 1] = '\0';
|
procp->taskname[LINE_MAX - 1] = '\0';
|
||||||
|
|
@ -206,7 +411,10 @@ void stats_store_taskname(int pid, const char* taskname) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void stats_purge_tasknames() {
|
void stats_purge_tasknames() {
|
||||||
if (!pidhash) return;
|
if (!enable_stats_log || !pidhash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct proc* procp;
|
struct proc* procp;
|
||||||
struct proc* next;
|
struct proc* next;
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -220,3 +428,5 @@ void stats_purge_tasknames() {
|
||||||
}
|
}
|
||||||
memset(pidhash, 0, PIDHASH_SZ * sizeof(struct proc));
|
memset(pidhash, 0, PIDHASH_SZ * sizeof(struct proc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* LMKD_LOG_STATS */
|
||||||
|
|
|
||||||
103
statslog.h
103
statslog.h
|
|
@ -18,6 +18,7 @@
|
||||||
#define _STATSLOG_H_
|
#define _STATSLOG_H_
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stats_event_list.h>
|
#include <stats_event_list.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
|
|
@ -26,6 +27,20 @@
|
||||||
|
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
|
struct memory_stat {
|
||||||
|
int64_t pgfault;
|
||||||
|
int64_t pgmajfault;
|
||||||
|
int64_t rss_in_bytes;
|
||||||
|
int64_t cache_in_bytes;
|
||||||
|
int64_t swap_in_bytes;
|
||||||
|
int64_t process_start_time_ns;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kernel_poll_info {
|
||||||
|
int poll_fd;
|
||||||
|
void (*handler)(int poll_fd);
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These are defined in
|
* These are defined in
|
||||||
* http://cs/android/frameworks/base/cmds/statsd/src/atoms.proto
|
* http://cs/android/frameworks/base/cmds/statsd/src/atoms.proto
|
||||||
|
|
@ -35,37 +50,17 @@ __BEGIN_DECLS
|
||||||
#define LMK_STATE_CHANGE_START 1
|
#define LMK_STATE_CHANGE_START 1
|
||||||
#define LMK_STATE_CHANGE_STOP 2
|
#define LMK_STATE_CHANGE_STOP 2
|
||||||
|
|
||||||
|
#ifdef LMKD_LOG_STATS
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The single event tag id for all stats logs.
|
* The single event tag id for all stats logs.
|
||||||
* Keep this in sync with system/core/logcat/event.logtags
|
* Keep this in sync with system/core/logcat/event.logtags
|
||||||
*/
|
*/
|
||||||
const static int kStatsEventTag = 1937006964;
|
const static int kStatsEventTag = 1937006964;
|
||||||
|
|
||||||
static inline void statslog_init(android_log_context* log_ctx, bool* enable_stats_log) {
|
void statslog_init();
|
||||||
assert(log_ctx != NULL);
|
|
||||||
assert(enable_stats_log != NULL);
|
|
||||||
*enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
|
|
||||||
|
|
||||||
if (*enable_stats_log) {
|
void statslog_destroy();
|
||||||
*log_ctx = create_android_logger(kStatsEventTag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void statslog_destroy(android_log_context* log_ctx) {
|
|
||||||
assert(log_ctx != NULL);
|
|
||||||
if (*log_ctx) {
|
|
||||||
android_log_destroy(log_ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct memory_stat {
|
|
||||||
int64_t pgfault;
|
|
||||||
int64_t pgmajfault;
|
|
||||||
int64_t rss_in_bytes;
|
|
||||||
int64_t cache_in_bytes;
|
|
||||||
int64_t swap_in_bytes;
|
|
||||||
int64_t process_start_time_ns;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%u/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_FILE_PATH "/proc/%d/stat"
|
||||||
|
|
@ -78,47 +73,63 @@ struct memory_stat {
|
||||||
* Code: LMK_STATE_CHANGED = 54
|
* Code: LMK_STATE_CHANGED = 54
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state);
|
stats_write_lmk_state_changed(int32_t code, int32_t state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs the event when LMKD kills a process to reduce memory pressure.
|
* Logs the event when LMKD kills a process to reduce memory pressure.
|
||||||
* Code: LMK_KILL_OCCURRED = 51
|
* Code: LMK_KILL_OCCURRED = 51
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
stats_write_lmk_kill_occurred_pid(android_log_context ctx, int32_t code, int32_t uid, int pid,
|
stats_write_lmk_kill_occurred(int32_t code, int32_t uid,
|
||||||
int32_t oom_score, int64_t pgfault, int64_t pgmajfault,
|
char const* process_name, int32_t oom_score, int32_t min_oom_score,
|
||||||
int64_t rss_in_bytes, int64_t cache_in_bytes,
|
int tasksize, struct memory_stat *mem_st);
|
||||||
int64_t swap_in_bytes, int64_t process_start_time_ns,
|
|
||||||
int32_t min_oom_score);
|
|
||||||
|
|
||||||
/**
|
struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t uid);
|
||||||
* Logs the event when LMKD kills a process to reduce memory pressure.
|
|
||||||
* Code: LMK_KILL_OCCURRED = 51
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
|
|
||||||
char const* process_name, int32_t oom_score, int64_t pgfault,
|
|
||||||
int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
|
|
||||||
int64_t swap_in_bytes, int64_t process_start_time_ns,
|
|
||||||
int32_t min_oom_score);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a process taskname by pid, while it is still alive.
|
* Registers a process taskname by pid, while it is still alive.
|
||||||
*/
|
*/
|
||||||
void
|
void stats_store_taskname(int pid, const char* taskname, int poll_fd);
|
||||||
stats_store_taskname(int pid, const char* taskname);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregister all process tasknames.
|
* Unregister all process tasknames.
|
||||||
*/
|
*/
|
||||||
void
|
void stats_purge_tasknames();
|
||||||
stats_purge_tasknames();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregister a process taskname, e.g. after it has been killed.
|
* Unregister a process taskname, e.g. after it has been killed.
|
||||||
*/
|
*/
|
||||||
void
|
void stats_remove_taskname(int pid, int poll_fd);
|
||||||
stats_remove_taskname(int pid);
|
|
||||||
|
bool init_poll_kernel(struct kernel_poll_info *poll_info);
|
||||||
|
|
||||||
|
#else /* LMKD_LOG_STATS */
|
||||||
|
|
||||||
|
static inline void statslog_init() {}
|
||||||
|
static inline void statslog_destroy() {}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
stats_write_lmk_state_changed(int32_t code __unused, int32_t state __unused) { return -EINVAL; }
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
stats_write_lmk_kill_occurred(int32_t code __unused, int32_t uid __unused,
|
||||||
|
char const* process_name __unused, int32_t oom_score __unused,
|
||||||
|
int32_t min_oom_score __unused, int tasksize __unused,
|
||||||
|
struct memory_stat *mem_st __unused) { return -EINVAL; }
|
||||||
|
|
||||||
|
static inline struct memory_stat *stats_read_memory_stat(bool per_app_memcg __unused,
|
||||||
|
int pid __unused, uid_t uid __unused) { return NULL; }
|
||||||
|
|
||||||
|
static inline void stats_store_taskname(int pid __unused, const char* taskname __unused,
|
||||||
|
int poll_fd __unused) {}
|
||||||
|
|
||||||
|
static inline void stats_purge_tasknames() {}
|
||||||
|
|
||||||
|
static inline void stats_remove_taskname(int pid __unused, int poll_fd __unused) {}
|
||||||
|
|
||||||
|
static inline bool init_poll_kernel(struct kernel_poll_info *poll_info __unused) { return false; }
|
||||||
|
|
||||||
|
#endif /* LMKD_LOG_STATS */
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue