1 /*
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>
19 #include <log/log.h>
20 #include <log/log_id.h>
21 #include <statslog.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/uio.h>
27 #include <time.h>
28
29 #ifdef LMKD_LOG_STATS
30
31 #define LINE_MAX 128
32 #define STRINGIFY(x) STRINGIFY_INTERNAL(x)
33 #define STRINGIFY_INTERNAL(x) #x
34
35 static bool enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
36
37 struct proc {
38 int pid;
39 char taskname[LINE_MAX];
40 struct proc* pidhash_next;
41 };
42
43 #define PIDHASH_SZ 1024
44 static struct proc** pidhash = NULL;
45 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
46
47 /**
48 * Logs the change in LMKD state which is used as start/stop boundaries for logging
49 * LMK_KILL_OCCURRED event.
50 * Code: LMK_STATE_CHANGED = 54
51 */
52 int
stats_write_lmk_state_changed(int32_t state)53 stats_write_lmk_state_changed(int32_t state) {
54 if (enable_stats_log) {
55 return android::lmkd::stats::stats_write(android::lmkd::stats::LMK_STATE_CHANGED, state);
56 } else {
57 return -EINVAL;
58 }
59 }
60
pid_lookup(int pid)61 static struct proc* pid_lookup(int pid) {
62 struct proc* procp;
63
64 if (!pidhash) return NULL;
65
66 for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid; procp = procp->pidhash_next)
67 ;
68
69 return procp;
70 }
71
map_kill_reason(enum kill_reasons reason)72 inline int32_t map_kill_reason(enum kill_reasons reason) {
73 switch (reason) {
74 case PRESSURE_AFTER_KILL:
75 return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__PRESSURE_AFTER_KILL;
76 case NOT_RESPONDING:
77 return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__NOT_RESPONDING;
78 case LOW_SWAP_AND_THRASHING:
79 return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__LOW_SWAP_AND_THRASHING;
80 case LOW_MEM_AND_SWAP:
81 return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_SWAP;
82 case LOW_MEM_AND_THRASHING:
83 return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_THRASHING;
84 case DIRECT_RECL_AND_THRASHING:
85 return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__DIRECT_RECL_AND_THRASHING;
86 case LOW_MEM_AND_SWAP_UTIL:
87 return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_SWAP_UTIL;
88 default:
89 return android::lmkd::stats::LMK_KILL_OCCURRED__REASON__UNKNOWN;
90 }
91 }
92
93 /**
94 * Logs the event when LMKD kills a process to reduce memory pressure.
95 * Code: LMK_KILL_OCCURRED = 51
96 */
stats_write_lmk_kill_occurred(struct kill_stat * kill_st,struct memory_stat * mem_st)97 int stats_write_lmk_kill_occurred(struct kill_stat *kill_st, struct memory_stat *mem_st) {
98 if (enable_stats_log) {
99 return android::lmkd::stats::stats_write(
100 android::lmkd::stats::LMK_KILL_OCCURRED,
101 kill_st->uid,
102 kill_st->taskname,
103 kill_st->oom_score,
104 mem_st ? mem_st->pgfault : -1,
105 mem_st ? mem_st->pgmajfault : -1,
106 mem_st ? mem_st->rss_in_bytes : kill_st->tasksize * BYTES_IN_KILOBYTE,
107 mem_st ? mem_st->cache_in_bytes : -1,
108 mem_st ? mem_st->swap_in_bytes : -1,
109 mem_st ? mem_st->process_start_time_ns : -1,
110 kill_st->min_oom_score,
111 kill_st->free_mem_kb,
112 kill_st->free_swap_kb,
113 map_kill_reason(kill_st->kill_reason)
114 );
115 } else {
116 return -EINVAL;
117 }
118 }
119
stats_write_lmk_kill_occurred_pid(int pid,struct kill_stat * kill_st,struct memory_stat * mem_st)120 int stats_write_lmk_kill_occurred_pid(int pid, struct kill_stat *kill_st,
121 struct memory_stat* mem_st) {
122 struct proc* proc = pid_lookup(pid);
123 if (!proc) return -EINVAL;
124
125 kill_st->taskname = proc->taskname;
126 return stats_write_lmk_kill_occurred(kill_st, mem_st);
127 }
128
memory_stat_parse_line(char * line,struct memory_stat * mem_st)129 static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
130 char key[LINE_MAX + 1];
131 int64_t value;
132
133 sscanf(line, "%" STRINGIFY(LINE_MAX) "s %" SCNd64 "", key, &value);
134
135 if (strcmp(key, "total_") < 0) {
136 return;
137 }
138
139 if (!strcmp(key, "total_pgfault"))
140 mem_st->pgfault = value;
141 else if (!strcmp(key, "total_pgmajfault"))
142 mem_st->pgmajfault = value;
143 else if (!strcmp(key, "total_rss"))
144 mem_st->rss_in_bytes = value;
145 else if (!strcmp(key, "total_cache"))
146 mem_st->cache_in_bytes = value;
147 else if (!strcmp(key, "total_swap"))
148 mem_st->swap_in_bytes = value;
149 }
150
memory_stat_from_cgroup(struct memory_stat * mem_st,int pid,uid_t uid)151 static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
152 FILE *fp;
153 char buf[PATH_MAX];
154
155 snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
156
157 fp = fopen(buf, "r");
158
159 if (fp == NULL) {
160 return -1;
161 }
162
163 while (fgets(buf, PAGE_SIZE, fp) != NULL) {
164 memory_stat_parse_line(buf, mem_st);
165 }
166 fclose(fp);
167
168 return 0;
169 }
170
memory_stat_from_procfs(struct memory_stat * mem_st,int pid)171 static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
172 char path[PATH_MAX];
173 char buffer[PROC_STAT_BUFFER_SIZE];
174 int fd, ret;
175
176 snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
177 if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
178 return -1;
179 }
180
181 ret = read(fd, buffer, sizeof(buffer));
182 if (ret < 0) {
183 close(fd);
184 return -1;
185 }
186 close(fd);
187
188 // field 10 is pgfault
189 // field 12 is pgmajfault
190 // field 22 is starttime
191 // field 24 is rss_in_pages
192 int64_t pgfault = 0, pgmajfault = 0, starttime = 0, rss_in_pages = 0;
193 if (sscanf(buffer,
194 "%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
195 "%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
196 "%" SCNd64 " %*d %" SCNd64 "",
197 &pgfault, &pgmajfault, &starttime, &rss_in_pages) != 4) {
198 return -1;
199 }
200 mem_st->pgfault = pgfault;
201 mem_st->pgmajfault = pgmajfault;
202 mem_st->rss_in_bytes = (rss_in_pages * PAGE_SIZE);
203 mem_st->process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
204
205 return 0;
206 }
207
stats_read_memory_stat(bool per_app_memcg,int pid,uid_t uid)208 struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t uid) {
209 static struct memory_stat mem_st = {};
210
211 if (!enable_stats_log) {
212 return NULL;
213 }
214
215 if (per_app_memcg) {
216 if (memory_stat_from_cgroup(&mem_st, pid, uid) == 0) {
217 return &mem_st;
218 }
219 } else {
220 if (memory_stat_from_procfs(&mem_st, pid) == 0) {
221 return &mem_st;
222 }
223 }
224
225 return NULL;
226 }
227
proc_insert(struct proc * procp)228 static void proc_insert(struct proc* procp) {
229 if (!pidhash) {
230 pidhash = static_cast<struct proc**>(calloc(PIDHASH_SZ, sizeof(*pidhash)));
231 }
232
233 int hval = pid_hashfn(procp->pid);
234 procp->pidhash_next = pidhash[hval];
235 pidhash[hval] = procp;
236 }
237
stats_remove_taskname(int pid)238 void stats_remove_taskname(int pid) {
239 if (!enable_stats_log || !pidhash) {
240 return;
241 }
242
243 int hval = pid_hashfn(pid);
244 struct proc* procp;
245 struct proc* prevp;
246
247 for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid;
248 procp = procp->pidhash_next)
249 prevp = procp;
250
251 if (!procp)
252 return;
253
254 if (!prevp)
255 pidhash[hval] = procp->pidhash_next;
256 else
257 prevp->pidhash_next = procp->pidhash_next;
258
259 free(procp);
260 }
261
stats_store_taskname(int pid,const char * taskname)262 void stats_store_taskname(int pid, const char* taskname) {
263 if (!enable_stats_log || !taskname) {
264 return;
265 }
266
267 struct proc* procp = pid_lookup(pid);
268 if (procp != NULL) {
269 if (strcmp(procp->taskname, taskname) == 0) {
270 return;
271 }
272 stats_remove_taskname(pid);
273 }
274 procp = static_cast<struct proc*>(malloc(sizeof(struct proc)));
275 procp->pid = pid;
276 strncpy(procp->taskname, taskname, LINE_MAX - 1);
277 procp->taskname[LINE_MAX - 1] = '\0';
278 proc_insert(procp);
279 }
280
stats_purge_tasknames()281 void stats_purge_tasknames() {
282 if (!enable_stats_log || !pidhash) {
283 return;
284 }
285
286 struct proc* procp;
287 struct proc* next;
288 int i;
289 for (i = 0; i < PIDHASH_SZ; i++) {
290 procp = pidhash[i];
291 while (procp) {
292 next = procp->pidhash_next;
293 free(procp);
294 procp = next;
295 }
296 }
297 memset(pidhash, 0, PIDHASH_SZ * sizeof(*pidhash));
298 }
299
300 #endif /* LMKD_LOG_STATS */
301