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