blob: c0fd6df83343d9bdf4708126cb7ff0a7b28e2312 [file] [log] [blame]
Rajeev Kumarf0216a82018-01-24 14:40:36 -08001/*
2 * Copyright 2018 Google, Inc
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <assert.h>
18#include <errno.h>
Rajeev Kumarf0216a82018-01-24 14:40:36 -080019#include <log/log_id.h>
Yao Chen389aee12018-05-02 11:19:27 -070020#include <stats_event_list.h>
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -070021#include <statslog.h>
22#include <stdlib.h>
23#include <string.h>
Jim Blacklerd2da8142019-09-10 15:30:05 +010024#include <stdlib.h>
25#include <string.h>
Yao Chen389aee12018-05-02 11:19:27 -070026#include <time.h>
27
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -070028#ifdef LMKD_LOG_STATS
29
Jim Blacklerd2da8142019-09-10 15:30:05 +010030#define LINE_MAX 128
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -070031#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
32#define STRINGIFY_INTERNAL(x) #x
33
34static bool enable_stats_log;
35static android_log_context log_ctx;
Jim Blacklerd2da8142019-09-10 15:30:05 +010036
37struct proc {
38 int pid;
39 char taskname[LINE_MAX];
40 struct proc* pidhash_next;
41};
42
43#define PIDHASH_SZ 1024
44static struct proc** pidhash = NULL;
45#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
46
Yao Chen389aee12018-05-02 11:19:27 -070047static int64_t getElapsedRealTimeNs() {
48 struct timespec t;
49 t.tv_sec = t.tv_nsec = 0;
50 clock_gettime(CLOCK_BOOTTIME, &t);
51 return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
52}
Rajeev Kumarf0216a82018-01-24 14:40:36 -080053
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -070054void statslog_init() {
55 enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
56
57 if (enable_stats_log) {
58 log_ctx = create_android_logger(kStatsEventTag);
59 }
60}
61
62void statslog_destroy() {
63 if (log_ctx) {
64 android_log_destroy(&log_ctx);
65 }
66}
67
Rajeev Kumarf0216a82018-01-24 14:40:36 -080068/**
69 * Logs the change in LMKD state which is used as start/stop boundaries for logging
70 * LMK_KILL_OCCURRED event.
71 * Code: LMK_STATE_CHANGED = 54
72 */
73int
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -070074stats_write_lmk_state_changed(int32_t code, int32_t state) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -080075 int ret = -EINVAL;
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -070076
77 if (!enable_stats_log) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -080078 return ret;
79 }
80
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -070081 assert(log_ctx != NULL);
82 if (!log_ctx) {
Yao Chen389aee12018-05-02 11:19:27 -070083 return ret;
84 }
85
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -070086 reset_log_context(log_ctx);
87
88 if ((ret = android_log_write_int64(log_ctx, getElapsedRealTimeNs())) < 0) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -080089 return ret;
90 }
91
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -070092 if ((ret = android_log_write_int32(log_ctx, code)) < 0) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -080093 return ret;
94 }
Yao Chen389aee12018-05-02 11:19:27 -070095
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -070096 if ((ret = android_log_write_int32(log_ctx, state)) < 0) {
97 return ret;
98 }
99
100 return write_to_logger(log_ctx, LOG_ID_STATS);
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800101}
102
Jim Blacklerd2da8142019-09-10 15:30:05 +0100103static struct proc* pid_lookup(int pid) {
104 struct proc* procp;
105
106 if (!pidhash) return NULL;
107
108 for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid; procp = procp->pidhash_next)
109 ;
110
111 return procp;
112}
113
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800114/**
115 * Logs the event when LMKD kills a process to reduce memory pressure.
116 * Code: LMK_KILL_OCCURRED = 51
117 */
118int
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700119stats_write_lmk_kill_occurred(int32_t code, int32_t uid, char const* process_name,
120 int32_t oom_score, int32_t min_oom_score, int tasksize,
121 struct memory_stat *mem_st) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800122 int ret = -EINVAL;
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700123 if (!enable_stats_log) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800124 return ret;
125 }
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700126 if (!log_ctx) {
127 return ret;
128 }
129 reset_log_context(log_ctx);
Yao Chen389aee12018-05-02 11:19:27 -0700130
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700131 if ((ret = android_log_write_int64(log_ctx, getElapsedRealTimeNs())) < 0) {
Yao Chen389aee12018-05-02 11:19:27 -0700132 return ret;
133 }
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800134
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700135 if ((ret = android_log_write_int32(log_ctx, code)) < 0) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800136 return ret;
137 }
138
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700139 if ((ret = android_log_write_int32(log_ctx, uid)) < 0) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800140 return ret;
141 }
142
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700143 if ((ret = android_log_write_string8(log_ctx, (process_name == NULL) ? "" : process_name)) < 0) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800144 return ret;
145 }
146
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700147 if ((ret = android_log_write_int32(log_ctx, oom_score)) < 0) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800148 return ret;
149 }
150
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700151 if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->pgfault : -1)) < 0) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800152 return ret;
153 }
154
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700155 if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->pgmajfault : -1)) < 0) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800156 return ret;
157 }
158
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700159 if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->rss_in_bytes
160 : tasksize * BYTES_IN_KILOBYTE)) < 0) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800161 return ret;
162 }
163
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700164 if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->cache_in_bytes : -1)) < 0) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800165 return ret;
166 }
167
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700168 if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->swap_in_bytes : -1)) < 0) {
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800169 return ret;
170 }
171
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700172 if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->process_start_time_ns
173 : -1)) < 0) {
Jim Blackler1417cdb2018-11-21 16:22:36 +0000174 return ret;
175 }
176
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700177 if ((ret = android_log_write_int32(log_ctx, min_oom_score)) < 0) {
Suren Baghdasaryanec5e4c62019-03-04 11:07:39 -0800178 return ret;
179 }
180
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700181 return write_to_logger(log_ctx, LOG_ID_STATS);
Rajeev Kumarf0216a82018-01-24 14:40:36 -0800182}
Jim Blacklerd2da8142019-09-10 15:30:05 +0100183
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700184static int stats_write_lmk_kill_occurred_pid(int32_t code, int32_t uid, int pid,
185 int32_t oom_score, int32_t min_oom_score, int tasksize,
186 struct memory_stat *mem_st) {
Jim Blacklerd2da8142019-09-10 15:30:05 +0100187 struct proc* proc = pid_lookup(pid);
188 if (!proc) return -EINVAL;
189
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700190 return stats_write_lmk_kill_occurred(code, uid, proc->taskname, oom_score, min_oom_score,
191 tasksize, mem_st);
192}
193
194static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
195 char key[LINE_MAX + 1];
196 int64_t value;
197
198 sscanf(line, "%" STRINGIFY(LINE_MAX) "s %" SCNd64 "", key, &value);
199
200 if (strcmp(key, "total_") < 0) {
201 return;
202 }
203
204 if (!strcmp(key, "total_pgfault"))
205 mem_st->pgfault = value;
206 else if (!strcmp(key, "total_pgmajfault"))
207 mem_st->pgmajfault = value;
208 else if (!strcmp(key, "total_rss"))
209 mem_st->rss_in_bytes = value;
210 else if (!strcmp(key, "total_cache"))
211 mem_st->cache_in_bytes = value;
212 else if (!strcmp(key, "total_swap"))
213 mem_st->swap_in_bytes = value;
214}
215
216static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
217 FILE *fp;
218 char buf[PATH_MAX];
219
220 snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
221
222 fp = fopen(buf, "r");
223
224 if (fp == NULL) {
225 return -1;
226 }
227
228 while (fgets(buf, PAGE_SIZE, fp) != NULL) {
229 memory_stat_parse_line(buf, mem_st);
230 }
231 fclose(fp);
232
233 return 0;
234}
235
236static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
237 char path[PATH_MAX];
238 char buffer[PROC_STAT_BUFFER_SIZE];
239 int fd, ret;
240
241 snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
242 if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
243 return -1;
244 }
245
246 ret = read(fd, buffer, sizeof(buffer));
247 if (ret < 0) {
248 close(fd);
249 return -1;
250 }
251 close(fd);
252
253 // field 10 is pgfault
254 // field 12 is pgmajfault
255 // field 22 is starttime
256 // field 24 is rss_in_pages
257 int64_t pgfault = 0, pgmajfault = 0, starttime = 0, rss_in_pages = 0;
258 if (sscanf(buffer,
259 "%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
260 "%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
261 "%" SCNd64 " %*d %" SCNd64 "",
262 &pgfault, &pgmajfault, &starttime, &rss_in_pages) != 4) {
263 return -1;
264 }
265 mem_st->pgfault = pgfault;
266 mem_st->pgmajfault = pgmajfault;
267 mem_st->rss_in_bytes = (rss_in_pages * PAGE_SIZE);
268 mem_st->process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
269
270 return 0;
271}
272
273struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t uid) {
274 static struct memory_stat mem_st = {};
275
276 if (!enable_stats_log) {
277 return NULL;
278 }
279
280 if (per_app_memcg) {
281 if (memory_stat_from_cgroup(&mem_st, pid, uid) == 0) {
282 return &mem_st;
283 }
284 } else {
285 if (memory_stat_from_procfs(&mem_st, pid) == 0) {
286 return &mem_st;
287 }
288 }
289
290 return NULL;
291}
292
293static void poll_kernel(int poll_fd) {
294 if (poll_fd == -1) {
295 // not waiting
296 return;
297 }
298
299 while (1) {
300 char rd_buf[256];
301 int bytes_read =
302 TEMP_FAILURE_RETRY(pread(poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
303 if (bytes_read <= 0) break;
304 rd_buf[bytes_read] = '\0';
305
306 int64_t pid;
307 int64_t uid;
308 int64_t group_leader_pid;
309 int64_t rss_in_pages;
310 struct memory_stat mem_st = {};
311 int16_t oom_score_adj;
312 int16_t min_score_adj;
313 int64_t starttime;
314 char* taskname = 0;
315
316 int fields_read = sscanf(rd_buf,
317 "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
318 " %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
319 &pid, &uid, &group_leader_pid, &mem_st.pgfault,
320 &mem_st.pgmajfault, &rss_in_pages, &oom_score_adj,
321 &min_score_adj, &starttime, &taskname);
322
323 /* only the death of the group leader process is logged */
324 if (fields_read == 10 && group_leader_pid == pid) {
325 mem_st.process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
326 mem_st.rss_in_bytes = rss_in_pages * PAGE_SIZE;
327 stats_write_lmk_kill_occurred_pid(LMK_KILL_OCCURRED, uid, pid, oom_score_adj,
328 min_score_adj, 0, &mem_st);
329 }
330
331 free(taskname);
332 }
333}
334
335bool init_poll_kernel(struct kernel_poll_info *poll_info) {
336 if (!enable_stats_log) {
337 return false;
338 }
339
340 poll_info->poll_fd =
341 TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
342
343 if (poll_info->poll_fd < 0) {
344 ALOGE("kernel lmk event file could not be opened; errno=%d", errno);
345 return false;
346 }
347 poll_info->handler = poll_kernel;
348
349 return true;
Jim Blacklerd2da8142019-09-10 15:30:05 +0100350}
351
352static void proc_insert(struct proc* procp) {
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700353 if (!pidhash) {
Jim Blacklerd2da8142019-09-10 15:30:05 +0100354 pidhash = calloc(PIDHASH_SZ, sizeof(struct proc));
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700355 }
356
Jim Blacklerd2da8142019-09-10 15:30:05 +0100357 int hval = pid_hashfn(procp->pid);
358 procp->pidhash_next = pidhash[hval];
359 pidhash[hval] = procp;
360}
361
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700362void stats_remove_taskname(int pid, int poll_fd) {
363 if (!enable_stats_log || !pidhash) {
364 return;
365 }
366
367 /*
368 * Perform an extra check before the pid is removed, after which it
369 * will be impossible for poll_kernel to get the taskname. poll_kernel()
370 * is potentially a long-running blocking function; however this method
371 * handles AMS requests but does not block AMS.
372 */
373 poll_kernel(poll_fd);
Jim Blacklerd2da8142019-09-10 15:30:05 +0100374
375 int hval = pid_hashfn(pid);
376 struct proc* procp;
377 struct proc* prevp;
378
379 for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid;
380 procp = procp->pidhash_next)
381 prevp = procp;
382
383 if (!procp)
384 return;
385
386 if (!prevp)
387 pidhash[hval] = procp->pidhash_next;
388 else
389 prevp->pidhash_next = procp->pidhash_next;
390
391 free(procp);
392}
393
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700394void stats_store_taskname(int pid, const char* taskname, int poll_fd) {
395 if (!enable_stats_log) {
Jim Blacklerd2da8142019-09-10 15:30:05 +0100396 return;
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700397 }
398
399 struct proc* procp = pid_lookup(pid);
400 if (procp != NULL) {
401 if (strcmp(procp->taskname, taskname) == 0) {
402 return;
403 }
404 stats_remove_taskname(pid, poll_fd);
405 }
Jim Blacklerd2da8142019-09-10 15:30:05 +0100406 procp = malloc(sizeof(struct proc));
Jim Blacklerd2da8142019-09-10 15:30:05 +0100407 procp->pid = pid;
408 strncpy(procp->taskname, taskname, LINE_MAX - 1);
409 procp->taskname[LINE_MAX - 1] = '\0';
410 proc_insert(procp);
411}
412
413void stats_purge_tasknames() {
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700414 if (!enable_stats_log || !pidhash) {
415 return;
416 }
417
Jim Blacklerd2da8142019-09-10 15:30:05 +0100418 struct proc* procp;
419 struct proc* next;
420 int i;
421 for (i = 0; i < PIDHASH_SZ; i++) {
422 procp = pidhash[i];
423 while (procp) {
424 next = procp->pidhash_next;
425 free(procp);
426 procp = next;
427 }
428 }
429 memset(pidhash, 0, PIDHASH_SZ * sizeof(struct proc));
430}
Suren Baghdasaryanb72c6652019-09-04 19:12:29 -0700431
432#endif /* LMKD_LOG_STATS */