To manage memory usage, the launchpad checks the proc filesystem.
If the percentage of memory usage is over the threshold value, the
launchpad terminates the running slots. In this time, the running slots
are managed by the score and the memory usage(PSS). (exclude hydra loader)
The score of the candidate slot is decided by the amount of usage.
Change-Id: Ie8678be7dc6dfb346b4523b996bfa39d02b006f5
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
LowValue=2
NormalKey=memory/sysman/low_memory
NormalValue=1
+
+[MemoryMonitor]
+Threshold=80
+Interval=5000
CONFIG_TYPE_MEMORY_STATUS_LOW_VALUE,
CONFIG_TYPE_MEMORY_STATUS_NORMAL_KEY,
CONFIG_TYPE_MEMORY_STATUS_NORMAL_VALUE,
+ CONFIG_TYPE_MEMORY_MONITOR_THRESHOLD,
+ CONFIG_TYPE_MEMORY_MONITOR_INTERVAL,
} config_type_e;
const char *_config_get_string_value(config_type_e type);
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LAUNCHPAD_MEMORY_MONITOR_H__
+#define __LAUNCHPAD_MEMORY_MONITOR_H__
+
+#include <stdbool.h>
+
+typedef int (*memory_monitor_cb)(bool low_memory, void *user_data);
+
+int _memory_monitor_init(void);
+
+void _memory_monitor_fini(void);
+
+int _memory_monitor_set_event_cb(memory_monitor_cb callback, void *user_data);
+
+int _memory_monitor_reset_timer(void);
+
+bool _memory_monitor_is_low_memory(void);
+
+#endif /* __LAUNCHPAD_MEMORY_MONITOR_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LAUNCHPAD_PROC_H__
+#define __LAUNCHPAD_PROC_H__
+
+int _proc_get_mem_used_ratio(unsigned int *mem_used_ratio);
+
+int _proc_get_mem_pss(int pid, unsigned int *mem_pss);
+
+#endif /* __LAUNCHPAD_PROC_H__ */
#include "launchpad_common.h"
#include "launchpad_config.h"
#include "launchpad_debug.h"
+#include "launchpad_inotify.h"
#include "launchpad_io_channel.h"
+#include "launchpad_memory_monitor.h"
+#include "launchpad_proc.h"
#include "launchpad_signal.h"
#include "launchpad_types.h"
#include "loader_info.h"
#include "perf.h"
-#include "launchpad_inotify.h"
#define AUL_PR_NAME 16
#define EXEC_CANDIDATE_EXPIRED 5
#define PAD_ERR_INVALID_PATH -4
#define CPU_CHECKER_TIMEOUT 1000
#define PI 3.14159265
+#define WIN_SCORE 100
+#define LOSE_SCORE_RATE 0.7f
enum candidate_process_state_e {
CANDIDATE_PROCESS_STATE_RUNNING,
io_channel_h client_channel;
io_channel_h channel;
io_channel_h hydra_channel;
+ unsigned int score;
+ unsigned int pss;
} candidate_process_context_t;
typedef struct {
static int __add_idle_checker(int detection_method, GList *cur);
static void __dispose_candidate_process(candidate_process_context_t *cpc);
static bool __is_low_memory(void);
-static void __update_slot_state(candidate_process_context_t *cpc, int method);
+static void __update_slot_state(candidate_process_context_t *cpc, int method,
+ bool force);
static void __init_app_defined_loader_monitor(void);
static gboolean __handle_queuing_slots(gpointer data)
return __find_slot_from_static_type(type);
}
+static void __update_slot_score(candidate_process_context_t *slot)
+{
+ if (!slot)
+ return;
+
+ slot->score *= LOSE_SCORE_RATE;
+ slot->score += WIN_SCORE;
+}
+
+static void __update_slots_pss(void)
+{
+ candidate_process_context_t *cpc;
+ GList *iter;
+
+ iter = candidate_slot_list;
+ while (iter) {
+ cpc = (candidate_process_context_t *)iter->data;
+ iter = g_list_next(iter);
+
+ if (cpc->pid == CANDIDATE_NONE)
+ continue;
+
+ _proc_get_mem_pss(cpc->pid, &cpc->pss);
+ }
+}
+
+static gint __compare_slot(gconstpointer a, gconstpointer b)
+{
+ candidate_process_context_t *slot_a = (candidate_process_context_t *)a;
+ candidate_process_context_t *slot_b = (candidate_process_context_t *)b;
+
+ if (slot_a->is_hydra && !slot_b->is_hydra)
+ return -1;
+ if (!slot_a->is_hydra && slot_b->is_hydra)
+ return 1;
+
+ if (slot_a->score < slot_b->score)
+ return 1;
+ if (slot_a->score > slot_b->score)
+ return -1;
+
+ if (slot_a->pss < slot_b->pss)
+ return -1;
+ if (slot_a->pss > slot_b->pss)
+ return 1;
+
+ return 0;
+}
+
+static candidate_process_context_t *__get_running_slot(bool is_hydra)
+{
+ candidate_process_context_t *cpc;
+ GList *iter;
+
+ iter = candidate_slot_list;
+ while (iter) {
+ cpc = (candidate_process_context_t *)iter->data;
+ if (cpc->is_hydra == is_hydra && cpc->pid != CANDIDATE_NONE)
+ return cpc;
+
+ iter = g_list_next(iter);
+ }
+
+ return NULL;
+}
+
+static void __pause_last_running_slot(bool is_hydra)
+{
+ candidate_process_context_t *cpc = NULL;
+ GList *iter;
+
+ iter = g_list_last(candidate_slot_list);
+ while (iter) {
+ cpc = (candidate_process_context_t *)iter->data;
+ if (cpc->is_hydra == is_hydra && cpc->pid != CANDIDATE_NONE)
+ break;
+
+ iter = g_list_previous(iter);
+ }
+
+ if (!cpc)
+ return;
+
+ __update_slot_state(cpc, METHOD_OUT_OF_MEMORY, true);
+}
+
+static void __resume_all_slots(void)
+{
+ candidate_process_context_t *cpc;
+ GList *iter;
+
+ iter = candidate_slot_list;
+ while (iter) {
+ cpc = (candidate_process_context_t *)iter->data;
+ __update_slot_state(cpc, METHOD_AVAILABLE_MEMORY, false);
+
+ iter = g_list_next(iter);
+ }
+}
+
static void __kill_process(int pid)
{
char err_str[MAX_LOCAL_BUFSZ] = { 0, };
candidate_process_context_t *cpc;
cpc = (candidate_process_context_t *)user_data;
- __update_slot_state(cpc, METHOD_TTL);
+ __update_slot_state(cpc, METHOD_TTL, false);
_D("Deactivate event: type(%d)", cpc->type);
return G_SOURCE_REMOVE;
__set_live_timer(cpt);
}
+ _memory_monitor_reset_timer();
+
return 0;
}
cpc->pid = CANDIDATE_NONE;
__dispose_candidate_process(cpc);
__set_timer(cpc);
+ __update_slot_score(cpc);
+
return pid;
}
return 0;
}
-static void __update_slot_state(candidate_process_context_t *cpc, int method)
+static void __deactivate_slot(candidate_process_context_t *cpc)
{
- if (method == METHOD_OUT_OF_MEMORY) {
- if ((cpc->deactivation_method & method) && __is_low_memory()) {
- cpc->state = CANDIDATE_PROCESS_STATE_PAUSED;
- __dispose_candidate_process(cpc);
- } else {
- cpc->state = CANDIDATE_PROCESS_STATE_RUNNING;
- if (!cpc->touched && !cpc->on_boot)
- return;
- if (!cpc->app_exists || cpc->pid > CANDIDATE_NONE)
- return;
- if (cpc->detection_method & METHOD_TIMEOUT)
- __set_timer(cpc);
- }
- } else if (cpc->activation_method & method) {
- __update_slot_state(cpc, METHOD_OUT_OF_MEMORY);
- } else if (cpc->deactivation_method & method) {
- cpc->state = CANDIDATE_PROCESS_STATE_PAUSED;
- __dispose_candidate_process(cpc);
- } else {
- cpc->state = CANDIDATE_PROCESS_STATE_RUNNING;
- if (!cpc->touched && !cpc->on_boot)
- return;
- if (!cpc->app_exists || cpc->pid > CANDIDATE_NONE)
- return;
- if (cpc->detection_method & METHOD_TIMEOUT)
- __set_timer(cpc);
+ if (cpc->state == CANDIDATE_PROCESS_STATE_PAUSED)
+ return;
+
+ cpc->state = CANDIDATE_PROCESS_STATE_PAUSED;
+ __dispose_candidate_process(cpc);
+}
+
+static void __activate_slot(candidate_process_context_t *cpc)
+{
+ if (cpc->state == CANDIDATE_PROCESS_STATE_RUNNING)
+ return;
+
+ cpc->state = CANDIDATE_PROCESS_STATE_RUNNING;
+ if (!cpc->touched && !cpc->on_boot)
+ return;
+
+ if (!cpc->app_exists || cpc->pid > CANDIDATE_NONE)
+ return;
+
+ if (cpc->detection_method & METHOD_TIMEOUT)
+ __set_timer(cpc);
+}
+
+static void __update_slot_state(candidate_process_context_t *cpc, int method,
+ bool force)
+{
+ switch (method) {
+ case METHOD_OUT_OF_MEMORY:
+ if ((force || cpc->deactivation_method & method) &&
+ __is_low_memory())
+ __deactivate_slot(cpc);
+ else
+ __activate_slot(cpc);
+ break;
+ case METHOD_TTL:
+ if (force || cpc->deactivation_method & method)
+ __deactivate_slot(cpc);
+ break;
+ case METHOD_AVAILABLE_MEMORY:
+ case METHOD_REQUEST:
+ __update_slot_state(cpc, METHOD_OUT_OF_MEMORY, force);
+ break;
+ default:
+ __activate_slot(cpc);
+ break;
}
}
org_cpc->timer = 0;
}
- __update_slot_state(org_cpc, METHOD_REQUEST);
+ __update_slot_state(org_cpc, METHOD_REQUEST, true);
__set_timer(org_cpc);
}
} else {
pid = __send_launchpad_loader(cpc, pkt, app_path, clifd);
}
+ _memory_monitor_reset_timer();
__send_result_to_caller(clifd, pid, app_path);
clifd = -1;
end:
cpc->ttl = ttl;
cpc->live_timer = 0;
cpc->is_hydra = is_hydra;
+ cpc->score = WIN_SCORE;
+ cpc->pss = 0;
if ((cpc->deactivation_method & METHOD_OUT_OF_MEMORY) &&
__is_low_memory())
static bool __is_low_memory(void)
{
+ if (_memory_monitor_is_low_memory())
+ return true;
+
if (__memory_status_low >= MEMORY_STATUS_LOW)
return true;
+
return false;
}
iter = candidate_slot_list;
while (iter) {
cpc = (candidate_process_context_t *)iter->data;
- __update_slot_state(cpc, METHOD_OUT_OF_MEMORY);
+ __update_slot_state(cpc, METHOD_OUT_OF_MEMORY, false);
iter = g_list_next(iter);
}
}
iter = candidate_slot_list;
while (iter) {
cpc = (candidate_process_context_t *)iter->data;
- __update_slot_state(cpc, METHOD_AVAILABLE_MEMORY);
+ __update_slot_state(cpc, METHOD_AVAILABLE_MEMORY, true);
iter = g_list_next(iter);
}
}
return 0;
}
+static int __memory_monitor_cb(bool low_memory, void *user_data)
+{
+ candidate_process_context_t *cpc;
+
+ cpc = __get_running_slot(false);
+ if (!cpc && low_memory)
+ return -1;
+
+ __update_slots_pss();
+
+ candidate_slot_list = g_list_sort(candidate_slot_list, __compare_slot);
+
+ _W("low memory(%s)", low_memory ? "true" : "false");
+ if (low_memory) {
+ do {
+ __pause_last_running_slot(false);
+
+ cpc = __get_running_slot(false);
+ if (!cpc)
+ break;
+ } while (__is_low_memory());
+ } else {
+ __resume_all_slots();
+ }
+
+ return 0;
+}
+
static int __before_loop(int argc, char **argv)
{
int ret;
__register_vconf_events();
__init_app_defined_loader_monitor();
+ _memory_monitor_init();
+ _memory_monitor_set_event_cb(__memory_monitor_cb, NULL);
return 0;
}
static void __after_loop(void)
{
+ _memory_monitor_fini();
__unregister_vconf_events();
if (__pid_table)
g_hash_table_destroy(__pid_table);
#define KEY_MEMORY_STATUS_NORMAL_KEY "NormalKey"
#define KEY_MEMORY_STATUS_NORMAL_VALUE "NormalValue"
+#define TAG_MEMORY_MONITOR "MemoryMonitor"
+#define KEY_MEMORY_MONITOR_THRESHOLD "Threshold"
+#define KEY_MEMORY_MONITOR_INTERVAL "Interval"
+
struct memory_status_s {
char *low_key;
int low_value;
int normal_value;
};
+struct memory_monitor_s {
+ int threshold;
+ int interval;
+};
+
static struct memory_status_s __memory_status;
+static struct memory_monitor_s __memory_monitor;
const char *_config_get_string_value(config_type_e type)
{
case CONFIG_TYPE_MEMORY_STATUS_NORMAL_VALUE:
value = __memory_status.normal_value;
break;
+ case CONFIG_TYPE_MEMORY_MONITOR_THRESHOLD:
+ value = __memory_monitor.threshold;
+ break;
+ case CONFIG_TYPE_MEMORY_MONITOR_INTERVAL:
+ value = __memory_monitor.interval;
+ break;
default:
_E("Unknown type");
value = INT_MIN;
return iniparser_getint(d, buf, INT_MIN);
}
-int _config_init(void)
+static void __memory_status_init(void)
{
- dictionary *d;
- char *val;
- const char *str;
- int ret;
-
- _D("config init");
-
__memory_status.low_key = strdup(VCONFKEY_SYSMAN_LOW_MEMORY);
__memory_status.low_value = VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING;
__memory_status.normal_key = strdup(VCONFKEY_SYSMAN_LOW_MEMORY);
__memory_status.normal_value = VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL;
+}
- ret = access(PATH_LAUNCHPAD_CONF, F_OK);
- if (ret != 0) {
- _W("The file doesn't exist. errno(%d)", errno);
- return 0;
- }
+static void __memory_status_fini(void)
+{
+ __memory_status.normal_value = 0;
+ free(__memory_status.normal_key);
+ __memory_status.low_value = 0;
+ free(__memory_status.low_key);
+}
- d = iniparser_load(PATH_LAUNCHPAD_CONF);
- if (!d) {
- _E("Failed to load the config file");
- return -1;
- }
+static void __memory_status_set(dictionary *d)
+{
+ const char *str;
+ char *val;
+ int ret;
str = __get_string_value(d, TAG_MEMORY_STATUS,
KEY_MEMORY_STATUS_LOW_KEY);
__memory_status.low_value,
__memory_status.normal_key,
__memory_status.normal_value);
+}
+
+static void __memory_monitor_init(void)
+{
+ __memory_monitor.threshold = 80; /* 80 % */
+ __memory_monitor.interval = 5000; /* 5 seconds */
+}
+
+static void __memory_monitor_fini(void)
+{
+ __memory_monitor.threshold = 0;
+ __memory_monitor.interval = 0;
+}
+
+static void __memory_monitor_set(dictionary *d)
+{
+ int ret;
+
+ ret = __get_int_value(d, TAG_MEMORY_MONITOR,
+ KEY_MEMORY_MONITOR_THRESHOLD);
+ if (ret != INT_MIN)
+ __memory_monitor.threshold = ret;
+
+ ret = __get_int_value(d, TAG_MEMORY_MONITOR,
+ KEY_MEMORY_MONITOR_INTERVAL);
+ if (ret != INT_MIN)
+ __memory_monitor.interval = ret;
+
+ _W("Memory Monitor Threshold(%d), Interval(%d)",
+ __memory_monitor.threshold,
+ __memory_monitor.interval);
+}
+
+int _config_init(void)
+{
+ dictionary *d;
+ int ret;
+
+ _D("config init");
+
+ __memory_status_init();
+ __memory_monitor_init();
+
+ ret = access(PATH_LAUNCHPAD_CONF, F_OK);
+ if (ret != 0) {
+ _W("The file doesn't exist. errno(%d)", errno);
+ return 0;
+ }
+
+ d = iniparser_load(PATH_LAUNCHPAD_CONF);
+ if (!d) {
+ _E("Failed to load the config file");
+ return -1;
+ }
+
+ __memory_status_set(d);
+ __memory_monitor_set(d);
iniparser_freedict(d);
{
_D("config fini");
- free(__memory_status.normal_key);
- free(__memory_status.low_key);
+ __memory_monitor_fini();
+ __memory_status_fini();
}
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include "launchpad_config.h"
+#include "launchpad_memory_monitor.h"
+#include "launchpad_proc.h"
+#include "log_private.h"
+
+#define INTERVAL_BASE_RATE 0.15f
+
+struct memory_monitor_s {
+ unsigned int threshold;
+ unsigned int prev_used_ratio;
+ unsigned int base_interval;
+ unsigned int interval;
+ guint tag;
+ memory_monitor_cb callback;
+ void *user_data;
+};
+
+static struct memory_monitor_s __monitor;
+
+static void __memory_monitor_start(void);
+
+static gboolean __memory_check_cb(gpointer data)
+{
+ unsigned int mem_used_ratio = 0;
+ int ret;
+
+ __monitor.tag = 0;
+ ret = _proc_get_mem_used_ratio(&mem_used_ratio);
+ if (ret != 0) {
+ _E("Failed to get mem used ratio");
+ __monitor.interval = __monitor.base_interval;
+ __memory_monitor_start();
+ return G_SOURCE_REMOVE;
+ }
+
+ if (__monitor.callback) {
+ ret = __monitor.callback(mem_used_ratio > __monitor.threshold,
+ __monitor.user_data);
+ if (ret == 0) {
+ _W("Reset interval");
+ __monitor.interval = __monitor.base_interval;
+ }
+ }
+
+ _W("previous used ratio(%u), current used ratio(%u)",
+ __monitor.prev_used_ratio, mem_used_ratio);
+
+ __monitor.prev_used_ratio = mem_used_ratio;
+ __memory_monitor_start();
+
+ return G_SOURCE_REMOVE;
+}
+
+static void __memory_monitor_stop(void)
+{
+ if (!__monitor.tag)
+ return;
+
+ g_source_remove(__monitor.tag);
+ __monitor.tag = 0;
+}
+
+static void __memory_monitor_start(void)
+{
+ if (__monitor.tag)
+ return;
+
+ __monitor.tag = g_timeout_add(__monitor.interval,
+ __memory_check_cb, NULL);
+
+ __monitor.interval += __monitor.interval * INTERVAL_BASE_RATE;
+}
+
+int _memory_monitor_reset_timer(void)
+{
+ _W("Reset");
+ __monitor.interval = __monitor.base_interval;
+
+ __memory_monitor_stop();
+ __memory_monitor_start();
+
+ return 0;
+}
+
+bool _memory_monitor_is_low_memory(void)
+{
+ unsigned int mem_used_ratio = 0;
+
+ _proc_get_mem_used_ratio(&mem_used_ratio);
+ __monitor.prev_used_ratio = mem_used_ratio;
+
+ if (mem_used_ratio > __monitor.threshold)
+ return true;
+
+ return false;
+}
+
+int _memory_monitor_set_event_cb(memory_monitor_cb callback, void *user_data)
+{
+ __monitor.callback = callback;
+ __monitor.user_data = user_data;
+
+ return 0;
+}
+
+int _memory_monitor_init(void)
+{
+ int ret;
+
+ _W("MEMORY_MONITOR_INIT");
+
+ __monitor.threshold = _config_get_int_value(
+ CONFIG_TYPE_MEMORY_MONITOR_THRESHOLD);
+ __monitor.base_interval = _config_get_int_value(
+ CONFIG_TYPE_MEMORY_MONITOR_INTERVAL);
+ __monitor.interval = __monitor.base_interval;
+
+ ret = _proc_get_mem_used_ratio(&__monitor.prev_used_ratio);
+ if (ret != 0) {
+ _E("Failed to get mem used ratio. error(%d)", ret);
+ return ret;
+ }
+
+ __memory_monitor_start();
+
+ return 0;
+}
+
+void _memory_monitor_fini(void)
+{
+ _W("MEMORY_MONITOR_FINI");
+
+ __memory_monitor_stop();
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <limits.h>
+#include <linux/limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "launchpad_proc.h"
+#include "log_private.h"
+
+#define auto_ptr(x) __attribute__ ((__cleanup__(x)))
+
+static unsigned int __convert_string(const char *str)
+{
+ while (*str < '0' || *str > '9')
+ str++;
+
+ return atoi(str);
+}
+
+static int __open_file(const char *file, FILE **fp)
+{
+ int ret;
+
+ *fp = fopen(file, "r");
+ if (!*fp) {
+ ret = -errno;
+ _E("Failed to open %s. errno(%d)", file, errno);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __close_file(FILE **fp)
+{
+ if (!fp || !*fp)
+ return;
+
+ fclose(*fp);
+}
+
+int _proc_get_mem_used_ratio(unsigned int *mem_used_ratio)
+{
+ auto_ptr(__close_file) FILE *fp = NULL;
+ char buf[LINE_MAX];
+ char *str;
+ unsigned int mem_free = 0;
+ unsigned int mem_total = 0;
+ unsigned int mem_available = 0;
+ unsigned int cached = 0;
+ unsigned int used;
+ unsigned int used_ratio;
+ int ret;
+
+ if (!mem_used_ratio) {
+ _E("Invalid parameter");
+ return -EINVAL;
+ }
+
+ ret = __open_file("/proc/meminfo", &fp);
+ if (ret != 0)
+ return ret;
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if ((str = strstr(buf, "MemTotal:"))) {
+ str += strlen("MemTotal:");
+ mem_total = __convert_string(str);
+ } else if ((str = strstr(buf, "MemFree:"))) {
+ str += strlen("MemFree:");
+ mem_free = __convert_string(str);
+ } else if ((str = strstr(buf, "MemAvailable:"))) {
+ str += strlen("MemAvailable:");
+ mem_available = __convert_string(str);
+ } else if ((str = strstr(buf, "Cached:")) &&
+ !strstr(buf, "Swap")) {
+ str += strlen("Cached:");
+ cached = atoi(str);
+ break;
+ }
+ }
+
+ if (mem_total == 0) {
+ _E("Failed to get total memory size");
+ return -1;
+ }
+
+ if (mem_available == 0)
+ mem_available = mem_free + cached;
+
+ used = mem_total - mem_available;
+ used_ratio = used * 100 / mem_total;
+ _I("memory used ratio: %u %%", used_ratio);
+
+ *mem_used_ratio = used_ratio;
+
+ return 0;
+}
+
+int _proc_get_mem_pss(int pid, unsigned int *mem_pss)
+{
+ auto_ptr(__close_file) FILE *fp = NULL;
+ char path[PATH_MAX];
+ char buf[LINE_MAX];
+ unsigned int pss = 0;
+ unsigned int total_pss = 0;
+ int ret;
+
+ if (pid < 1 || !mem_pss) {
+ _E("Invalid parameter");
+ return -EINVAL;
+ }
+
+ snprintf(path, sizeof(path), "/proc/%d/smaps", pid);
+ ret = __open_file(path, &fp);
+ if (ret != 0)
+ return ret;
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if (sscanf(buf, "Pss: %d kB", &pss) == 1) {
+ total_pss += pss;
+ pss = 0;
+ }
+ }
+
+ *mem_pss = total_pss;
+ _I("[%d] PSS: %u kB", pid, total_pss);
+
+ return 0;
+}