#include "proc-common.h"
#include "lowmem-common.h"
#include "module.h"
+#include "file-helper.h"
+#include "swap-common.h"
+#include "smaps.h"
#define PAGE_SIZE_KB 4
return RESOURCED_ERROR_NONE;
}
-int proc_get_mem_usage(pid_t pid, unsigned int *vmsize, unsigned int *vmrss)
+int proc_get_mem_usage(pid_t pid, unsigned int *vmswap, unsigned int *vmrss)
{
- char buf[PROC_BUF_MAX];
- char statm_buf[PROC_NAME_MAX];
- unsigned int size, rss;
- FILE *fp;
+ char filename[PROC_BUF_MAX];
+ _cleanup_fclose_ FILE *fp = NULL;
+ unsigned int swap = 0, rss = 0;
+ sprintf(filename, "/proc/%d/status", pid);
+ fp = fopen(filename, "r");
+ if (!fp)
+ return RESOURCED_ERROR_FAIL;
- snprintf(buf, sizeof(buf), "/proc/%d/statm", pid);
- fp = fopen(buf, "r");
- if (fp == NULL)
+ if (vmrss != NULL) {
+ while (fgets(filename, sizeof(filename), fp)) {
+ /* Skip the lines, until first match */
+ if (!strstart_with(filename, "VmRSS:"))
+ continue;
+
+ /* Read RSS value and end this loop. */
+ if (sscanf(filename, "VmRSS: %d kB", &rss) == 1)
+ break;
+
+ return RESOURCED_ERROR_NO_DATA;
+ }
+ *vmrss = rss;
+ }
+
+ if (vmswap != NULL) {
+ /* Interate over rest of Vm* values */
+ while (fgets(filename, sizeof(filename), fp)) {
+ /* Read VmSwap and return with positive result */
+ if (sscanf(filename, "VmSwap: %d kB", &swap) == 1)
+ break;
+
+ /* End of file before VmSwap read, return with error */
+ if (feof(fp))
+ return RESOURCED_ERROR_NO_DATA;
+ }
+ *vmswap = swap;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int proc_get_pss(pid_t pid, unsigned int *pss)
+{
+ _cleanup_smaps_free_ struct smaps *smaps = NULL;
+ int ret;
+
+ *pss = 0;
+
+ /*
+ * PSS memory usage is number of KB accounted to a process
+ * where shared libraries are included.
+ * All libraries that are shared are accounted by formula
+ * "library size"/"number of processes that are using it"
+ *
+ * PSS provide more real live memory usage of non swapped process.
+ */
+ ret = smaps_get(pid, &smaps, SMAPS_MASK_PSS);
+
+ if (ret < 0) {
+ _E("Error while reading smaps or totmaps for pid %d", pid);
return RESOURCED_ERROR_FAIL;
+ }
- if (fgets(statm_buf, PROC_NAME_MAX-1, fp) == NULL) {
- fclose(fp);
+ *pss += smaps->sum[SMAPS_ID_PSS];
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int proc_get_uss(pid_t pid, unsigned int *uss)
+{
+ _cleanup_smaps_free_ struct smaps *smaps = NULL;
+ int ret;
+
+ *uss = 0;
+
+ /*
+ * USS memory usage is number of KB accounted to a process
+ * where only private (non-shared) data are accounted.
+ *
+ * USS provides the value how much memory will be freed after
+ * termination of process.
+ */
+ ret = smaps_get(pid, &smaps,
+ (SMAPS_MASK_PRIVATE_CLEAN | SMAPS_MASK_PRIVATE_DIRTY));
+
+ if (ret < 0) {
+ _E("Error while reading smaps or totmaps for pid %d", pid);
return RESOURCED_ERROR_FAIL;
}
- fclose(fp);
- if (sscanf(statm_buf, "%u %u", &size, &rss) < 2)
+ *uss += smaps->sum[SMAPS_ID_PRIVATE_CLEAN];
+ *uss += smaps->sum[SMAPS_ID_PRIVATE_DIRTY];
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int proc_get_zram_usage(pid_t pid, unsigned int *usage)
+{
+ int ret;
+ struct meminfo mi;
+ static unsigned int swap_total = 0;
+ unsigned int proc_swap_usage, zram_usage;
+
+ /* Read total swap size just once and cache it */
+ if (!swap_total) {
+ ret = proc_get_meminfo(&mi, MEMINFO_MASK_SWAP_TOTAL);
+ if (ret < 0) {
+ _E("Failed to get %s: %s",
+ meminfo_id_to_string(MEMINFO_ID_SWAP_TOTAL),
+ strerror(-ret));
+ return RESOURCED_ERROR_FAIL;
+ }
+ swap_total = mi.value[MEMINFO_ID_SWAP_TOTAL];
+ }
+
+ /* Read usage of Swap (VmSwap) of interested process */
+ ret = proc_get_mem_usage(pid, &proc_swap_usage, NULL);
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+
+ /* Read current total memory usage of zram device */
+ ret = fread_uint(SWAP_ZRAM_SYSFILE"mem_used_total", &zram_usage);
+ if (ret < 0)
return RESOURCED_ERROR_FAIL;
- if (vmsize != NULL)
- *vmsize = size*PAGE_SIZE_KB;
- if (vmrss != NULL)
- *vmrss = rss*PAGE_SIZE_KB;
+ /*
+ * Calculate aproximated value of zram usage for selected process
+ * by formula: proc_zram_usage = ( VmSwap x ZramMemoryUsage )/SwapTotal
+ */
+ *usage = ((float)((proc_swap_usage*BYTE_TO_KBYTE(zram_usage))/swap_total));
+
+ return RESOURCED_ERROR_NONE;
+}
+int proc_get_procstat_mem_usage(pid_t pid, unsigned int *usage)
+{
+ int ret;
+ unsigned int pss, swap;
+
+ ret = proc_get_pss(pid, &pss);
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+
+ ret = proc_get_mem_usage(pid, &swap, NULL);
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+
+ /*
+ * For user information it's better to present PSS value
+ * as it account libraries as they are shared between processes.
+ *
+ * If process is being swapped (moved to zram) then it's PSS goes
+ * to very low value, then it's needed to account VmSwap usage
+ * so there wouldn't be any strange information about process memory usage.
+ */
+ *usage = pss + swap;
return RESOURCED_ERROR_NONE;
}
unsigned int proc_get_mem_available(void)
{
struct meminfo mi;
- int r;
+ int ret;
char buf[256];
- r = proc_get_meminfo(&mi, MEMINFO_MASK_MEM_AVAILABLE);
- if (r < 0) {
+ ret = proc_get_meminfo(&mi, MEMINFO_MASK_MEM_AVAILABLE);
+ if (ret < 0) {
_E("Failed to get %s: %s",
meminfo_id_to_string(MEMINFO_ID_MEM_AVAILABLE),
- strerror_r(-r, buf, sizeof(buf)));
+ strerror_r(-ret, buf, sizeof(buf)));
return 0;
}
unsigned int proc_get_swap_free(void)
{
struct meminfo mi;
- int r;
+ int ret;
char error_buf[256];
- r = proc_get_meminfo(&mi, MEMINFO_MASK_SWAP_FREE);
- if (r < 0) {
+ ret = proc_get_meminfo(&mi, MEMINFO_MASK_SWAP_FREE);
+ if (ret < 0) {
_E("Failed to get %s: %s",
meminfo_id_to_string(MEMINFO_ID_SWAP_FREE),
- strerror_r(-r, error_buf, sizeof(error_buf)));
+ strerror_r(-ret, error_buf, sizeof(error_buf)));
return 0;
}
return cpu;
}
-int proc_get_uptime(unsigned long *uptime)
-{
- _cleanup_fclose_ FILE *fp = NULL;
- double stime;
-
- fp = fopen("/proc/uptime", "r");
- if (fp == NULL)
- return RESOURCED_ERROR_FAIL;
-
- if (fscanf(fp, "%lf %*s", &stime) < 0)
- return RESOURCED_ERROR_FAIL;
-
- *uptime = (unsigned long)stime;
- return RESOURCED_ERROR_NONE;
-}
-
int proc_get_exepath(pid_t pid, char *buf, int len)
{
char path[PROC_BUF_MAX];
meminfo_id_to_string(i));
}
- return 0;
+ return RESOURCED_ERROR_NONE;
+}
+
+int proc_get_uptime(unsigned long *uptime)
+{
+ _cleanup_fclose_ FILE *fp = NULL;
+ double stime;
+
+ fp = fopen("/proc/uptime", "r");
+ if (fp == NULL)
+ return RESOURCED_ERROR_FAIL;
+
+ if (fscanf(fp, "%lf %*s", &stime) < 0)
+ return RESOURCED_ERROR_FAIL;
+
+ *uptime = (unsigned long)stime;
+ return RESOURCED_ERROR_NONE;
}
int proc_get_label(pid_t pid, char *label);
/**
- * @desc get VmSize and VmRSS from /proc/{pid}/statm file.
+ * @desc get VmSwap and VmRSS from /proc/{pid}/status file.
* @return negative value if error or pid doesn't exist
*/
-int proc_get_mem_usage(pid_t pid, unsigned int *vmsize, unsigned int *vmrss);
+int proc_get_mem_usage(pid_t pid, unsigned int *vmswap, unsigned int *vmrss);
+
+/**
+ * @desc get PSS memory usage from /proc/{pid}/smaps file.
+ * @return negative value if error or pid doesn't exist
+ */
+int proc_get_pss(pid_t pid, unsigned int *pss);
+
+/**
+ * @desc get USS memory usage from /proc/{pid}/smaps file.
+ * @return negative value if error or pid doesn't exist
+ */
+int proc_get_uss(pid_t pid, unsigned int *uss);
+
+/**
+ * @desc get aproximated usage of Zram for pid
+ * @return negative value if error or pid doesn't exist
+ */
+int proc_get_zram_usage(pid_t pid, unsigned int *usage);
+
+/**
+ * @desc get memory usage for proc-stat: PSS+VmSwap
+ * @return negative value if error or pid doesn't exist
+ */
+int proc_get_procstat_mem_usage(pid_t pid, unsigned int *usage);
/**
* @desc get MemAvaliable from /proc/meminfo or calcuate it by MemFree+Cached
*/
int proc_get_uptime(unsigned long *uptime);
+
#endif /*__PROCFS_H__*/
#include "util.h"
#include "smaps.h"
+#include "trace.h"
+
+static int have_totmaps = 0;
+
+static bool totmaps_available(void)
+{
+ if (have_totmaps == 2) {
+ have_totmaps = (access("/proc/1/totmaps", R_OK) == 0);
+ }
+
+ return have_totmaps;
+}
static const char* const smaps_string_lookup[SMAPS_ID_MAX] = {
[SMAPS_ID_ANON_HUGE_PAGES] = "AnonHugePages",
_cleanup_fclose_ FILE *f = NULL;
struct smaps *m = NULL;
char buf[LINE_MAX];
- bool get_line = true;
+ bool get_line = true, totmaps;
int r;
assert(maps);
- r = asprintf(&path, "/proc/%d/smaps", pid);
+ totmaps = totmaps_available();
+ if (totmaps && (mask & ~SMAPS_MASK_TOTMAPS) == 0) {
+ r = asprintf(&path, "/proc/%d/totmaps", pid);
+ } else {
+ totmaps = false;
+ r = asprintf(&path, "/proc/%d/smaps", pid);
+ }
+
if (r < 0)
return -ENOMEM;
for (;;) {
struct smap *map = NULL;
int n;
+ size_t l;
+ enum smap_id id;
if (get_line && !fgets(buf, sizeof(buf), f)) {
if (ferror(f)) {
goto on_error;
}
break;
- } else
- get_line = true;
+ }
+ get_line = true;
+
+ /*
+ * If totmaps are available and the caller doesn't
+ * need fields exclusive to smaps, we use them here.
+ * Reading totmaps is on average 20 times faster than
+ * reading smaps.
+ */
+ if (totmaps) {
+ unsigned int v = 0;
+
+ l = strcspn(buf, ":");
+ if (l >= sizeof(buf) || !buf[l])
+ goto totmaps_error;
+ buf[l] = 0;
+
+ id = smap_string_to_id(buf);
+ if (id < 0 || id >= SMAPS_ID_MAX)
+ continue;
+
+ if (sscanf(buf + l + 1, "%d kB", &v) != 1)
+ goto totmaps_error;
+
+ m->sum[id] = v;
+ continue;
+
+totmaps_error:
+ _E("Unknown totmaps format, expected: '%%s : %%d kB'");
+ r = -EIO;
+ have_totmaps = 0; // don't ever read totmaps again
+ goto on_error;
+ }
map = new0(struct smap, 1);
if (!map) {
for (;;) {
_cleanup_free_ char *k = NULL;
unsigned int v = 0;
- enum smap_id id;
- size_t l;
if (!fgets(buf, sizeof(buf), f)) {
if (ferror(f)) {
SMAPS_MASK_SIZE = 1 << SMAPS_ID_SIZE,
SMAPS_MASK_SWAP = 1 << SMAPS_ID_SWAP,
SMAPS_MASK_ALL = (1 << SMAPS_ID_MAX) - 1,
+ SMAPS_MASK_TOTMAPS = SMAPS_MASK_RSS | SMAPS_MASK_PSS
+ | SMAPS_MASK_SHARED_CLEAN | SMAPS_MASK_SHARED_DIRTY
+ | SMAPS_MASK_PRIVATE_CLEAN | SMAPS_MASK_PRIVATE_DIRTY
+ | SMAPS_MASK_REFERENCED | SMAPS_MASK_ANONYMOUS
+ | SMAPS_MASK_ANON_HUGE_PAGES | SMAPS_MASK_SWAP
};
#define SMAPS_MASK_DEFAULT \
#include "memory-common.h"
+#define SWAP_ZRAM_SYSFILE "/sys/block/zram0/"
+
enum swap_state {
SWAP_ARG_START = -1,
SWAP_OFF,
#define LOWMEM_NO_LIMIT 0
#define LOWMEM_THRES_INIT 0
-/* Experimently, RSS is 3 times larger than the actual allocated memory. */
-#define LOWMEM_RSS_RATIO 0.3
-
#define MEMCG_MOVE_CHARGE_PATH "memory.move_charge_at_immigrate"
#define MEMCG_OOM_CONTROL_PATH "memory.oom_control"
#define MEMCG_LIMIT_PATH "memory.limit_in_bytes"
}
}
-#ifdef HEART_SUPPORT
-static int lowmem_get_proc_mem_uss(pid_t pid, unsigned int *uss)
-{
- struct proc_app_info *pai = NULL;
- unsigned int tpss = 0, tuss = 0;
- int ret;
-
- pai = find_app_info(pid);
- if (!pai)
- goto error;
-
- ret = heart_memory_get_latest_data(pai->appid, &tpss, &tuss);
- if (ret == RESOURCED_ERROR_FAIL)
- goto error;
- *uss = tuss;
- _D("success get uss = %u for %s from data crud", tuss, pai->appid);
- return RESOURCED_ERROR_NONE;
-
-error:
- *uss = 0;
- return RESOURCED_ERROR_FAIL;
-}
-#endif
-
static int get_proc_mem_usage(pid_t pid, unsigned int *usage)
{
+ unsigned int uss, zram = 0;
int ret;
-#ifdef HEART_SUPPORT
- static int logging_memory_avaliable = 10;
- if (logging_memory_avaliable > 0) {
- ret = lowmem_get_proc_mem_uss(pid, usage);
- if (ret == RESOURCED_ERROR_NONE && *usage > 0)
- return ret;
- /*
- * Calls to logging_memory_get_latest_data are expensive and
- * often. If we can't get the values, because most probably memory
- * module is disabled. Let's use the only available alternative.
- * We try 10 times, before we acknowledge that the module is not
- * available.
- */
- logging_memory_avaliable--;
- }
-#endif
+ *usage = 0;
/*
- * We fallback to getting RSS value if we can't get USS.
+ * In lowmem we need to know memory size of processes to
+ * for terminating apps. To get most real value of usage
+ * we should use USS + ZRAM usage for selected process.
+ *
+ * Those values will contain the most approximated amount
+ * of memory that will be freed after process termination.
*/
- ret = proc_get_mem_usage(pid, NULL, usage);
- if (ret == RESOURCED_ERROR_NONE)
+ ret = proc_get_uss(pid, &uss);
+ if (ret != RESOURCED_ERROR_NONE)
return ret;
- return RESOURCED_ERROR_FAIL;
+ if (swap_get_state() == SWAP_ON) {
+ ret = proc_get_zram_usage(pid, &zram);
+ /* If we don't get zram usage, it's not a problem */
+ if (ret != RESOURCED_ERROR_NONE)
+ zram = 0;
+ }
+ *usage = uss + zram;
+ return RESOURCED_ERROR_NONE;
}
static int lowmem_check_current_state(struct memcg_info *mi)
return;
}
- total += *total_size + ((float)tsk->size * LOWMEM_RSS_RATIO);
+ ret = get_proc_mem_usage(tsk->pid, &total);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to get memory usage : %d", ret);
+ return;
+ }
+ total += *total_size;
resourced_proc_status_change(PROC_CGROUP_SET_TERMINATE_REQUEST,
pid, NULL, NULL, PROC_TYPE_NONE);
pai->lru_state, pai->proc_exclude, pai->runtime_exclude,
pai->flags, pai->state);
- proc_get_mem_usage(pai->main_pid, NULL, &size);
+ if (proc_get_procstat_mem_usage(pai->main_pid, &size))
+ size = 0;
proc_get_cpu_time(pai->main_pid, &utime, &stime, &starttime);
LOG_DUMP(fp, "\t main pid : %d, oom score : %d, memory rss : %d KB,"
"utime : %lu, stime : %lu, starttime : %lu\n", pai->main_pid, oom_score_adj, size,
utime, stime, starttime);
if (pai->childs) {
gslist_for_each_item(iter_child, pai->childs) {
- proc_get_mem_usage(GPOINTER_TO_PID(iter_child->data), NULL, &size);
+ if (proc_get_procstat_mem_usage(GPOINTER_TO_PID(iter_child->data), &size))
+ size = 0;
proc_get_cpu_time(GPOINTER_TO_PID(iter_child->data),
&utime, &stime, &starttime);
LOG_DUMP(fp, "\t main pid : %d, oom score : %d, "
DBusMessageIter iter;
DBusMessage *reply;
char *appid;
- unsigned int rss;
+ unsigned int usage = 0;
struct proc_app_info *pai = NULL;
ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &appid,
return reply;
}
- if (proc_get_mem_usage(pai->main_pid, NULL, &rss) < 0) {
+ if (proc_get_procstat_mem_usage(pai->main_pid, &usage) < 0) {
_E("lowmem_get_proc_mem_usage failed for appid = %s (%d)",
appid, pai->main_pid);
reply = dbus_message_new_method_return(msg);
return reply;
}
- _D("memory usage of %s (%d), rss = %u", appid, pai->main_pid, rss);
+ _D("memory usage of %s (%d), rss = %u", appid, pai->main_pid, usage);
reply = dbus_message_new_method_return(msg);
dbus_message_iter_init_append(reply, &iter);
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &rss);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &usage);
return reply;
}
GSList *giter;
char *appid;
struct proc_app_info *pai;
- unsigned int total = 0, rss;
+ unsigned int total = 0, usage = 0;
reply = dbus_message_new_method_return(msg);
gslist_for_each_item(giter, proc_app_list) {
pai = (struct proc_app_info *)giter->data;
if (!pai->main_pid)
continue;
- if (proc_get_mem_usage(pai->main_pid, NULL, &rss) < 0)
+ if (proc_get_procstat_mem_usage(pai->main_pid, &usage) < 0)
continue;
- total += rss;
+ total += usage;
}
dbus_message_iter_init_append(reply, &iter);
pai = (struct proc_app_info *)giter->data;
if (!pai || !pai->main_pid)
continue;
- if (proc_get_mem_usage(pai->main_pid, NULL, &rss) < 0)
+ if (proc_get_procstat_mem_usage(pai->main_pid, &usage) < 0)
continue;
appid = pai->appid;
dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appid);
- dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &rss);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &usage);
dbus_message_iter_close_container(&arr, &sub);
}
dbus_message_iter_close_container(&iter, &arr);
char *appid;
int type, ret;
struct proc_app_info *pai;
- unsigned int rss;
+ unsigned int usage = 0;
ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &type,
DBUS_TYPE_INVALID);
continue;
if (type != PROC_TYPE_MAX && pai->type != type)
continue;
- if (proc_get_mem_usage(pai->main_pid, NULL, &rss) < 0)
+ if (proc_get_procstat_mem_usage(pai->main_pid, &usage) < 0)
continue;
appid = pai->appid;
dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appid);
- dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &rss);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &usage);
dbus_message_iter_close_container(&arr, &sub);
}
dbus_message_iter_close_container(&iter, &arr);
#define SWAP_BACKEND "zram"
#define SWAP_ZRAM_NUM_DEVICE "1"
#define SWAP_ZRAM_DEVICE "/dev/zram0"
-#define SWAP_ZRAM_SYSFILE "/sys/block/zram0/"
#define SWAP_ZRAM_DISK_SIZE SWAP_ZRAM_SYSFILE"disksize"
#define SWAP_ZRAM_MAX_COMP_STREAMS SWAP_ZRAM_SYSFILE"max_comp_streams"
#define SWAP_ZRAM_COMP_ALGORITHM SWAP_ZRAM_SYSFILE"comp_algorithm"