4 * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * @file logging-memory.c
22 * @desc start memory logging system for resourced
24 * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
34 #include <sys/types.h>
36 #include <linux/limits.h>
42 #include <sys/utsname.h>
43 #include <systemd/sd-journal.h>
45 #include "resourced.h"
47 #include "file-helper.h"
50 #include "proc-process.h"
53 #define MEM_NAME "memory"
54 #define MEM_COMMIT_INTERVAL 30*60 /* 30 min */
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 */
62 struct logging_memory_info {
67 unsigned sampling_count;
78 unsigned shared_clean;
79 unsigned shared_dirty;
80 unsigned private_clean;
81 unsigned private_dirty;
84 static int ignore_smaps_field;
85 static struct mapinfo *mi;
86 static struct mapinfo *maps;
88 static void check_kernel_version(void)
98 if (buf.release[0] == '3') {
102 pch = strstr(buf.release, ".");
103 strncpy(str, pch+1, 2);
104 sub_version = atoi(str);
106 if (sub_version >= 10)
107 ignore_smaps_field = 8; /* Referenced, Anonymous, AnonHugePages,
108 Swap, KernelPageSize, MMUPageSize,
112 ignore_smaps_field = 7; /* Referenced, Anonymous, AnonHugePages,
113 Swap, KernelPageSize, MMUPageSize,
116 ignore_smaps_field = 4; /* Referenced, Swap, KernelPageSize,
122 /* 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so
123 * 012345678901234567890123456789012345678901234567890123456789
127 static int read_mapinfo(char** smaps, int rest_line)
132 if ((line = cgets(smaps)) == 0)
133 return RESOURCED_ERROR_FAIL;
137 _E("line is less than 1");
138 return RESOURCED_ERROR_FAIL;
141 if ((line = cgets(smaps)) == 0)
143 if (sscanf(line, "Size: %d kB", &mi->size) != 1)
145 if ((line = cgets(smaps)) == 0)
147 if (sscanf(line, "Rss: %d kB", &mi->rss) != 1)
149 if ((line = cgets(smaps)) == 0)
151 if (sscanf(line, "Pss: %d kB", &mi->pss) == 1)
152 if ((line = cgets(smaps)) == 0)
154 if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1)
156 if ((line = cgets(smaps)) == 0)
158 if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1)
160 if ((line = cgets(smaps)) == 0)
162 if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1)
164 if ((line = cgets(smaps)) == 0)
166 if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1)
169 while (rest_line-- && cgets(smaps))
172 return RESOURCED_ERROR_NONE;
174 _E("mi get error\n");
175 return RESOURCED_ERROR_FAIL;
178 static void init_maps()
183 maps->shared_clean = 0;
184 maps->shared_dirty = 0;
185 maps->private_clean = 0;
186 maps->private_dirty = 0;
189 static int load_maps(int pid)
194 snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
197 return RESOURCED_ERROR_FAIL;
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;
215 return RESOURCED_ERROR_NONE;
218 int get_pss(pid_t pid, unsigned *pss, unsigned *uss)
221 ret = load_maps(pid);
222 if (ret != RESOURCED_ERROR_NONE) {
227 *uss = maps->private_clean + maps->private_dirty;
233 static void update_log_interval(struct logging_memory_info *loginfo, int oom,
236 if (!always && (oom < OOMADJ_FOREGRD_LOCKED))
241 case OOMADJ_SERVICE_MIN:
243 loginfo->log_interval = MEM_MAX_INTERVAL;
246 loginfo->log_interval = MEM_INIT_INTERVAL;
248 case OOMADJ_FOREGRD_LOCKED:
249 case OOMADJ_FOREGRD_UNLOCKED:
250 case OOMADJ_BACKGRD_LOCKED:
251 loginfo->log_interval = MEM_FOREGRD_INTERVAL;
253 case OOMADJ_BACKGRD_UNLOCKED:
254 loginfo->log_interval = MEM_BACKGRD_INTERVAL;
257 if (oom > OOMADJ_BACKGRD_UNLOCKED)
258 loginfo->log_interval = MEM_BACKGRD_OLD_INTERVAL;
263 static int init_memory_info(void **pl, pid_t pid, int oom, time_t now)
265 struct logging_memory_info *info;
267 info = (struct logging_memory_info *)
268 malloc(sizeof(struct logging_memory_info));
270 _E("malloc for logging_memory_info is failed");
271 return RESOURCED_ERROR_FAIL;
273 info->current_pid = 0;
278 info->last_log_time = now;
279 info->sampling_count = 0;
280 info->last_commited = false;
282 update_log_interval(info, oom, 1);
284 return RESOURCED_ERROR_NONE;
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)
291 int ret = RESOURCED_ERROR_NONE;
292 struct logging_memory_info *loginfo = (struct logging_memory_info *)pl;
293 unsigned pss = 0, uss = 0;
296 if (now < loginfo->last_log_time + loginfo->log_interval)
299 ret = get_pss(pid, &pss, &uss);
301 if (ret != RESOURCED_ERROR_NONE)
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;
313 loginfo->sampling_count++;
314 loginfo->last_log_time = now;
315 loginfo->last_commited = false;
316 update_log_interval(loginfo, oom, 0);
321 static int write_memory_info(char *name, struct logging_infos *infos,
324 struct logging_memory_info *mi = infos->stats[ss_index];
326 if (!infos->running && mi->last_commited)
327 return RESOURCED_ERROR_NONE;
329 sd_journal_send("NAME=memory",
330 "TIME=%ld", mi->last_log_time,
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,
338 mi->last_commited = true;
340 return RESOURCED_ERROR_NONE;
343 static struct logging_info_ops memory_info_ops = {
344 .update = update_memory_info,
345 .write = write_memory_info,
346 .init = init_memory_info,
349 static int allocate_memory(void)
351 maps = (struct mapinfo *)malloc(sizeof(struct mapinfo));
354 _E("fail to allocate mapinfo\n");
355 return RESOURCED_ERROR_FAIL;
358 mi = malloc(sizeof(struct mapinfo));
360 _E("malloc failed for mapinfo");
362 return RESOURCED_ERROR_FAIL;
364 return RESOURCED_ERROR_NONE;
367 static void free_memory(void)
373 static int logging_memory_init(void *data)
376 check_kernel_version();
378 ret = allocate_memory();
380 if (ret != RESOURCED_ERROR_NONE) {
381 _E("allocate structures failed");
382 return RESOURCED_ERROR_FAIL;
385 ret = register_logging_subsystem(MEM_NAME, &memory_info_ops);
386 if(ret != RESOURCED_ERROR_NONE) {
387 _E("register logging subsystem failed");
389 return RESOURCED_ERROR_FAIL;
391 ret = update_commit_interval(MEM_NAME, MEM_COMMIT_INTERVAL);
392 if(ret != RESOURCED_ERROR_NONE) {
393 _E("update commit interval logging subsystem failed");
395 return RESOURCED_ERROR_FAIL;
398 _D("logging memory init finished");
399 return RESOURCED_ERROR_NONE;
402 static int logging_memory_exit(void *data)
404 _D("logging memory finalize");
406 return RESOURCED_ERROR_NONE;
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,
416 MODULE_REGISTER(&logging_memory_ops)