tizen 2.3.1 release
[kernel/api/system-resource.git] / src / memory / logging-memory.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 /*
20  * @file logging-memory.c
21  *
22  * @desc start memory logging system for resourced
23  *
24  * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
25  *
26  */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/vfs.h>
36 #include <linux/limits.h>
37
38 #include <ctype.h>
39 #include <stddef.h>
40
41 #include <dirent.h>
42 #include <sys/utsname.h>
43 #include <systemd/sd-journal.h>
44
45 #include "resourced.h"
46 #include "trace.h"
47 #include "file-helper.h"
48 #include "module.h"
49 #include "macro.h"
50 #include "proc-process.h"
51 #include "logging.h"
52
53 #define MEM_NAME "memory"
54 #define MEM_COMMIT_INTERVAL             30*60   /* 30 min */
55
56 #define MEM_MAX_INTERVAL                10*60   /* 10 min */
57 #define MEM_INIT_INTERVAL               8*60    /* 8 min */
58 #define MEM_FOREGRD_INTERVAL            5*60    /* 5 min */
59 #define MEM_BACKGRD_INTERVAL            10*60   /* 10 min */
60 #define MEM_BACKGRD_OLD_INTERVAL        15*60   /* 15 min */
61
62 struct logging_memory_info {
63         unsigned avg_pss;
64         unsigned max_pss;
65         unsigned avg_uss;
66         unsigned max_uss;
67         unsigned sampling_count;
68         bool last_commited;
69         time_t last_log_time;
70         time_t log_interval;
71         pid_t current_pid;
72 };
73
74 struct mapinfo {
75         unsigned size;
76         unsigned rss;
77         unsigned pss;
78         unsigned shared_clean;
79         unsigned shared_dirty;
80         unsigned private_clean;
81         unsigned private_dirty;
82 };
83
84 static int ignore_smaps_field;
85 static struct mapinfo *mi;
86 static struct mapinfo *maps;
87
88 static void check_kernel_version(void)
89 {
90         struct utsname buf;
91         int ret;
92
93         ret = uname(&buf);
94
95         if (ret)
96                 return;
97
98         if (buf.release[0] == '3') {
99                 char *pch;
100                 char str[3];
101                 int sub_version;
102                 pch = strstr(buf.release, ".");
103                 strncpy(str, pch+1, 2);
104                 sub_version = atoi(str);
105
106                 if (sub_version >= 10)
107                         ignore_smaps_field = 8; /* Referenced, Anonymous, AnonHugePages,
108                                                    Swap, KernelPageSize, MMUPageSize,
109                                                    Locked, VmFlags */
110
111                 else
112                         ignore_smaps_field = 7; /* Referenced, Anonymous, AnonHugePages,
113                                                    Swap, KernelPageSize, MMUPageSize,
114                                                    Locked */
115         } else {
116                 ignore_smaps_field = 4; /* Referenced, Swap, KernelPageSize,
117                                            MMUPageSize */
118         }
119 }
120
121
122 /* 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /android/lib/libcomposer.so
123  * 012345678901234567890123456789012345678901234567890123456789
124  * 0         1         2         3         4         5
125  */
126
127 static int read_mapinfo(char** smaps, int rest_line)
128 {
129         char* line;
130         int len;
131
132         if ((line = cgets(smaps)) == 0)
133                 return RESOURCED_ERROR_FAIL;
134
135         len    = strlen(line);
136         if (len < 1) {
137                 _E("line is less than 1");
138                 return RESOURCED_ERROR_FAIL;
139         }
140
141         if ((line = cgets(smaps)) == 0)
142                 goto oops;
143         if (sscanf(line, "Size: %d kB", &mi->size) != 1)
144                 goto oops;
145         if ((line = cgets(smaps)) == 0)
146                 goto oops;
147         if (sscanf(line, "Rss: %d kB", &mi->rss) != 1)
148                 goto oops;
149         if ((line = cgets(smaps)) == 0)
150                 goto oops;
151         if (sscanf(line, "Pss: %d kB", &mi->pss) == 1)
152                 if ((line = cgets(smaps)) == 0)
153                         goto oops;
154         if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1)
155                 goto oops;
156         if ((line = cgets(smaps)) == 0)
157                 goto oops;
158         if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1)
159                 goto oops;
160         if ((line = cgets(smaps)) == 0)
161                 goto oops;
162         if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1)
163                 goto oops;
164         if ((line = cgets(smaps)) == 0)
165                 goto oops;
166         if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1)
167                 goto oops;
168
169         while (rest_line-- && cgets(smaps))
170                 ;
171
172         return RESOURCED_ERROR_NONE;
173  oops:
174         _E("mi get error\n");
175         return RESOURCED_ERROR_FAIL;
176 }
177
178 static void init_maps()
179 {
180         maps->size = 0;
181         maps->rss = 0;
182         maps->pss = 0;
183         maps->shared_clean = 0;
184         maps->shared_dirty = 0;
185         maps->private_clean = 0;
186         maps->private_dirty = 0;
187 }
188
189 static int load_maps(int pid)
190 {
191         char* smaps, *start;
192         char tmp[128];
193
194         snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
195         smaps = cread(tmp);
196         if (smaps == NULL)
197                 return RESOURCED_ERROR_FAIL;
198
199         start = smaps;
200         init_maps();
201
202         while (read_mapinfo(&smaps, ignore_smaps_field)
203                         == RESOURCED_ERROR_NONE) {
204                 maps->size += mi->size;
205                 maps->rss += mi->rss;
206                 maps->pss += mi->pss;
207                 maps->shared_clean += mi->shared_clean;
208                 maps->shared_dirty += mi->shared_dirty;
209                 maps->private_clean += mi->private_clean;
210                 maps->private_dirty += mi->private_dirty;
211         }
212
213         if(start)
214                 free(start);
215         return RESOURCED_ERROR_NONE;
216 }
217
218 int get_pss(pid_t pid, unsigned *pss, unsigned *uss)
219 {
220         int ret;
221         ret = load_maps(pid);
222         if (ret != RESOURCED_ERROR_NONE) {
223                 *pss = 0;
224                 *uss = 0;
225         } else {
226                 *pss = maps->pss;
227                 *uss = maps->private_clean + maps->private_dirty;
228         }
229
230         return ret;
231 }
232
233 static void update_log_interval(struct logging_memory_info *loginfo, int oom,
234         int always)
235 {
236         if (!always && (oom < OOMADJ_FOREGRD_LOCKED))
237                 return;
238
239         switch (oom) {
240         case OOMADJ_DISABLE:
241         case OOMADJ_SERVICE_MIN:
242         case OOMADJ_SU:
243                 loginfo->log_interval = MEM_MAX_INTERVAL;
244                 break;
245         case OOMADJ_INIT:
246                 loginfo->log_interval = MEM_INIT_INTERVAL;
247                 break;
248         case OOMADJ_FOREGRD_LOCKED:
249         case OOMADJ_FOREGRD_UNLOCKED:
250         case OOMADJ_BACKGRD_LOCKED:
251                 loginfo->log_interval = MEM_FOREGRD_INTERVAL;
252                 break;
253         case OOMADJ_BACKGRD_UNLOCKED:
254                 loginfo->log_interval = MEM_BACKGRD_INTERVAL;
255                 break;
256         default:
257                 if (oom > OOMADJ_BACKGRD_UNLOCKED)
258                         loginfo->log_interval = MEM_BACKGRD_OLD_INTERVAL;
259                 break;
260         }
261 }
262
263 static int init_memory_info(void **pl, pid_t pid, int oom, time_t now)
264 {
265         struct logging_memory_info *info;
266
267         info = (struct logging_memory_info *)
268                         malloc(sizeof(struct logging_memory_info));
269         if (!info) {
270                 _E("malloc for logging_memory_info is failed");
271                 return RESOURCED_ERROR_FAIL;
272         }
273         info->current_pid = 0;
274         info->avg_pss = 0;
275         info->max_pss = 0;
276         info->avg_uss = 0;
277         info->max_uss = 0;
278         info->last_log_time = now;
279         info->sampling_count = 0;
280         info->last_commited = false;
281
282         update_log_interval(info, oom, 1);
283         *pl = (void *)info;
284         return RESOURCED_ERROR_NONE;
285 }
286
287 /* pss_interval should be adjusted depending on app type */
288 static int update_memory_info(void *pl, pid_t pid, int oom,
289         time_t now, unsigned always)
290 {
291         int ret = RESOURCED_ERROR_NONE;
292         struct logging_memory_info *loginfo = (struct logging_memory_info *)pl;
293         unsigned pss = 0, uss = 0;
294
295         if (!always)
296                 if (now < loginfo->last_log_time + loginfo->log_interval)
297                         return ret;
298
299         ret = get_pss(pid, &pss, &uss);
300
301         if (ret != RESOURCED_ERROR_NONE)
302                 return ret;
303
304         loginfo->avg_pss = (loginfo->avg_pss * loginfo->sampling_count +
305                         pss)/(loginfo->sampling_count + 1);
306         loginfo->avg_uss = (loginfo->avg_uss * loginfo->sampling_count +
307                         uss)/(loginfo->sampling_count + 1);
308         if (pss > loginfo->max_pss)
309                 loginfo->max_pss = pss;
310         if (uss > loginfo->max_uss)
311                 loginfo->max_uss = uss;
312
313         loginfo->sampling_count++;
314         loginfo->last_log_time = now;
315         loginfo->last_commited = false;
316         update_log_interval(loginfo, oom, 0);
317
318         return ret;
319 }
320
321 static int write_memory_info(char *name, struct logging_infos *infos,
322         int ss_index)
323 {
324         struct logging_memory_info *mi = infos->stats[ss_index];
325
326         if (!infos->running && mi->last_commited)
327                 return RESOURCED_ERROR_NONE;
328
329         sd_journal_send("NAME=memory",
330                 "TIME=%ld", mi->last_log_time,
331                 "PNAME=%s", name,
332                 "AVG_PSS=%lu", mi->avg_pss,
333                 "MAX_PSS=%lu", mi->max_pss,
334                 "AVG_USS=%lu", mi->avg_uss,
335                 "MAX_USS=%lu", mi->max_uss,
336                 NULL);
337
338         mi->last_commited = true;
339
340         return RESOURCED_ERROR_NONE;
341 }
342
343 static struct logging_info_ops memory_info_ops = {
344         .update = update_memory_info,
345         .write  = write_memory_info,
346         .init   = init_memory_info,
347 };
348
349 static int allocate_memory(void)
350 {
351         maps = (struct mapinfo *)malloc(sizeof(struct mapinfo));
352
353         if (!maps) {
354                 _E("fail to allocate mapinfo\n");
355                 return RESOURCED_ERROR_FAIL;
356         }
357
358         mi = malloc(sizeof(struct mapinfo));
359         if (mi == NULL) {
360                 _E("malloc failed for mapinfo");
361                 free(maps);
362                 return RESOURCED_ERROR_FAIL;
363         }
364         return RESOURCED_ERROR_NONE;
365 }
366
367 static void free_memory(void)
368 {
369         free(maps);
370         free(mi);
371 }
372
373 static int logging_memory_init(void *data)
374 {
375         int ret;
376         check_kernel_version();
377
378         ret = allocate_memory();
379
380         if (ret != RESOURCED_ERROR_NONE) {
381                 _E("allocate structures failed");
382                 return RESOURCED_ERROR_FAIL;
383         }
384
385         ret = register_logging_subsystem(MEM_NAME, &memory_info_ops);
386         if(ret != RESOURCED_ERROR_NONE) {
387                 _E("register logging subsystem failed");
388                 free_memory();
389                 return RESOURCED_ERROR_FAIL;
390         }
391         ret = update_commit_interval(MEM_NAME, MEM_COMMIT_INTERVAL);
392         if(ret != RESOURCED_ERROR_NONE) {
393                 _E("update commit interval logging subsystem failed");
394                 free_memory();
395                 return RESOURCED_ERROR_FAIL;
396         }
397
398         _D("logging memory init finished");
399         return RESOURCED_ERROR_NONE;
400 }
401
402 static int logging_memory_exit(void *data)
403 {
404         _D("logging memory finalize");
405         free_memory();
406         return RESOURCED_ERROR_NONE;
407 }
408
409 static struct module_ops logging_memory_ops = {
410         .priority       = MODULE_PRIORITY_NORMAL,
411         .name           = "logging_memory",
412         .init           = logging_memory_init,
413         .exit           = logging_memory_exit,
414 };
415
416 MODULE_REGISTER(&logging_memory_ops)