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