1 /* Copyright 2014-2020 Samsung Electronics Co., Ltd All Rights Reserved
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
25 #include <sys/types.h>
27 #include <linux/limits.h>
35 #include <sys/utsname.h>
37 #define STR_SGX_PATH "/dev/pvrsrvkm"
38 #define STR_3D_PATH1 "/dev/mali"
39 #define STR_3D_PATH2 "/dev/kgsl-3d0"
40 #define STR_3D_UNIFIED_PATH "/usr/bin/gpu_mem_info"
41 #define STR_DRM_PATH1 "/drm mm object (deleted)"
42 #define STR_DRM_PATH2 "/dev/dri/card"
43 #define STR_DRM_DBG_DIR "/sys/kernel/debug/dri/"
44 #define STR_DRM_RENDER_PATH "/dev/dri/renderD"
45 #define MEMCG_PATH "/sys/fs/cgroup/memory"
46 #define ZRAM_USED_PATH "/sys/block/zram0/mem_used_total"
47 #define ZRAM_MM_STAT_PATH "/sys/block/zram0/mm_stat"
48 #define OOM_SCORE_ADJ_STR "oom_score_adj"
49 #define OOM_SCORE_STR "oom_score"
51 #define BUF_MAX (BUFSIZ) /* most optimal for libc::stdio */
52 #define BUF_INC_SIZE (512 * 1024) /* maximal SMAPS I saw 2 MB */
53 #define KB(bytes) ((bytes)/1024)
55 #define BYTE_TO_KBYTE(b) ((b) >> 10)
56 #define BYTE_TO_MBYTE(b) ((b) >> 20)
57 #define BYTE_TO_GBYTE(b) ((b) >> 30)
59 #define KBYTE_TO_BYTE(k) ((k) << 10)
60 #define KBYTE_TO_MBYTE(k) ((k) >> 10)
61 #define KBYTE_TO_GBYTE(k) ((k) >> 20)
63 #define GBYTE_TO_BYTE(g) ((g) << 30)
64 #define GBYTE_TO_KBYTE(g) ((g) << 20)
65 #define GBYTE_TO_MBYTE(g) ((g) << 10)
67 typedef struct geminfo geminfo;
68 typedef struct mapinfo mapinfo;
69 typedef struct trib_mapinfo trib_mapinfo;
85 unsigned shared_clean;
86 unsigned shared_dirty;
87 unsigned private_clean;
88 unsigned private_dirty;
93 /* classify normal, graphic and other devices memory */
95 unsigned shared_clean;
96 unsigned shared_dirty;
97 unsigned private_clean;
98 unsigned private_dirty;
99 unsigned shared_clean_pss;
100 unsigned shared_dirty_pss;
109 unsigned other_devices;
111 unsigned render_gem_mmap;
124 static int use_gpu_mem_info = 0;
125 static int pid_max = 0;
126 static int dri_card = 0;
128 /* reads file contents into memory */
129 static char* cread(const char* path)
131 /* once allocated area for reads */
132 static char* text = NULL;
133 static size_t size = 0;
138 int fd = open(path, O_RDONLY);
144 /* ensure we have enough space */
146 ptr = (char*)realloc(text, size + BUF_INC_SIZE);
155 size += BUF_INC_SIZE;
157 ret = read(fd, ptr, cap);
160 } else if (ret > 0) {
167 return (ret < 0 ? NULL : text);
170 /* like fgets/gets but adjusting contents pointer */
171 static inline char* cgets(char** contents)
173 if (contents && *contents && **contents) {
174 char* bos = *contents; /* begin of string */
175 char* eos = strchr(bos, '\n'); /* end of string */
189 /* get pid_max value */
190 static inline int get_pid_max(void)
192 static const char pid_max_path[] = "/proc/sys/kernel/pid_max";
195 line = cread(pid_max_path);
197 fprintf(stderr, "cannot open %s\n", pid_max_path);
201 return strtoul(line, NULL, 10);
205 static unsigned get_peak_rss(unsigned int pid)
207 static const char field[] = "VmHWM:";
212 snprintf(tmp, sizeof(tmp), "/proc/%d/status", pid);
215 fprintf(stderr, "cannot open %s\n", tmp);
219 value = strstr(line, field);
221 value += sizeof(field);
222 return strtoul(value, NULL, 10);
227 #define NUM_GEM_FIELD 6
229 static geminfo *read_geminfo(FILE *fp)
233 unsigned int pid, tgid, handle, refcount, hcount;
236 if (fgets(line, BUF_MAX, fp) != NULL) {
237 if (sscanf(line, "%d %d %d %d %d 0x%x",
238 &pid, &tgid, &handle, &refcount,
239 &hcount, &gem_size) != NUM_GEM_FIELD)
244 tgeminfo = malloc(sizeof(geminfo));
245 if (tgeminfo == NULL)
247 tgeminfo->tgid = tgid;
248 tgeminfo->hcount = hcount;
249 tgeminfo->rss_size = KB(gem_size);
250 tgeminfo->pss_size = KB(gem_size/tgeminfo->hcount);
258 static geminfo *find_geminfo(unsigned int tgid, geminfo *gilist)
261 for (gi = gilist; gi; ) {
262 if (gi->tgid == tgid)
270 static geminfo *get_geminfo(FILE *drm_fp)
273 geminfo *gilist = NULL;
274 geminfo *exist_ginfo = NULL;
278 if (fgets(line, BUF_MAX, drm_fp) == NULL) {
281 /* we should count a number of whitespace separated fields */
282 int in_field = (line[0] && !isblank(line[0]));
283 unsigned int size = (unsigned)in_field;
284 const char* ptr = &line[1];
286 /* sscanf() was used in original code, so number of fields */
287 /* in string is expected to be at least NUM_GEM_FIELD */
288 while (*ptr && size < NUM_GEM_FIELD) {
289 if (isblank(*ptr++)) {
296 /* next field started */
303 if (size != NUM_GEM_FIELD) {
308 while ((ginfo = read_geminfo(drm_fp)) != NULL) {
309 if (gilist && ginfo->tgid == gilist->tgid) {
310 gilist->pss_size += ginfo->pss_size;
311 gilist->rss_size += ginfo->rss_size;
314 } else if (gilist && ((exist_ginfo = find_geminfo(ginfo->tgid, gilist)) != NULL)) {
315 exist_ginfo->pss_size += ginfo->pss_size;
316 exist_ginfo->rss_size += ginfo->rss_size;
320 ginfo->next = gilist;
327 static geminfo *load_geminfo(void)
332 drm_fp = fopen(STR_DRM_DBG_DIR "0/gem_info", "r");
333 if (drm_fp == NULL) {
334 drm_fp = fopen(STR_DRM_DBG_DIR "1/gem_info", "r");
336 if (drm_fp == NULL) {
338 "cannot open " STR_DRM_DBG_DIR "%d/gem_info\n",
344 gilist = get_geminfo(drm_fp);
351 static geminfo *load_gpu_geminfo(void)
356 /* Check the render dri card having gpu_gem_info */
357 drm_fp = fopen(STR_DRM_DBG_DIR "128/gpu_gem_info", "r");
358 if (drm_fp == NULL) {
360 "cannot open " STR_DRM_DBG_DIR "128/gpu_gem_info\n");
364 gilist = get_geminfo(drm_fp);
371 /* b6e82000-b6e83000 rw-p 00020000 b3:19 714 /usr/lib/ld-2.20-2014.11.so : TM1
372 * 7f9389d000-7f9389e000 rw-p 0001f000 b3:12 618 /usr/lib64/ld-2.20-2014.11.so : TM2
373 * 7fae2e4b2000-7fae2e4b3000 r--p 00021000 fe:01 603 /usr/lib64/ld-2.20-2014.11.so : x86-64 Emulator
374 * 01234567890123456789012345678901234567890123456789012345678901234567890123456789
377 mapinfo *read_mapinfo(char** smaps)
384 if ((line = cgets(smaps)) == 0)
391 mi = malloc(sizeof(mapinfo));
395 n = sscanf(line, "%lx-%lx %ms %*s %*s %*s %m[^\n]",
396 &mi->start, &mi->end, &mi->perm, &mi->name);
398 if (n == 3 && !mi->name)
399 mi->name = strndup("[anon]", strlen("[anon]"));
401 fprintf(stderr, "Fail to parse smaps\n");
406 while ((line = cgets(smaps))) {
407 if (sscanf(line, "Size: %d kB", &mi->size) == 1)
409 else if (sscanf(line, "Rss: %d kB", &mi->rss) == 1)
411 else if (sscanf(line, "Pss: %d kB", &mi->pss) == 1)
413 else if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) == 1)
415 else if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) == 1)
417 else if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) == 1)
419 else if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) == 1)
421 else if (sscanf(line, "Swap: %d kB", &mi->swap) == 1)
424 /* Drain lines until it meets next VMA address */
426 if ((next >= '0' && next <= '9') || (next >= 'a' && next <= 'f'))
434 static unsigned total_gem_memory(void)
437 unsigned total_gem_mem = 0;
438 unsigned name, size, handles, refcount;
442 gem_fp = fopen(STR_DRM_DBG_DIR "0/gem_names", "r");
444 gem_fp = fopen(STR_DRM_DBG_DIR "1/gem_names", "r");
445 if (gem_fp == NULL) {
447 "cannot open " STR_DRM_DBG_DIR "%d/gem_names\n", dri_card);
451 if (fgets(line, BUF_MAX, gem_fp) == NULL) {
456 while (fgets(line, BUF_MAX, gem_fp) != NULL) {
457 if (sscanf(line, "%d %d %d %d\n",
458 &name, &size, &handles, &refcount) == 4) {
459 if (total_gem_mem <= UINT_MAX - size) {
460 total_gem_mem += size;
466 return total_gem_mem;
469 int get_zram_used(u_int32_t *zram_used)
472 /* only read 3rd value */
473 char *fscanf_format = "%*d %*d %d %*d %*d %*d %*d %*d";
476 f = fopen(ZRAM_MM_STAT_PATH, "r");
479 * ZRAM_USED_PATH is deprecated on latest kernel, but to support
480 * old kernel try with that if fails new node 'mm_stat'.
482 f = fopen(ZRAM_USED_PATH, "r");
484 fprintf(stderr, "Fail to open zram file.\n");
487 fscanf_format = "%u";
490 ret = fscanf(f, fscanf_format, zram_used);
492 fprintf(stderr, "Fail to read file\n");
501 int fread_uint(const char *path, u_int32_t *number)
506 f = fopen(path, "r");
509 fprintf(stderr, "Fail to open %s file.\n", path);
513 ret = fscanf(f, "%u", number);
515 fprintf(stderr, "Fail to read file\n");
524 static int cgroup_read_node(const char *cgroup_name,
525 const char *file_name, unsigned int *value)
527 char buf[PATH_MAX + NAME_MAX];
529 snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name);
530 ret = fread_uint(buf, value);
535 * @desc Provides usage in bytes for provided memory cgroup. Works
536 * with/without swap accounting.
538 * @param memcg_path[in] Full path to memory cgroup
539 * @param swap[in] Boolean value for deciding if account usage with swap
540 * @return current cgroup usage in bytes or 0 on error
542 static unsigned int get_memcg_usage(const char *memcg_path, bool swap)
548 ret = cgroup_read_node(memcg_path,
549 "/memory.memsw.usage_in_bytes", &usage);
551 ret = cgroup_read_node(memcg_path, "/memory.usage_in_bytes",
561 static void get_memcg_info(FILE *output_fp)
565 struct dirent *entry;
566 struct stat path_stat;
568 unsigned long usage, usage_with_swap;
570 fprintf(output_fp, "====================================================================\n");
571 fprintf(output_fp, "MEMORY CGROUPS USAGE INFO\n");
573 pdir = opendir(MEMCG_PATH);
575 fprintf(stderr, "cannot read directory %s", MEMCG_PATH);
580 while ((entry = readdir(pdir)) != NULL && !errno) {
581 snprintf(buf, sizeof(buf), "%s/%s", MEMCG_PATH, entry->d_name);
582 /* If can't stat then ignore */
583 if (stat(buf, &path_stat) != 0)
586 /* If it's not directory or it's parent path then ignore */
587 if (!(S_ISDIR(path_stat.st_mode) &&
588 strncmp(entry->d_name, "..", 3)))
591 usage = get_memcg_usage(buf, false);
592 usage_with_swap = get_memcg_usage(buf, true);
593 /* It is posible by rounding errors to get negative value */
594 usage_swap = usage_with_swap - usage;
598 /* Case of root cgroup in hierarchy */
599 if (!strncmp(entry->d_name, ".", 2))
600 fprintf(output_fp, "%13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB) \n\n",
601 MEMCG_PATH, BYTE_TO_MBYTE(usage),
602 BYTE_TO_KBYTE(usage),
603 BYTE_TO_MBYTE(usage_with_swap),
604 BYTE_TO_KBYTE(usage_with_swap),
605 BYTE_TO_MBYTE(usage_swap),
606 BYTE_TO_KBYTE(usage_swap));
608 fprintf(output_fp, "memcg: %13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB)\n",
609 entry->d_name, BYTE_TO_MBYTE(usage),
610 BYTE_TO_KBYTE(usage),
611 BYTE_TO_MBYTE(usage_with_swap),
612 BYTE_TO_KBYTE(usage_with_swap),
613 BYTE_TO_MBYTE(usage_swap),
614 BYTE_TO_KBYTE(usage_swap));
621 static void get_mem_info(FILE *output_fp)
626 unsigned int free = 0, cached = 0;
627 unsigned int total_mem = 0, available = 0, used;
628 unsigned int swap_total = 0, swap_free = 0, zram_used, swap_used;
629 unsigned int used_ratio;
631 if (output_fp == NULL)
634 fp = fopen("/proc/meminfo", "r");
637 fprintf(stderr, "/proc/meminfo open failed, %p", fp);
641 while (fgets(buf, PATH_MAX, fp) != NULL) {
642 if ((idx = strstr(buf, "MemTotal:"))) {
643 idx += strlen("Memtotal:");
644 while ((idx < buf + PATH_MAX) && (*idx < '0' || *idx > '9'))
646 total_mem = atoi(idx);
647 } else if ((idx = strstr(buf, "MemFree:"))) {
648 idx += strlen("MemFree:");
649 while ((idx < buf + PATH_MAX) && (*idx < '0' || *idx > '9'))
652 } else if ((idx = strstr(buf, "MemAvailable:"))) {
653 idx += strlen("MemAvailable:");
654 while ((idx < buf + PATH_MAX) && (*idx < '0' || *idx > '9'))
656 available = atoi(idx);
657 } else if ((idx = strstr(buf, "Cached:")) && !strstr(buf, "Swap")) {
658 idx += strlen("Cached:");
659 while ((idx < buf + PATH_MAX) && (*idx < '0' || *idx > '9'))
662 } else if ((idx = strstr(buf, "SwapTotal:"))) {
663 idx += strlen("SwapTotal:");
664 while ((idx < buf + PATH_MAX) && (*idx < '0' || *idx > '9'))
666 swap_total = atoi(idx);
667 } else if ((idx = strstr(buf, "SwapFree:"))) {
668 idx += strlen("SwapFree");
669 while ((idx < buf + PATH_MAX) && (*idx < '0' || *idx > '9'))
671 swap_free = atoi(idx);
676 if (total_mem == 0) {
677 fprintf(stderr, "cannot get total memory size\n");
683 available = free + cached;
684 used = total_mem - available;
685 used_ratio = used * 100 / total_mem;
686 swap_used = swap_total - swap_free;
688 if (get_zram_used(&zram_used) < 0)
692 "====================================================================\n");
695 fprintf(output_fp, "Total RAM size: \t%15d MB( %6d kB)\n",
696 total_mem >> 10, total_mem);
698 fprintf(output_fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n",
699 (total_mem - free) >> 10, total_mem - free);
701 fprintf(output_fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n",
704 fprintf(output_fp, "Used (Mem): \t\t%15d MB( %6d kB)\n",
707 fprintf(output_fp, "Used (Swap): \t\t%15d MB( %6d kB)\n",
708 swap_used >> 10, swap_used);
710 fprintf(output_fp, "Used (Zram block device): %13d MB( %6d kB)\n",
711 BYTE_TO_MBYTE(zram_used), BYTE_TO_KBYTE(zram_used));
713 fprintf(output_fp, "Used Ratio: \t\t%15d %%\n", used_ratio);
715 fprintf(output_fp, "Mem Free:\t\t%15d MB( %6d kB)\n",
718 fprintf(output_fp, "Available (Free+Reclaimable):%10d MB( %6d kB)\n",
724 static int get_tmpfs_info(FILE *output_fp)
728 char *tmpfs_mp; /* tmpfs mount point */
729 struct statfs tmpfs_info;
731 if (output_fp == NULL)
734 fp = fopen("/etc/mtab", "r");
739 "====================================================================\n");
740 fprintf(output_fp, "TMPFS INFO\n");
742 while (fgets(line, BUF_MAX, fp) != NULL) {
743 if (sscanf(line, "tmpfs %ms tmpfs", &tmpfs_mp) == 1) {
744 if (statfs(tmpfs_mp, &tmpfs_info) == 0) {
746 #ifndef __USE_FILE_OFFSET64
747 "tmpfs %16s Total %8ld KB, Used %8ld, Avail %8ld\n",
749 "tmpfs %16s Total %8"PRId64" KB, Used %8"PRId64", Avail %8"PRId64"\n",
752 /* 1 block is 4 KB */
753 tmpfs_info.f_blocks * 4,
754 (tmpfs_info.f_blocks - tmpfs_info.f_bfree) * 4,
755 tmpfs_info.f_bfree * 4);
765 mapinfo *load_maps(int pid)
772 snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
777 while ((mi = read_mapinfo(&smaps)) != 0) {
779 if ((!strcmp(mi->name, milist->name)
780 && (mi->name[0] != '['))) {
781 milist->size += mi->size;
782 milist->swap += mi->swap;
783 milist->rss += mi->rss;
784 milist->pss += mi->pss;
785 milist->shared_clean += mi->shared_clean;
786 milist->shared_dirty += mi->shared_dirty;
787 milist->private_clean += mi->private_clean;
788 milist->private_dirty += mi->private_dirty;
790 milist->end = mi->end;
791 strncpy(milist->perm, mi->perm, 4);
805 static void init_trib_mapinfo(trib_mapinfo *tmi)
809 tmi->shared_clean = 0;
810 tmi->shared_dirty = 0;
811 tmi->private_clean = 0;
812 tmi->private_dirty = 0;
814 tmi->shared_clean_pss = 0;
815 tmi->shared_dirty_pss = 0;
823 tmi->other_devices = 0;
825 tmi->render_gem_mmap = 0;
828 unsigned int get_graphic_3d_meminfo(unsigned int tgid)
830 char command[256], buf[256];
832 unsigned int size = 0;
836 snprintf(command, sizeof(command), "%s %d", STR_3D_UNIFIED_PATH, tgid);
837 p_gpu = popen(command, "r");
839 if (fgets(buf, 256, p_gpu)) {
840 ret = sscanf(buf, "%d %ms %d %ms", &tid, &tmp[0], &size, &tmp[1]);
852 get_trib_mapinfo(unsigned int tgid, mapinfo *milist,
853 geminfo *gilist, geminfo *gpu_glist,
854 trib_mapinfo *result)
858 mapinfo *temp = NULL;
864 init_trib_mapinfo(result);
866 if (use_gpu_mem_info)
867 result->graphic_3d = get_graphic_3d_meminfo(tgid);
869 for (mi = milist; mi;) {
870 if (!use_gpu_mem_info && strstr(mi->name, STR_SGX_PATH)) {
871 result->graphic_3d += mi->pss;
872 } else if (!use_gpu_mem_info && (strstr(mi->name, STR_3D_PATH1) ||
873 strstr(mi->name, STR_3D_PATH2))) {
874 result->graphic_3d += mi->size;
875 } else if (mi->rss != 0 && mi->pss == 0
876 && mi->shared_clean == 0
877 && mi->shared_dirty == 0
878 && mi->private_clean == 0
879 && mi->private_dirty == 0
881 result->other_devices += mi->size;
882 } else if (!strncmp(mi->name, STR_DRM_PATH1,
883 sizeof(STR_DRM_PATH1)) ||
884 !strncmp(mi->name, STR_DRM_PATH2,
885 sizeof(STR_DRM_PATH2))) {
886 result->gem_mmap += mi->rss;
887 } else if (!strncmp(mi->name, STR_DRM_RENDER_PATH,
888 sizeof(STR_DRM_RENDER_PATH))) {
889 result->render_gem_mmap += mi->rss;
891 result->shared_clean += mi->shared_clean;
892 result->shared_dirty += mi->shared_dirty;
893 result->private_clean += mi->private_clean;
894 result->private_dirty += mi->private_dirty;
895 result->swap += mi->swap;
896 result->rss += mi->rss;
897 result->pss += mi->pss;
898 result->size += mi->size;
900 if (mi->shared_clean != 0)
901 result->shared_clean_pss += mi->pss;
902 else if (mi->shared_dirty != 0)
903 result->shared_dirty_pss += mi->pss;
914 result->peak_rss = get_peak_rss(tgid);
915 if (result->peak_rss < result->rss)
916 result->peak_rss = result->rss;
917 if (result->gem_mmap > 0)
918 result->peak_rss -= result->gem_mmap;
919 if (result->render_gem_mmap > 0)
920 result->peak_rss -= result->render_gem_mmap;
922 gi = find_geminfo(tgid, gilist);
924 result->gem_rss = gi->rss_size;
925 result->gem_pss = gi->pss_size;
930 * drm render has gpu_gem_info, then use gpua gem pss
931 * as graphic 3d memory
933 gi = find_geminfo(tgid, gpu_glist);
935 result->graphic_3d += gi->pss_size;
941 static int get_cmdline(unsigned int pid, char *cmdline)
944 char buf[NAME_MAX] = {0, };
947 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
948 fp = fopen(buf, "r");
952 fprintf(stderr, "cannot file open %s\n", buf);
955 if ((ret = fscanf(fp, "%4095s", cmdline)) < 1) {
964 static int get_oomscoreadj(unsigned int pid, const char *oom_string)
970 snprintf(tmp, sizeof(tmp), "/proc/%d/%s", pid, oom_string);
971 fp = fopen(tmp, "r");
977 if (fgets(tmp, sizeof(tmp), fp) == NULL) {
983 oomadj_val = atoi(tmp);
989 static void get_rss(pid_t pid, unsigned long *result)
992 char proc_path[PATH_MAX];
993 unsigned long rss = 0;
997 snprintf(proc_path, sizeof(proc_path), "/proc/%d/statm", pid);
998 fp = fopen(proc_path, "r");
1002 if (fscanf(fp, "%*s %ld", &rss) < 1) {
1009 /* convert page to Kb */
1014 static const char* get_readable_oom_path(void)
1019 snprintf(tmp, sizeof(tmp), "/proc/1/%s", OOM_SCORE_ADJ_STR);
1020 fp = fopen(tmp, "r");
1023 return OOM_SCORE_ADJ_STR;
1025 snprintf(tmp, sizeof(tmp), "/proc/1/%s", OOM_SCORE_STR);
1026 fp = fopen(tmp, "r");
1029 return OOM_SCORE_STR;
1034 static void show_rss(int output_type, char *output_path)
1037 struct dirent *curdir;
1039 char cmdline[PATH_MAX];
1040 FILE *output_file = NULL;
1043 const char *oom_path = NULL;
1045 oom_path = get_readable_oom_path();
1047 fprintf(stderr, "there is no readable oom path\n");
1051 pDir = opendir("/proc");
1053 fprintf(stderr, "cannot read directory /proc.\n");
1057 if (output_type == OUTPUT_FILE && output_path) {
1058 output_file = fopen(output_path, "w+");
1060 fprintf(stderr, "cannot open output file(%s)\n",
1066 output_file = stdout;
1069 fprintf(output_file,
1070 " PID RSS %14s COMMAND\n", oom_path);
1073 while ((curdir = readdir(pDir)) != NULL && !errno) {
1074 pid = atoi(curdir->d_name);
1075 if (pid < 1 || pid > pid_max || pid == getpid())
1078 if (get_cmdline(pid, cmdline) < 0)
1081 oom_score_adj = get_oomscoreadj(pid, oom_path);
1083 fprintf(output_file,
1084 "%8d %8lu %8d %s\n",
1091 } /* end of while */
1093 get_tmpfs_info(output_file);
1094 get_mem_info(output_file);
1096 fclose(output_file);
1102 static int show_map_all_new(int output_type, char *output_path)
1105 struct dirent *curdir;
1110 unsigned total_pss = 0;
1111 unsigned total_private = 0;
1112 unsigned total_private_code = 0;
1113 unsigned total_private_data = 0;
1114 unsigned total_shared_code = 0;
1115 unsigned total_shared_data = 0;
1116 unsigned total_shared_code_pss = 0;
1117 unsigned total_shared_data_pss = 0;
1118 unsigned total_swap = 0;
1119 unsigned total_rss = 0;
1120 unsigned total_graphic_3d = 0;
1121 unsigned total_gem_rss = 0;
1122 unsigned total_gem_pss = 0;
1123 unsigned total_peak_rss = 0;
1124 unsigned total_allocated_gem = 0;
1126 char cmdline[PATH_MAX];
1127 FILE *output_file = NULL;
1129 const char *oom_path = NULL;
1131 oom_path = get_readable_oom_path();
1133 fprintf(stderr, "there is no readable oom path\n");
1137 use_gpu_mem_info = (!access(STR_3D_UNIFIED_PATH, F_OK | X_OK)) ? 1 : 0;
1140 pDir = opendir("/proc");
1142 fprintf(stderr, "cannot read directory /proc.\n");
1146 if (output_type == OUTPUT_FILE && output_path) {
1147 output_file = fopen(output_path, "w+");
1149 fprintf(stderr, "cannot open output file(%s)\n",
1155 output_file = stdout;
1157 glist = load_geminfo();
1158 gpu_glist = load_gpu_geminfo();
1162 fprintf(output_file,
1163 " PID S(CODE) S(DATA) P(CODE) P(DATA) "
1164 " PEAK PSS 3D GEM(PSS) GEM(RSS) "
1165 " SWAP %14s COMMAND\n", oom_path);
1167 fprintf(output_file,
1168 " PID CODE DATA PEAK PSS "
1169 " 3D GEM(PSS) SWAP COMMAND\n");
1173 while ((curdir = readdir(pDir)) != NULL && !errno) {
1174 pid = atoi(curdir->d_name);
1175 if (pid < 1 || pid > pid_max || pid == getpid())
1178 if (get_cmdline(pid, cmdline) < 0)
1181 milist = load_maps(pid);
1185 /* get classified map info */
1186 get_trib_mapinfo(pid, milist, glist, gpu_glist, &tmi);
1187 oom_score_adj = get_oomscoreadj(pid, oom_path);
1191 fprintf(output_file,
1192 "%8d %8d %8d %8d %8d "
1193 "%8d %8d %8d %8d %8d "
1209 fprintf(output_file,
1210 "%8d %8d %8d %8d %8d "
1213 tmi.shared_clean + tmi.private_clean,
1214 tmi.shared_dirty + tmi.private_dirty,
1222 if (tmi.other_devices != 0)
1223 fprintf(output_file,
1224 "%s(%d) %d KB may mapped by device(s).\n",
1225 cmdline, pid, tmi.other_devices);
1228 total_private += (tmi.private_clean + tmi.private_dirty);
1229 total_pss += tmi.pss;
1230 total_rss += tmi.rss;
1231 total_graphic_3d += tmi.graphic_3d;
1232 total_gem_rss += tmi.gem_rss;
1233 total_gem_pss += tmi.gem_pss;
1234 total_private_code += tmi.private_clean;
1235 total_private_data += tmi.private_dirty;
1236 total_swap += tmi.swap;
1237 total_shared_code += tmi.shared_clean;
1238 total_shared_data += tmi.shared_dirty;
1239 total_peak_rss += tmi.peak_rss;
1241 total_shared_code_pss += tmi.shared_clean_pss;
1242 total_shared_data_pss += tmi.shared_dirty_pss;
1244 } /* end of while */
1246 total_allocated_gem = KB(total_gem_memory());
1247 fprintf(output_file,
1248 "==============================================="
1249 "===============================================\n");
1251 fprintf(output_file,
1252 "TOTAL: S(CODE) S(DATA) P(CODE) P(DATA) "
1253 " PEAK PSS 3D GEM(PSS) GEM(RSS) "
1254 "GEM(ALLOC) SWAP TOTAL(KB)\n");
1255 fprintf(output_file,
1257 "%8d %8d %8d %8d %8d "
1259 total_shared_code, total_shared_data,
1260 total_private_code, total_private_data,
1261 total_peak_rss, total_pss, total_graphic_3d,
1262 total_gem_pss, total_gem_rss,
1263 total_allocated_gem, total_swap,
1264 total_pss + total_graphic_3d +
1265 total_allocated_gem);
1267 fprintf(output_file,
1268 "TOTAL: CODE DATA PEAK PSS "
1269 " 3D GEM(PSS) GEM(ALLOC) SWAP TOTAL(KB)\n");
1270 fprintf(output_file,
1272 "%8d %8d %10d %8d %10d\n",
1273 total_shared_code + total_private_code,
1274 total_shared_data + total_private_data,
1275 total_peak_rss, total_pss,
1276 total_graphic_3d, total_gem_pss,
1277 total_allocated_gem, total_swap,
1278 total_pss + total_graphic_3d +
1279 total_allocated_gem);
1284 fprintf(output_file,
1285 "* S(CODE): shared clean memory, it includes"
1286 " duplicated memory\n"
1287 "* S(DATA): shared dirty memory, it includes"
1288 " duplicated memory\n"
1289 "* P(CODE): private clean memory\n"
1290 "* P(DATA): private dirty memory\n"
1291 "* PEAK: peak memory usage of S(CODE) + S(DATA)"
1292 " + P(CODE) + P(DATA)\n"
1293 "* PSS: Proportional Set Size\n"
1294 "* 3D: memory allocated by GPU driver\n"
1295 "* GEM(PSS): GEM memory divided by # of sharers\n"
1296 "* GEM(RSS): GEM memory including duplicated memory\n"
1297 "* GEM(ALLOC): sum of unique gem memory in the system\n"
1298 "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1300 fprintf(output_file,
1301 "* CODE: shared and private clean memory\n"
1302 "* DATA: shared and private dirty memory\n"
1303 "* PEAK: peak memory usage of CODE + DATA\n"
1304 "* PSS: Proportional Set Size\n"
1305 "* 3D: memory allocated by GPU driver\n"
1306 "* GEM(PSS): GEM memory divided by # of sharers\n"
1307 "* GEM(ALLOC): sum of unique GEM memory in the system\n"
1308 "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1310 get_tmpfs_info(output_file);
1311 get_memcg_info(output_file);
1312 get_mem_info(output_file);
1314 fclose(output_file);
1323 static int show_map_new(int pid)
1327 mapinfo *temp = NULL;
1328 unsigned shared_dirty = 0;
1329 unsigned shared_clean = 0;
1330 unsigned private_dirty = 0;
1331 unsigned private_clean = 0;
1333 unsigned long start = 0;
1334 unsigned long end = 0;
1335 unsigned private_clean_total = 0;
1336 unsigned private_dirty_total = 0;
1337 unsigned shared_clean_total = 0;
1338 unsigned shared_dirty_total = 0;
1339 int duplication = 0;
1341 milist = load_maps(pid);
1344 fprintf(stderr, "cannot get /proc/smaps for pid %d\n", pid);
1349 if (sizeof(unsigned long) == 4) {
1350 /* for 32-bit address */
1351 printf(" S(CODE) S(DATA) P(CODE) P(DATA) PSS "
1354 printf("-------- -------- -------- -------- -------- "
1355 "----------------- "
1356 "------------------------------\n");
1358 /* for 64-bit address */
1359 printf(" S(CODE) S(DATA) P(CODE) P(DATA) PSS "
1362 printf("-------- -------- -------- -------- -------- "
1363 "--------------------------------- "
1364 "------------------------------\n");
1367 printf(" S(CODE) S(DATA) P(CODE) P(DATA) PSS\n");
1368 printf("-------- -------- -------- -------- --------\n");
1370 for (mi = milist; mi;) {
1371 shared_clean += mi->shared_clean;
1372 shared_dirty += mi->shared_dirty;
1373 private_clean += mi->private_clean;
1374 private_dirty += mi->private_dirty;
1377 shared_clean_total += mi->shared_clean;
1378 shared_dirty_total += mi->shared_dirty;
1379 private_clean_total += mi->private_clean;
1380 private_dirty_total += mi->private_dirty;
1385 if ((mi->next && !strcmp(mi->next->name, mi->name)) &&
1386 (mi->next->start == mi->end)) {
1401 if (sizeof(unsigned long) == 4) {
1402 /* for 32-bit address */
1403 printf("%8d %8d %8d %8d %8d %08lx-%08lx %s\n",
1404 shared_clean, shared_dirty,
1405 private_clean, private_dirty, mi->pss,
1406 start, end, mi->name);
1408 /* for 64-bit address */
1409 printf("%8d %8d %8d %8d %8d %016lx-%016lx %s\n",
1410 shared_clean, shared_dirty,
1411 private_clean, private_dirty, mi->pss,
1412 start, end, mi->name);
1428 printf("%8d %8d %8d %8d %8d\n",
1431 private_clean_total,
1432 private_dirty_total,
1439 int main(int argc, char *argv[])
1444 pid_max = get_pid_max();
1447 if (!strncmp(argv[1], "-r", strlen("-r")+1)) {
1449 show_rss(OUTPUT_FILE, argv[2]);
1451 show_rss(OUTPUT_UART, NULL);
1453 } else if (!strncmp(argv[1], "-s", strlen("-s")+1)) {
1455 if (argc == 3 && atoi(argv[2]) > 0) {
1456 show_map_new(atoi(argv[2]));
1459 } else if (!strncmp(argv[1], "-a", strlen("-a")+1)) {
1461 show_map_all_new(OUTPUT_UART, NULL);
1463 } else if (!strncmp(argv[1], "-v", strlen("-v")+1)) {
1465 show_map_all_new(OUTPUT_UART, NULL);
1467 } else if (!strncmp(argv[1], "-f", strlen("-f")+1)) {
1470 show_map_all_new(OUTPUT_FILE, argv[2]);
1473 } else if (argc == 2 && atoi(argv[1]) > 0) {
1474 show_map_new(atoi(argv[1]));
1480 "memps [-a] | [-v] | [-r] | [-s] <pid> | <pid> | [-f] <output file full path>\n"
1481 " -s = sum (show only sum of each)\n"
1482 " -f = all (show all processes via output file)\n"
1483 " -a = all (show all processes)\n"
1484 " -r = all (show rss of all processes)\n"
1485 " -v = verbose (show all processes in detail)\n");