• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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