summaryrefslogtreecommitdiffstats
path: root/lmkd
diff options
context:
space:
mode:
authorSuren Baghdasaryan <surenb@google.com>2018-04-13 13:11:51 -0700
committerSuren Baghdasaryan <surenb@google.com>2018-04-15 07:55:27 +0000
commitceffb411623d68b773bd9e6650f409ac08c91362 (patch)
treefda85bc9f1aff65279abcb5c1a741fd43632cd56 /lmkd
parent6499e5ec4abe535d97931affc3b1a937fe2f6185 (diff)
downloadsystem_core-ceffb411623d68b773bd9e6650f409ac08c91362.tar.gz
system_core-ceffb411623d68b773bd9e6650f409ac08c91362.tar.bz2
system_core-ceffb411623d68b773bd9e6650f409ac08c91362.zip
lmkd: Add zoneinfo and meminfo parsing routines
/proc/zoneinfo and /proc/meminfo contain information necessary for lmkd to assess system memory state. Add routines to parse these files. Bug: 77299493 Bug: 75322373 Merged-In: Ie7d80bbb81fd0d2fc0629f6f678e6afc97fed1f6 Change-Id: Ie7d80bbb81fd0d2fc0629f6f678e6afc97fed1f6 Signed-off-by: Suren Baghdasaryan <surenb@google.com>
Diffstat (limited to 'lmkd')
-rw-r--r--lmkd/lmkd.c244
1 files changed, 205 insertions, 39 deletions
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index c44d2c5a0..77830a346 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -65,6 +65,7 @@
#define MEMCG_MEMORY_USAGE "/dev/memcg/memory.usage_in_bytes"
#define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
#define ZONEINFO_PATH "/proc/zoneinfo"
+#define MEMINFO_PATH "/proc/meminfo"
#define LINE_MAX 128
#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
@@ -149,6 +150,86 @@ static int lowmem_adj[MAX_TARGETS];
static int lowmem_minfree[MAX_TARGETS];
static int lowmem_targets_size;
+/* Fields to parse in /proc/zoneinfo */
+enum zoneinfo_field {
+ ZI_NR_FREE_PAGES = 0,
+ ZI_NR_FILE_PAGES,
+ ZI_NR_SHMEM,
+ ZI_NR_UNEVICTABLE,
+ ZI_WORKINGSET_REFAULT,
+ ZI_HIGH,
+ ZI_FIELD_COUNT
+};
+
+static const char* const zoneinfo_field_names[ZI_FIELD_COUNT] = {
+ "nr_free_pages",
+ "nr_file_pages",
+ "nr_shmem",
+ "nr_unevictable",
+ "workingset_refault",
+ "high",
+};
+
+union zoneinfo {
+ struct {
+ int64_t nr_free_pages;
+ int64_t nr_file_pages;
+ int64_t nr_shmem;
+ int64_t nr_unevictable;
+ int64_t workingset_refault;
+ int64_t high;
+ /* fields below are calculated rather than read from the file */
+ int64_t totalreserve_pages;
+ } field;
+ int64_t arr[ZI_FIELD_COUNT];
+};
+
+/* Fields to parse in /proc/meminfo */
+enum meminfo_field {
+ MI_NR_FREE_PAGES = 0,
+ MI_CACHED,
+ MI_SWAP_CACHED,
+ MI_BUFFERS,
+ MI_SHMEM,
+ MI_UNEVICTABLE,
+ MI_FREE_SWAP,
+ MI_DIRTY,
+ MI_FIELD_COUNT
+};
+
+static const char* const meminfo_field_names[MI_FIELD_COUNT] = {
+ "MemFree:",
+ "Cached:",
+ "SwapCached:",
+ "Buffers:",
+ "Shmem:",
+ "Unevictable:",
+ "SwapFree:",
+ "Dirty:",
+};
+
+union meminfo {
+ struct {
+ int64_t nr_free_pages;
+ int64_t cached;
+ int64_t swap_cached;
+ int64_t buffers;
+ int64_t shmem;
+ int64_t unevictable;
+ int64_t free_swap;
+ int64_t dirty;
+ /* fields below are calculated rather than read from the file */
+ int64_t nr_file_pages;
+ } field;
+ int64_t arr[MI_FIELD_COUNT];
+};
+
+enum field_match_result {
+ NO_MATCH,
+ PARSE_FAIL,
+ PARSE_SUCCESS
+};
+
struct sysmeminfo {
int nr_free_pages;
int nr_file_pages;
@@ -194,6 +275,22 @@ static bool parse_int64(const char* str, int64_t* ret) {
return true;
}
+static enum field_match_result match_field(const char* cp, const char* ap,
+ const char* const field_names[],
+ int field_count, int64_t* field,
+ int *field_idx) {
+ int64_t val;
+ int i;
+
+ for (i = 0; i < field_count; i++) {
+ if (!strcmp(cp, field_names[i])) {
+ *field_idx = i;
+ return parse_int64(ap, field) ? PARSE_SUCCESS : PARSE_FAIL;
+ }
+ }
+ return NO_MATCH;
+}
+
/*
* Read file content from the beginning up to max_len bytes or EOF
* whichever happens first.
@@ -626,73 +723,142 @@ static void ctrl_connect_handler(int data __unused, uint32_t events __unused) {
maxevents++;
}
-static int zoneinfo_parse_protection(char *cp) {
- int max = 0;
- int zoneval;
+/* /prop/zoneinfo parsing routines */
+static int64_t zoneinfo_parse_protection(char *cp) {
+ int64_t max = 0;
+ long long zoneval;
char *save_ptr;
- for (cp = strtok_r(cp, "(), ", &save_ptr); cp; cp = strtok_r(NULL, "), ", &save_ptr)) {
- zoneval = strtol(cp, &cp, 0);
- if (zoneval > max)
- max = zoneval;
+ for (cp = strtok_r(cp, "(), ", &save_ptr); cp;
+ cp = strtok_r(NULL, "), ", &save_ptr)) {
+ zoneval = strtoll(cp, &cp, 0);
+ if (zoneval > max) {
+ max = (zoneval > INT64_MAX) ? INT64_MAX : zoneval;
+ }
}
return max;
}
-static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) {
+static bool zoneinfo_parse_line(char *line, union zoneinfo *zi) {
char *cp = line;
char *ap;
char *save_ptr;
+ int64_t val;
+ int field_idx;
cp = strtok_r(line, " ", &save_ptr);
- if (!cp)
- return;
+ if (!cp) {
+ return true;
+ }
- ap = strtok_r(NULL, " ", &save_ptr);
- if (!ap)
- return;
+ if (!strcmp(cp, "protection:")) {
+ ap = strtok_r(NULL, ")", &save_ptr);
+ } else {
+ ap = strtok_r(NULL, " ", &save_ptr);
+ }
+
+ if (!ap) {
+ return true;
+ }
- if (!strcmp(cp, "nr_free_pages"))
- mip->nr_free_pages += strtol(ap, NULL, 0);
- else if (!strcmp(cp, "nr_file_pages"))
- mip->nr_file_pages += strtol(ap, NULL, 0);
- else if (!strcmp(cp, "nr_shmem"))
- mip->nr_shmem += strtol(ap, NULL, 0);
- else if (!strcmp(cp, "high"))
- mip->totalreserve_pages += strtol(ap, NULL, 0);
- else if (!strcmp(cp, "protection:"))
- mip->totalreserve_pages += zoneinfo_parse_protection(ap);
+ switch (match_field(cp, ap, zoneinfo_field_names,
+ ZI_FIELD_COUNT, &val, &field_idx)) {
+ case (PARSE_SUCCESS):
+ zi->arr[field_idx] += val;
+ break;
+ case (NO_MATCH):
+ if (!strcmp(cp, "protection:")) {
+ zi->field.totalreserve_pages +=
+ zoneinfo_parse_protection(ap);
+ }
+ break;
+ case (PARSE_FAIL):
+ default:
+ return false;
+ }
+ return true;
}
-static int zoneinfo_parse(struct sysmeminfo *mip) {
- int fd;
- ssize_t size;
+static int zoneinfo_parse(union zoneinfo *zi) {
+ static struct reread_data file_data = {
+ .filename = ZONEINFO_PATH,
+ .fd = -1,
+ };
char buf[PAGE_SIZE];
char *save_ptr;
char *line;
- memset(mip, 0, sizeof(struct sysmeminfo));
+ memset(zi, 0, sizeof(union zoneinfo));
- fd = open(ZONEINFO_PATH, O_RDONLY | O_CLOEXEC);
- if (fd == -1) {
- ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
+ if (reread_file(&file_data, buf, sizeof(buf)) < 0) {
return -1;
}
- size = read_all(fd, buf, sizeof(buf) - 1);
- if (size < 0) {
- ALOGE("%s read: errno=%d", ZONEINFO_PATH, errno);
- close(fd);
+ for (line = strtok_r(buf, "\n", &save_ptr); line;
+ line = strtok_r(NULL, "\n", &save_ptr)) {
+ if (!zoneinfo_parse_line(line, zi)) {
+ ALOGE("%s parse error", file_data.filename);
+ return -1;
+ }
+ }
+ zi->field.totalreserve_pages += zi->field.high;
+
+ return 0;
+}
+
+/* /prop/meminfo parsing routines */
+static bool meminfo_parse_line(char *line, union meminfo *mi) {
+ char *cp = line;
+ char *ap;
+ char *save_ptr;
+ int64_t val;
+ int field_idx;
+ enum field_match_result match_res;
+
+ cp = strtok_r(line, " ", &save_ptr);
+ if (!cp) {
+ return false;
+ }
+
+ ap = strtok_r(NULL, " ", &save_ptr);
+ if (!ap) {
+ return false;
+ }
+
+ match_res = match_field(cp, ap, meminfo_field_names, MI_FIELD_COUNT,
+ &val, &field_idx);
+ if (match_res == PARSE_SUCCESS) {
+ mi->arr[field_idx] = val / page_k;
+ }
+ return (match_res != PARSE_FAIL);
+}
+
+static int meminfo_parse(union meminfo *mi) {
+ static struct reread_data file_data = {
+ .filename = MEMINFO_PATH,
+ .fd = -1,
+ };
+ char buf[PAGE_SIZE];
+ char *save_ptr;
+ char *line;
+
+ memset(mi, 0, sizeof(union meminfo));
+
+ if (reread_file(&file_data, buf, sizeof(buf)) < 0) {
return -1;
}
- ALOG_ASSERT((size_t)size < sizeof(buf) - 1, "/proc/zoneinfo too large");
- buf[size] = 0;
- for (line = strtok_r(buf, "\n", &save_ptr); line; line = strtok_r(NULL, "\n", &save_ptr))
- zoneinfo_parse_line(line, mip);
+ for (line = strtok_r(buf, "\n", &save_ptr); line;
+ line = strtok_r(NULL, "\n", &save_ptr)) {
+ if (!meminfo_parse_line(line, mi)) {
+ ALOGE("%s parse error", file_data.filename);
+ return -1;
+ }
+ }
+ mi->field.nr_file_pages = mi->field.cached + mi->field.swap_cached +
+ mi->field.buffers;
- close(fd);
return 0;
}