4 * Copyright (c) 2015 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.
23 * @desc communicate with procfs in resourced
25 * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
37 #include <sys/types.h>
42 #include "resourced.h"
47 #include "proc-common.h"
48 #include "memory-cgroup.h"
50 #include "file-helper.h"
51 #include "swap-common.h"
54 #include "lowmem-handler.h"
56 #define MEM_SWAP_RATIO 0.5
58 static struct sys_node_table sys_node_tables[] = {
59 { SYS_VM_SHRINK_MEMORY, "/proc/sys/vm/shrink_memory", 1, 1 },
60 { SYS_VM_COMPACT_MEMORY, "/proc/sys/vm/compact_memory", 1, 1 },
64 int proc_get_cmdline(pid_t pid, char *cmdline, size_t maxcmdline)
67 assert(maxcmdline > 0);
69 char buf[PROC_BUF_MAX];
70 char cmdline_buf[PROC_NAME_MAX];
75 snprintf(buf, sizeof(buf), "/proc/cmdline");
77 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
81 return RESOURCED_ERROR_FAIL;
83 if (fgets(cmdline_buf, sizeof cmdline_buf, fp) == NULL) {
85 return RESOURCED_ERROR_FAIL;
89 filename = strrchr(cmdline_buf, '/');
91 filename = cmdline_buf;
93 filename = filename + 1;
95 strncpy(cmdline, filename, maxcmdline - 1);
96 cmdline[maxcmdline - 1] = '\0';
98 return RESOURCED_ERROR_NONE;
101 pid_t find_pid_from_cmdline(char *cmdline)
103 pid_t pid = -1, foundpid = -1;
106 struct dirent *dentry;
107 char appname[PROC_NAME_MAX];
109 dp = opendir("/proc");
111 _E("BACKGRD MANAGE : fail to open /proc");
112 return RESOURCED_ERROR_FAIL;
114 while ((dentry = readdir(dp))) {
115 if (!isdigit(dentry->d_name[0]))
118 pid = atoi(dentry->d_name);
121 ret = proc_get_cmdline(pid, appname, sizeof appname);
122 if (ret == RESOURCED_ERROR_NONE) {
123 if (!strncmp(cmdline, appname, strlen(appname)+1)) {
133 int proc_get_oom_score_adj(int pid, int *oom_score_adj)
135 char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
139 return RESOURCED_ERROR_FAIL;
141 snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
142 fp = fopen(buf, "r");
145 _E("fopen %s failed", buf);
146 return RESOURCED_ERROR_FAIL;
148 if (fgets(buf, sizeof(buf), fp) == NULL) {
150 return RESOURCED_ERROR_FAIL;
152 (*oom_score_adj) = atoi(buf);
154 return RESOURCED_ERROR_NONE;
157 int proc_set_oom_score_adj(int pid, int oom_score_adj, struct proc_app_info *pai)
160 struct lowmem_control_data lowmem_data;
161 char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
163 /* Don't touch OOM-fixed process' score */
164 if (pai && pai->app_memcg_update_exclude)
165 return RESOURCED_ERROR_NONE;
167 snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
168 fp = fopen(buf, "r+");
170 return RESOURCED_ERROR_FAIL;
171 if (fgets(buf, sizeof(buf), fp) == NULL) {
173 return RESOURCED_ERROR_FAIL;
175 fprintf(fp, "%d", oom_score_adj);
178 if (oom_score_adj >= OOMADJ_SU) {
179 lowmem_data.control_type = LOWMEM_MOVE_CGROUP;
180 lowmem_data.pid = pid;
181 lowmem_data.oom_score_adj = oom_score_adj;
182 lowmem_data.pai = pai;
183 resourced_notify(RESOURCED_NOTIFIER_MEM_CONTROL, &lowmem_data);
186 return RESOURCED_ERROR_NONE;
189 int proc_get_label(pid_t pid, char *label)
191 char buf[PROC_BUF_MAX];
194 snprintf(buf, sizeof(buf), "/proc/%d/attr/current", pid);
195 fp = fopen(buf, "r");
197 return RESOURCED_ERROR_FAIL;
199 if (fgets(label, PROC_NAME_MAX-1, fp) == NULL) {
201 return RESOURCED_ERROR_FAIL;
204 return RESOURCED_ERROR_NONE;
207 int proc_get_mem_status(pid_t pid, unsigned int *vmswap_kb, unsigned int *vmrss_kb)
209 char filename[PROC_BUF_MAX];
210 _cleanup_fclose_ FILE *fp = NULL;
211 unsigned int swap_kb = 0, rss_kb = 0;
213 snprintf(filename, PROC_BUF_MAX, "/proc/%d/status", pid);
214 fp = fopen(filename, "r");
216 return RESOURCED_ERROR_FAIL;
218 if (vmrss_kb != NULL) {
219 while (fgets(filename, sizeof(filename), fp)) {
220 /* Skip the lines, until first match */
221 if (!strstart_with(filename, "VmRSS:"))
224 /* Read RSS value and end this loop. */
225 if (sscanf(filename, "VmRSS: %u kB", &rss_kb) == 1)
228 return RESOURCED_ERROR_NO_DATA;
233 if (vmswap_kb != NULL) {
234 /* Interate over rest of Vm* values */
235 while (fgets(filename, sizeof(filename), fp)) {
236 /* Read VmSwap and return with positive result */
237 if (sscanf(filename, "VmSwap: %d kB", &swap_kb) == 1)
240 /* End of file before VmSwap read, return with error */
242 return RESOURCED_ERROR_NO_DATA;
244 *vmswap_kb = swap_kb;
247 return RESOURCED_ERROR_NONE;
250 int proc_get_uss(pid_t pid, unsigned int *uss)
252 _cleanup_smaps_free_ struct smaps *smaps = NULL;
258 * USS memory usage is number of KB accounted to a process
259 * where only private (non-shared) data are accounted.
261 * USS provides the value how much memory will be freed after
262 * termination of process.
264 ret = smaps_get(pid, &smaps,
265 (SMAPS_MASK_PRIVATE_CLEAN | SMAPS_MASK_PRIVATE_DIRTY));
268 _E("Error while reading smaps or totmaps for pid %d", pid);
269 return RESOURCED_ERROR_FAIL;
272 *uss += smaps->sum[SMAPS_ID_PRIVATE_CLEAN];
273 *uss += smaps->sum[SMAPS_ID_PRIVATE_DIRTY];
275 return RESOURCED_ERROR_NONE;
278 int proc_get_zram_usage(pid_t pid, unsigned int *usage_kb)
282 static unsigned int swap_total_kb = 0;
283 unsigned int proc_swap_usage_kb;
284 unsigned long long zram_usage_bytes;
286 /* Read total swap size just once and cache it */
287 if (!swap_total_kb) {
288 ret = proc_get_meminfo(&mi, MEMINFO_MASK_SWAP_TOTAL);
290 _E("Failed to get %s: %m",
291 meminfo_id_to_string(MEMINFO_ID_SWAP_TOTAL));
292 return RESOURCED_ERROR_FAIL;
294 swap_total_kb = mi.value[MEMINFO_ID_SWAP_TOTAL];
297 /* Read usage of Swap (VmSwap) of interested process */
298 ret = proc_get_mem_status(pid, &proc_swap_usage_kb, NULL);
299 if (ret != RESOURCED_ERROR_NONE)
302 /* Read current total memory usage of zram device */
303 ret = fread_nth_ulonglong(SWAP_ZRAM_SYSFILE"mm_stat", 2, &zram_usage_bytes);
304 if (ret == -ENOENT) {
305 ret = fread_ulonglong(SWAP_ZRAM_SYSFILE"mem_used_total", &zram_usage_bytes);
309 return RESOURCED_ERROR_FAIL;
312 * Calculate aproximated value of zram usage for selected process
313 * by formula: proc_zram_usage = ( VmSwap x ZramMemoryUsage )/SwapTotal
315 *usage_kb = (int)((float)proc_swap_usage_kb * BYTE_TO_KBYTE(zram_usage_bytes) / swap_total_kb);
317 return RESOURCED_ERROR_NONE;
320 int proc_get_approx_mem_usage(pid_t pid, unsigned int *usage_kb)
323 unsigned long resident_pages = 0, shared_pages = 0;
324 char filename[PROC_BUF_MAX];
325 _cleanup_fclose_ FILE *fp = NULL;
327 snprintf(filename, PROC_BUF_MAX, "/proc/%d/statm", pid);
328 fp = fopen(filename, "r");
330 return RESOURCED_ERROR_FAIL;
333 * For quick read, open code by putting numbers directly
335 * seq_printf(m, "%lu %lu %lu %lu 0 %lu 0\n",
336 * size, resident, shared, text, data);
338 ret = fscanf(fp, "%*s %lu %lu %*s %*s %*s %*s\n", &resident_pages, &shared_pages);
340 return RESOURCED_ERROR_FAIL;
343 * The value resident - shared is mostly similar to Uss.
345 *usage_kb = BYTE_TO_KBYTE((unsigned long long)(resident_pages - shared_pages) << PAGE_SHIFT);
346 return RESOURCED_ERROR_NONE;
350 * @desc get total memory usage from VmSwap and VmRSS.
351 * @return negative value if error or pid doesn't exist
353 int proc_get_mem_usage(pid_t pid, unsigned int *usage)
356 unsigned int vmswap = 0, total = 0;
358 ret = proc_get_approx_mem_usage(pid, &total);
360 _E("Failed to get usage : %d", pid);
364 if (swap_get_state() == SWAP_ON) {
365 ret = proc_get_mem_status(pid, &vmswap, NULL);
366 if (ret != RESOURCED_ERROR_NONE)
373 return RESOURCED_ERROR_NONE;
377 * @desc get how much ram is used in each application
378 * @return negative value if error or pid doesn't exist
380 int proc_get_ram_usage(pid_t pid, unsigned int *usage_kb)
383 unsigned int vmswap_kb = 0, total_kb = 0;
385 ret = proc_get_approx_mem_usage(pid, &total_kb);
387 _E("Failed to get usage : %d", pid);
391 if (swap_get_state() == SWAP_ON) {
392 ret = proc_get_mem_status(pid, &vmswap_kb, NULL);
393 if (ret != RESOURCED_ERROR_NONE)
397 * If it is necessary to know real ram size about each application,
398 * it should consider compression ratio.
400 vmswap_kb *= MEM_SWAP_RATIO;
401 total_kb += vmswap_kb;
404 *usage_kb = total_kb;
405 return RESOURCED_ERROR_NONE;
408 unsigned int proc_get_mem_available(void)
413 ret = proc_get_meminfo(&mi, MEMINFO_MASK_MEM_AVAILABLE);
415 _E("Failed to get %s: %m",
416 meminfo_id_to_string(MEMINFO_ID_MEM_AVAILABLE));
420 return KBYTE_TO_MBYTE(mi.value[MEMINFO_ID_MEM_AVAILABLE]);
423 unsigned int proc_get_swap_free(void)
428 ret = proc_get_meminfo(&mi, MEMINFO_MASK_SWAP_FREE);
430 _E("Failed to get %s: %m",
431 meminfo_id_to_string(MEMINFO_ID_SWAP_FREE));
435 return mi.value[MEMINFO_ID_SWAP_FREE];
438 unsigned int proc_get_swap_total(void)
443 ret = proc_get_meminfo(&mi, MEMINFO_MASK_SWAP_TOTAL);
445 _E("Failed to get %s: %m",
446 meminfo_id_to_string(MEMINFO_ID_SWAP_TOTAL));
450 return mi.value[MEMINFO_ID_SWAP_TOTAL];
453 int proc_get_cpu_time(pid_t pid, unsigned long *utime,
454 unsigned long *stime, unsigned long *starttime)
456 char proc_path[sizeof(PROC_STAT_PATH) + MAX_DEC_SIZE(int)];
457 _cleanup_fclose_ FILE *fp = NULL;
459 assert(utime != NULL);
460 assert(stime != NULL);
462 snprintf(proc_path, sizeof(proc_path), PROC_STAT_PATH, pid);
463 fp = fopen(proc_path, "r");
465 return RESOURCED_ERROR_FAIL;
467 if (fscanf(fp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s") < 0)
468 return RESOURCED_ERROR_FAIL;
470 if (fscanf(fp, "%lu %lu", utime, stime) < 1)
471 return RESOURCED_ERROR_FAIL;
473 if (fscanf(fp, "%*s %*s %*s %*s %*s %*s") < 0)
474 return RESOURCED_ERROR_FAIL;
476 if (fscanf(fp, "%lu", starttime) < 1)
477 return RESOURCED_ERROR_FAIL;
479 return RESOURCED_ERROR_NONE;
482 unsigned int proc_get_cpu_number(void)
488 fp = fopen("/proc/cpuinfo", "r");
491 _E("/proc/cpuinfo open failed");
492 return RESOURCED_ERROR_FAIL;
495 while (fgets(buf, PATH_MAX, fp) != NULL) {
496 if (!strncmp(buf, "processor", 9))
504 int proc_get_exepath(pid_t pid, char *buf, int len)
506 char path[PROC_BUF_MAX];
509 snprintf(path, sizeof(path), "/proc/%d/exe", pid);
510 ret = readlink(path, buf, len-1);
515 return RESOURCED_ERROR_NONE;
518 static int proc_get_data(char *path, char *buf, int len)
520 _cleanup_close_ int fd = -1;
523 fd = open(path, O_RDONLY);
525 return RESOURCED_ERROR_FAIL;
527 ret = read(fd, buf, len-1);
530 return RESOURCED_ERROR_FAIL;
533 return RESOURCED_ERROR_NONE;
536 int proc_get_raw_cmdline(pid_t pid, char *buf, int len)
538 char path[PROC_BUF_MAX];
539 snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
540 return proc_get_data(path, buf, len);
543 int proc_get_stat(pid_t pid, char *buf, int len)
545 char path[PROC_BUF_MAX];
546 snprintf(path, sizeof(path), "/proc/%d/stat", pid);
547 return proc_get_data(path, buf, len);
550 int proc_get_status(pid_t pid, char *buf, int len)
552 char path[PROC_BUF_MAX];
553 snprintf(path, sizeof(path), "/proc/%d/status", pid);
554 return proc_get_data(path, buf, len);
557 int proc_sys_node_trigger(enum sys_node_id sys_node_id)
561 if (sys_node_id >= ARRAY_SIZE(sys_node_tables)) {
562 _E("sys_node_id[%d] is out of range.\n", sys_node_id);
563 return RESOURCED_ERROR_FAIL;
565 if (!sys_node_tables[sys_node_id].valid) {
566 _E("sys_node_id[%d] is not valid.\n", sys_node_id);
567 return RESOURCED_ERROR_FAIL;
570 /* open and check if the path exists, else return fail */
571 fp = fopen(sys_node_tables[sys_node_id].path, "w");
573 _E("Failed to open %s: %m\n",
574 sys_node_tables[sys_node_id].path);
575 sys_node_tables[sys_node_id].valid = 0;
576 return RESOURCED_ERROR_FAIL;
578 fputc(sys_node_tables[sys_node_id].value, fp);
580 return RESOURCED_ERROR_NONE;
583 static const char* const meminfo_string_lookup[MEMINFO_ID_MAX] = {
584 [MEMINFO_ID_MEM_TOTAL] = "MemTotal",
585 [MEMINFO_ID_MEM_FREE] = "MemFree",
586 [MEMINFO_ID_MEM_AVAILABLE] = "MemAvailable",
587 [MEMINFO_ID_BUFFERS] = "Buffers",
588 [MEMINFO_ID_CACHED] = "Cached",
589 [MEMINFO_ID_SWAP_CACHED] = "SwapCached",
590 [MEMINFO_ID_ACTIVE] = "Active",
591 [MEMINFO_ID_INACTIVE] = "Inactive",
592 [MEMINFO_ID_ACTIVE_ANON] = "Active(anon)",
593 [MEMINFO_ID_INACTIVE_ANON] = "Inactive(anon)",
594 [MEMINFO_ID_ACTIVE_FILE] = "Active(file)",
595 [MEMINFO_ID_INACTIVE_FILE] = "Inactive(file)",
596 [MEMINFO_ID_UNEVICTABLE] = "Unevictable",
597 [MEMINFO_ID_MLOCKED] = "Mlocked",
598 [MEMINFO_ID_HIGH_TOTAL] = "HighTotal",
599 [MEMINFO_ID_HIGH_FREE] = "HighFree",
600 [MEMINFO_ID_LOW_TOTAL] = "LowTotal",
601 [MEMINFO_ID_LOW_FREE] = "LowFree",
602 [MEMINFO_ID_SWAP_TOTAL] = "SwapTotal",
603 [MEMINFO_ID_SWAP_FREE] = "SwapFree",
604 [MEMINFO_ID_DIRTY] = "Dirty",
605 [MEMINFO_ID_WRITEBACK] = "Writeback",
606 [MEMINFO_ID_ANON_PAGES] = "AnonPages",
607 [MEMINFO_ID_MAPPED] = "Mapped",
608 [MEMINFO_ID_SHMEM] = "Shmem",
609 [MEMINFO_ID_SLAB] = "Slab",
610 [MEMINFO_ID_SRECLAIMABLE] = "SReclaimable",
611 [MEMINFO_ID_SUNRECLAIM] = "SUnreclaim",
612 [MEMINFO_ID_KERNEL_STACK] = "KernelStack",
613 [MEMINFO_ID_PAGE_TABLES] = "PageTables",
614 [MEMINFO_ID_NFS_UNSTABLE] = "NFS_Unstable",
615 [MEMINFO_ID_BOUNCE] = "Bounce",
616 [MEMINFO_ID_WRITEBACK_TMP] = "WritebackTmp",
617 [MEMINFO_ID_COMMIT_LIMIT] = "CommitLimit",
618 [MEMINFO_ID_COMMITTED_AS] = "Committed_AS",
619 [MEMINFO_ID_VMALLOC_TOTAL] = "VmallocTotal",
620 [MEMINFO_ID_VMALLOC_USED] = "VmallocUsed",
621 [MEMINFO_ID_VMALLOC_CHUNK] = "VmallocChunk",
624 const char *meminfo_id_to_string(enum meminfo_id id)
626 assert(id >= 0 && id < MEMINFO_ID_MAX);
628 return meminfo_string_lookup[id];
631 int proc_get_meminfo(struct meminfo *mi, unsigned long long mask)
633 _cleanup_fclose_ FILE *f = NULL;
634 unsigned long long remain_mask = mask;
639 memset(mi, 0x0, sizeof(struct meminfo));
641 f = fopen("/proc/meminfo", "r");
643 _E("Failed to read /proc/meminfo");
647 if (remain_mask & MEMINFO_MASK_MEM_AVAILABLE)
648 remain_mask |= (MEMINFO_MASK_MEM_FREE |
649 MEMINFO_MASK_CACHED);
651 while (remain_mask) {
652 _cleanup_free_ char *k = NULL;
657 if (!fgets(buf, sizeof(buf), f)) {
663 l = strcspn(buf, ":");
671 id = meminfo_string_to_id(k);
672 if (id < 0 || id >= MEMINFO_ID_MAX)
675 if (!(remain_mask & (1ULL << id)))
678 remain_mask &= ~((1ULL << id));
680 if (sscanf(buf + l + 1, "%d", &v) != 1)
686 if (remain_mask & MEMINFO_MASK_MEM_AVAILABLE) {
687 mi->value[MEMINFO_ID_MEM_AVAILABLE] =
688 mi->value[MEMINFO_ID_MEM_FREE]
689 + mi->value[MEMINFO_ID_CACHED];
691 remain_mask &= ~MEMINFO_MASK_MEM_AVAILABLE;
697 for (i = 0; i < MEMINFO_ID_MAX; i++)
698 if (remain_mask & (1ULL << i))
699 _E("Failed to get meminfo: '%s'",
700 meminfo_id_to_string(i));
703 return RESOURCED_ERROR_NONE;
706 static int parse_spanned_pages(const char *s, regmatch_t *match,
707 unsigned int parse_tag, void *data)
711 unsigned long *parse_v = (unsigned long *)data;
713 v = strtoul(s + match[1].rm_so, &e, 0);
719 int proc_get_ram_total(unsigned int *total_kb)
721 unsigned long total_spanned = 0;
722 static unsigned int total_ram_kb = 0;
724 const struct parse_arg args[] = {
725 PARSE_TAG("spanned[[:blank:]]+([0-9]+)\n",
726 parse_spanned_pages, SPANNED),
730 if (total_ram_kb > 0) {
731 *total_kb = total_ram_kb;
732 return RESOURCED_ERROR_NONE;
735 ret = proc_parse_zoneinfo(args, &total_spanned);
736 if (ret != RESOURCED_ERROR_NONE)
737 return RESOURCED_ERROR_NO_DATA;
739 total_ram_kb = (unsigned int)BYTE_TO_KBYTE((unsigned long long)total_spanned << PAGE_SHIFT);
740 *total_kb = total_ram_kb;
742 return RESOURCED_ERROR_NONE;
745 static inline char *proc_skip_blanks(const char *s)
747 while (s && (isblank(*s) || !isalnum(*s)))
752 #define PROC_MAX_REG_MATCH 6
754 static int proc_find_match(char *s, const struct parse_arg *parse,
755 void *data, size_t *parsed)
758 regmatch_t match[PROC_MAX_REG_MATCH];
761 if (!s || !(*s != '\0'))
764 if (!parse->callback)
769 result = regcomp(®ex, parse->re_exp, REG_EXTENDED);
771 result = regexec(®ex, s, ARRAY_SIZE(match), match, 0);
773 regmatch_t *m = match;
775 result = parse->callback(s, match, parse->tag, data);
778 while ((m - match) < PROC_MAX_REG_MATCH &&
779 m->rm_so >= 0 && m->rm_eo > 0)
782 *parsed = m > match ? (--m)->rm_eo + 1 : 0;
791 * @brief Rather basic parser that relies on provided arguments,
792 * specifying the actual expression to look for.
793 * Note that it does not make any assumptions as to how
794 * the input to be parsed is actual formated and what is the
795 * layout of the information held within it.
796 * - mind it as badly specified arguments might result in a failure
797 * to identify potential match or finding a match but not one that
798 * has been expected. It is up to the caller to perform
799 * the actual formatting of data.
800 * Parsing is done per line with possible multiple regexes.
801 * @return Returns number of found matches against supplied parse arguments
803 static int proc_parse_single(const char *buffer,
804 const struct parse_arg *parse_args,
807 char *s = (char*)buffer;
809 unsigned int match = 0;
811 while (s && *s != '\n' && *s != '\0') {
812 const struct parse_arg *p = parse_args;
815 s = proc_skip_blanks(s);
817 while (proc_find_match(s, p, data, &size))
829 * @brief Simplified procfs parser
831 * Reads the procfs entry line by line triggering regex match
832 * function for each supplied argument.
833 * Note: The set of arguments should end with an empty one.
835 * @return returns 0 upon successfully calling the actual parsing
836 * procedure at least once, error code otherwise.
837 * Note: it is up to the caller to properly handle
838 * callbacks triggered upon each match found and to determine
839 * if parsing itself actually succeeded.
841 int proc_parse_entry(const char *path, const struct parse_arg *parse_args,
845 _cleanup_fclose_ FILE *f = NULL;
847 f = fopen(path, "r");
851 while (fgets(buf, sizeof(buf), f))
852 proc_parse_single(buf, parse_args, data);
853 return ferror(f) ? -errno : 0;
856 int proc_get_uptime(unsigned long *uptime)
858 _cleanup_fclose_ FILE *fp = NULL;
861 fp = fopen("/proc/uptime", "r");
863 return RESOURCED_ERROR_FAIL;
865 if (fscanf(fp, "%lf %*s", &stime) < 0)
866 return RESOURCED_ERROR_FAIL;
868 *uptime = (unsigned long)stime;
869 return RESOURCED_ERROR_NONE;
873 * @brief print simple memory info with rss and memtotal
875 * Reads the procfs about all process id and print RSS usage.
876 * Note: If fp is valid, the result is written by desired file.
877 * Otherwise, it printed message through dlog.
879 void proc_print_meninfo(FILE *fp)
881 _cleanup_closedir_ DIR *dir = NULL;
884 char cmdline[PATH_MAX];
885 _cleanup_fclose_ FILE *output_file = NULL;
886 int oom_score_adj, ret;
887 unsigned int rss, swap;
889 unsigned int free_kb = 0;
890 unsigned int total_mem_kb = 0, available_kb = 0, used_kb;
891 unsigned int swap_total_kb = 0, swap_free_kb = 0, swap_used_kb;
892 unsigned long long zram_used_bytes;
894 dir = opendir("/proc");
896 _E("cannot read directory /proc.");
900 LOG_DUMP(fp, " PID RSS SWAP OOM_SCORE COMMAND\n");
903 while ((de = readdir(dir)) != NULL) {
904 pid = atoi(de->d_name);
906 /* exclude negative value which means string value and kernel thread */
907 if (pid < 1 || pid == caller)
910 ret = proc_get_cmdline(pid, cmdline, sizeof cmdline);
914 ret = proc_get_mem_status(pid, &swap, &rss);
918 ret = proc_get_oom_score_adj(pid, &oom_score_adj);
922 LOG_DUMP(fp, "%8d %8u %8u %8d %s\n",
931 ret = proc_get_meminfo(&mi,
932 (MEMINFO_MASK_MEM_TOTAL |
933 MEMINFO_MASK_MEM_FREE |
934 MEMINFO_MASK_MEM_AVAILABLE |
935 MEMINFO_MASK_CACHED |
936 MEMINFO_MASK_SWAP_TOTAL |
937 MEMINFO_MASK_SWAP_FREE));
939 _E("Failed to get meminfo");
943 total_mem_kb = mi.value[MEMINFO_ID_MEM_TOTAL];
944 free_kb = mi.value[MEMINFO_ID_MEM_FREE];
945 available_kb = mi.value[MEMINFO_ID_MEM_AVAILABLE];
946 swap_total_kb = mi.value[MEMINFO_ID_SWAP_TOTAL];
947 swap_free_kb = mi.value[MEMINFO_ID_SWAP_FREE];
949 used_kb = total_mem_kb - available_kb;
950 swap_used_kb = swap_total_kb - swap_free_kb;
952 ret = fread_nth_ulonglong(SWAP_ZRAM_SYSFILE"mm_stat", 2, &zram_used_bytes);
953 if (ret == -ENOENT) {
954 ret = fread_ulonglong(SWAP_ZRAM_SYSFILE"mem_used_total", &zram_used_bytes);
957 if (ret != RESOURCED_ERROR_NONE)
960 LOG_DUMP(fp, "====================================================================\n");
961 LOG_DUMP(fp, "Total RAM size: \t%15d MB( %6d kB)\n",
962 KBYTE_TO_MBYTE(total_mem_kb), total_mem_kb);
963 LOG_DUMP(fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n",
964 KBYTE_TO_MBYTE(total_mem_kb - free_kb), total_mem_kb - free_kb);
965 LOG_DUMP(fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n",
966 KBYTE_TO_MBYTE(used_kb), used_kb);
967 LOG_DUMP(fp, "Used (Mem): \t\t%15d MB( %6d kB)\n",
968 KBYTE_TO_MBYTE(used_kb), used_kb);
969 LOG_DUMP(fp, "Used (Swap): \t\t%15d MB( %6d kB)\n",
970 KBYTE_TO_MBYTE(swap_used_kb), swap_used_kb);
971 LOG_DUMP(fp, "Used (Zram block device): %13d MB( %6d kB)\n",
972 (int)BYTE_TO_MBYTE(zram_used_bytes), (int)BYTE_TO_KBYTE(zram_used_bytes));
973 LOG_DUMP(fp, "Mem Free:\t\t%15d MB( %6d kB)\n",
974 KBYTE_TO_MBYTE(free_kb), free_kb);
975 LOG_DUMP(fp, "Available (Free+Reclaimable):%10d MB( %6d kB)\n",
976 KBYTE_TO_MBYTE(available_kb), available_kb);
980 int proc_get_buddyinfo(const char *zone, struct buddyinfo *bi)
982 _cleanup_fclose_ FILE *f = NULL;
988 f = fopen("/proc/buddyinfo", "re");
994 char zonename[32] = { 0, };
996 if (!fgets(buf, sizeof(buf), f)) {
1003 n = sscanf(buf, "Node %d, zone %31s %d %d %d %d %d %d %d %d %d %d %d",
1008 &bi->page[PAGE_16K],
1009 &bi->page[PAGE_32K],
1010 &bi->page[PAGE_64K],
1011 &bi->page[PAGE_128K],
1012 &bi->page[PAGE_256K],
1013 &bi->page[PAGE_512K],
1016 &bi->page[PAGE_4M]);
1020 if (!streq(zone, zonename))
1029 void proc_swap_free(struct proc_swaps *swap)
1034 free(swap->filename);
1039 void proc_swaps_free(struct proc_swaps **swaps)
1046 for (i = 0; swaps[i]; i++)
1047 proc_swap_free(swaps[i]);
1052 int proc_get_swaps(struct proc_swaps ***swaps)
1054 _cleanup_proc_swaps_free_ struct proc_swaps **ss = NULL;
1055 _cleanup_fclose_ FILE *f = NULL;
1062 f = fopen("/proc/swaps", "re");
1067 _cleanup_proc_swap_free_ struct proc_swaps *swap = NULL;
1069 if (!fgets(buf, sizeof(buf), f)) {
1076 swap = calloc(sizeof(struct proc_swaps), 1);
1080 if (sscanf(buf, "%ms %ms %u %u %d",
1085 &swap->priority) != 5)
1088 ss = (struct proc_swaps **)realloc(ss, sizeof(struct proc_swaps *) * (cnt + 2));