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 <fcntl.h>
20 #include <log/log.h>
21 #include <log/log_id.h>
22 #include <statslog.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/endian.h>
28 #include <sys/param.h>
29 #include <sys/uio.h>
30 #include <time.h>
31 #include <unistd.h>
32
33 #ifdef LMKD_LOG_STATS
34
35 #define STRINGIFY(x) STRINGIFY_INTERNAL(x)
36 #define STRINGIFY_INTERNAL(x) #x
37
38 /**
39 * Used to make sure that the payload is always smaller than LMKD_REPLY_MAX_SIZE
40 */
41 #define BUILD_BUG_ON(cond) ((void)sizeof(char[1 - 2 * !!(cond)]))
42
43 static bool enable_stats_log = property_get_bool("ro.lmk.log_stats", true);
44
45 struct proc {
46 int pid;
47 char taskname[MAX_TASKNAME_LEN];
48 struct proc* pidhash_next;
49 };
50
51 #define PIDHASH_SZ 1024
52 static struct proc** pidhash = NULL;
53 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
54
pid_lookup(int pid)55 static struct proc* pid_lookup(int pid) {
56 struct proc* procp;
57
58 if (!pidhash) return NULL;
59
60 for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid; procp = procp->pidhash_next)
61 ;
62
63 return procp;
64 }
65
memory_stat_parse_line(char * line,struct memory_stat * mem_st)66 static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
67 char key[MAX_TASKNAME_LEN + 1];
68 int64_t value;
69
70 sscanf(line, "%" STRINGIFY(MAX_TASKNAME_LEN) "s %" SCNd64 "", key, &value);
71
72 if (strcmp(key, "total_") < 0) {
73 return;
74 }
75
76 if (!strcmp(key, "total_pgfault"))
77 mem_st->pgfault = value;
78 else if (!strcmp(key, "total_pgmajfault"))
79 mem_st->pgmajfault = value;
80 else if (!strcmp(key, "total_rss"))
81 mem_st->rss_in_bytes = value;
82 else if (!strcmp(key, "total_cache"))
83 mem_st->cache_in_bytes = value;
84 else if (!strcmp(key, "total_swap"))
85 mem_st->swap_in_bytes = value;
86 }
87
memory_stat_from_cgroup(struct memory_stat * mem_st,int pid,uid_t uid)88 static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
89 FILE *fp;
90 char buf[PATH_MAX];
91
92 snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
93
94 fp = fopen(buf, "r");
95
96 if (fp == NULL) {
97 return -1;
98 }
99
100 while (fgets(buf, PAGE_SIZE, fp) != NULL) {
101 memory_stat_parse_line(buf, mem_st);
102 }
103 fclose(fp);
104
105 return 0;
106 }
107
memory_stat_from_procfs(struct memory_stat * mem_st,int pid)108 static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
109 char path[PATH_MAX];
110 char buffer[PROC_STAT_BUFFER_SIZE];
111 int fd, ret;
112
113 snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
114 if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
115 return -1;
116 }
117
118 ret = read(fd, buffer, sizeof(buffer));
119 if (ret < 0) {
120 close(fd);
121 return -1;
122 }
123 close(fd);
124
125 // field 10 is pgfault
126 // field 12 is pgmajfault
127 // field 22 is starttime
128 int64_t pgfault = 0, pgmajfault = 0, starttime = 0;
129 if (sscanf(buffer,
130 "%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
131 "%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
132 "%" SCNd64 "",
133 &pgfault, &pgmajfault, &starttime) != 3) {
134 return -1;
135 }
136 mem_st->pgfault = pgfault;
137 mem_st->pgmajfault = pgmajfault;
138 mem_st->process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
139
140 return 0;
141 }
142
stats_read_memory_stat(bool per_app_memcg,int pid,uid_t uid,int64_t rss_bytes,int64_t swap_bytes)143 struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t uid,
144 int64_t rss_bytes, int64_t swap_bytes) {
145 static struct memory_stat mem_st = {};
146 if (!enable_stats_log) {
147 return NULL;
148 }
149
150 if (per_app_memcg) {
151 if (memory_stat_from_cgroup(&mem_st, pid, uid) == 0) {
152 return &mem_st;
153 }
154 } else {
155 if (memory_stat_from_procfs(&mem_st, pid) == 0) {
156 mem_st.rss_in_bytes = rss_bytes;
157 mem_st.swap_in_bytes = swap_bytes;
158 return &mem_st;
159 }
160 }
161
162 return NULL;
163 }
164
proc_insert(struct proc * procp)165 static void proc_insert(struct proc* procp) {
166 if (!pidhash) {
167 pidhash = static_cast<struct proc**>(calloc(PIDHASH_SZ, sizeof(*pidhash)));
168 }
169
170 int hval = pid_hashfn(procp->pid);
171 procp->pidhash_next = pidhash[hval];
172 pidhash[hval] = procp;
173 }
174
stats_remove_taskname(int pid)175 void stats_remove_taskname(int pid) {
176 if (!enable_stats_log || !pidhash) {
177 return;
178 }
179
180 int hval = pid_hashfn(pid);
181 struct proc* procp;
182 struct proc* prevp;
183
184 for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid;
185 procp = procp->pidhash_next)
186 prevp = procp;
187
188 if (!procp)
189 return;
190
191 if (!prevp)
192 pidhash[hval] = procp->pidhash_next;
193 else
194 prevp->pidhash_next = procp->pidhash_next;
195
196 free(procp);
197 }
198
stats_store_taskname(int pid,const char * taskname)199 void stats_store_taskname(int pid, const char* taskname) {
200 if (!enable_stats_log || !taskname) {
201 return;
202 }
203
204 struct proc* procp = pid_lookup(pid);
205 if (procp != NULL) {
206 if (strcmp(procp->taskname, taskname) == 0) {
207 return;
208 }
209 stats_remove_taskname(pid);
210 }
211 procp = static_cast<struct proc*>(malloc(sizeof(struct proc)));
212 procp->pid = pid;
213 strncpy(procp->taskname, taskname, MAX_TASKNAME_LEN - 1);
214 procp->taskname[MAX_TASKNAME_LEN - 1] = '\0';
215 proc_insert(procp);
216 }
217
stats_purge_tasknames()218 void stats_purge_tasknames() {
219 if (!enable_stats_log || !pidhash) {
220 return;
221 }
222
223 struct proc* procp;
224 struct proc* next;
225 int i;
226 for (i = 0; i < PIDHASH_SZ; i++) {
227 procp = pidhash[i];
228 while (procp) {
229 next = procp->pidhash_next;
230 free(procp);
231 procp = next;
232 }
233 }
234 memset(pidhash, 0, PIDHASH_SZ * sizeof(*pidhash));
235 }
236
stats_get_task_name(int pid)237 const char* stats_get_task_name(int pid) {
238 struct proc* proc = pid_lookup(pid);
239 return proc ? proc->taskname : NULL;
240 }
241
242 /**
243 * Writes int32 in a machine independent way
244 * https://docs.oracle.com/javase/7/docs/api/java/io/DataOutput.html#writeInt(int)
245 */
pack_int32(LMK_KILL_OCCURRED_PACKET packet,size_t index,int32_t value)246 static inline size_t pack_int32(LMK_KILL_OCCURRED_PACKET packet,
247 size_t index,
248 int32_t value) {
249 int32_t* int_buffer = (int32_t*)(packet + index);
250
251 *int_buffer = htonl(value);
252
253 return index + sizeof(int32_t);
254 }
255
256 /**
257 * Writes int64 in a machine independent way
258 * https://docs.oracle.com/javase/7/docs/api/java/io/DataOutput.html#writeLong(long)
259 */
pack_int64(LMK_KILL_OCCURRED_PACKET packet,size_t index,int64_t value)260 static inline size_t pack_int64(LMK_KILL_OCCURRED_PACKET packet,
261 size_t index,
262 int64_t value) {
263 int64_t* int64_buffer = (int64_t*)(packet + index);
264
265 *int64_buffer = htonq(value);
266
267 return index + sizeof(int64_t);
268 }
269
270 /**
271 * Writes ANSI string in a machine independent way
272 * https://docs.oracle.com/javase/7/docs/api/java/io/DataOutput.html#writeShort(int)
273 * 2 bytes str len following n chars
274 * to be read on the Java side with
275 * https://docs.oracle.com/javase/7/docs/api/java/io/DataInput.html#readUTF()
276 * Truncates the value string & packs up to MAX_TASKNAME_LEN - 1 chars
277 */
pack_string(LMK_KILL_OCCURRED_PACKET packet,size_t index,const char * value)278 static inline size_t pack_string(LMK_KILL_OCCURRED_PACKET packet,
279 size_t index,
280 const char* value) {
281 const size_t len_proc_name = MIN(strlen(value), MAX_TASKNAME_LEN - 1);
282 int16_t* short_buffer = (int16_t*)(packet + index);
283 *short_buffer = htons((int16_t)len_proc_name);
284
285 char* byte_buffer = (char*)(short_buffer + 1);
286 strncpy(byte_buffer, value, MAX_TASKNAME_LEN - 1);
287 byte_buffer[MAX_TASKNAME_LEN - 1] = '\0';
288
289 return index + sizeof(int16_t) + len_proc_name + 1;
290 }
291
lmkd_pack_set_kill_occurred(LMK_KILL_OCCURRED_PACKET packet,struct kill_stat * kill_stat,struct memory_stat * mem_stat)292 size_t lmkd_pack_set_kill_occurred(LMK_KILL_OCCURRED_PACKET packet,
293 struct kill_stat *kill_stat,
294 struct memory_stat *mem_stat) {
295 BUILD_BUG_ON(sizeof(LMK_KILL_OCCURRED_PACKET) > LMKD_REPLY_MAX_SIZE);
296
297 if (!enable_stats_log) {
298 return 0;
299 }
300
301 int32_t index = 0;
302 index = pack_int32(packet, index, LMK_STAT_KILL_OCCURRED);
303
304 if (mem_stat) {
305 index = pack_int64(packet, index, mem_stat->pgfault);
306 index = pack_int64(packet, index, mem_stat->pgmajfault);
307 index = pack_int64(packet, index, mem_stat->rss_in_bytes);
308 index = pack_int64(packet, index, mem_stat->cache_in_bytes);
309 index = pack_int64(packet, index, mem_stat->swap_in_bytes);
310 index = pack_int64(packet, index, mem_stat->process_start_time_ns);
311 } else {
312 index = pack_int64(packet, index, -1);
313 index = pack_int64(packet, index, -1);
314 index = pack_int64(packet, index, -1);
315 index = pack_int64(packet, index, -1);
316 index = pack_int64(packet, index, -1);
317 index = pack_int64(packet, index, -1);
318 }
319
320 index = pack_int32(packet, index, kill_stat->uid);
321 index = pack_int32(packet, index, kill_stat->oom_score);
322 index = pack_int32(packet, index, kill_stat->min_oom_score);
323 index = pack_int32(packet, index, (int)kill_stat->free_mem_kb);
324 index = pack_int32(packet, index, (int)kill_stat->free_swap_kb);
325 index = pack_int32(packet, index, (int)kill_stat->kill_reason);
326 index = pack_int32(packet, index, kill_stat->thrashing);
327 index = pack_int32(packet, index, kill_stat->max_thrashing);
328
329 index = pack_string(packet, index, kill_stat->taskname);
330 return index;
331 }
332
lmkd_pack_set_state_changed(LMKD_CTRL_PACKET packet,enum lmk_state state)333 size_t lmkd_pack_set_state_changed(LMKD_CTRL_PACKET packet,
334 enum lmk_state state) {
335 if (!enable_stats_log) {
336 return 0;
337 }
338
339 packet[0] = htonl(LMK_STAT_STATE_CHANGED);
340 packet[1] = htonl(state);
341
342 return 2 * sizeof(int);
343 }
344
345 #endif /* LMKD_LOG_STATS */
346