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