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;
120 unsigned int imported;
125 static int use_gpu_mem_info = 0;
126 static int pid_max = 0;
127 static int dri_card = 0;
129 /* reads file contents into memory */
130 static char* cread(const char* path)
132 /* once allocated area for reads */
133 static char* text = NULL;
134 static size_t size = 0;
139 int fd = open(path, O_RDONLY);
145 /* ensure we have enough space */
147 ptr = (char*)realloc(text, size + BUF_INC_SIZE);
156 size += BUF_INC_SIZE;
158 ret = read(fd, ptr, cap);
161 } else if (ret > 0) {
168 return (ret < 0 ? NULL : text);
171 /* like fgets/gets but adjusting contents pointer */
172 static inline char* cgets(char** contents)
174 if (contents && *contents && **contents) {
175 char* bos = *contents; /* begin of string */
176 char* eos = strchr(bos, '\n'); /* end of string */
190 /* get pid_max value */
191 static inline int get_pid_max(void)
193 static const char pid_max_path[] = "/proc/sys/kernel/pid_max";
196 line = cread(pid_max_path);
198 fprintf(stderr, "cannot open %s\n", pid_max_path);
202 return strtoul(line, NULL, 10);
206 static unsigned get_peak_rss(unsigned int pid)
208 static const char field[] = "VmHWM:";
213 snprintf(tmp, sizeof(tmp), "/proc/%d/status", pid);
216 fprintf(stderr, "cannot open %s\n", tmp);
220 value = strstr(line, field);
222 value += sizeof(field);
223 return strtoul(value, NULL, 10);
228 #define NUM_GEM_FIELD 7
230 static geminfo *read_geminfo(FILE *fp)
234 unsigned int pid, tgid, handle, refcount, hcount;
236 unsigned int imported;
238 if (fgets(line, BUF_MAX, fp) != NULL) {
239 if (sscanf(line, "%d %d %d %d %d 0x%x 0x%*x %*d %*d %d",
240 &pid, &tgid, &handle, &refcount,
241 &hcount, &gem_size, &imported) != NUM_GEM_FIELD)
246 tgeminfo = malloc(sizeof(geminfo));
247 if (tgeminfo == NULL)
249 tgeminfo->tgid = tgid;
250 tgeminfo->hcount = hcount;
251 tgeminfo->rss_size = KB(gem_size);
252 tgeminfo->pss_size = KB(gem_size/tgeminfo->hcount);
253 tgeminfo->imported = imported;
261 static geminfo *find_geminfo(unsigned int tgid, geminfo *gilist)
264 for (gi = gilist; gi; ) {
265 if (gi->tgid == tgid)
273 static geminfo *get_geminfo(FILE *drm_fp)
276 geminfo *gilist = NULL;
277 geminfo *exist_ginfo = NULL;
281 if (fgets(line, BUF_MAX, drm_fp) == NULL) {
284 /* we should count a number of whitespace separated fields */
285 int in_field = (line[0] && !isblank(line[0]));
286 unsigned int size = (unsigned)in_field;
287 const char* ptr = &line[1];
289 /* sscanf() was used in original code, so number of fields */
290 /* in string is expected to be at least NUM_GEM_FIELD */
291 while (*ptr && size < NUM_GEM_FIELD) {
292 if (isblank(*ptr++)) {
299 /* next field started */
306 if (size != NUM_GEM_FIELD) {
311 while ((ginfo = read_geminfo(drm_fp)) != NULL) {
312 if (gilist && ginfo->tgid == gilist->tgid) {
313 gilist->pss_size += ginfo->pss_size;
314 gilist->rss_size += ginfo->rss_size;
317 } else if (gilist && ((exist_ginfo = find_geminfo(ginfo->tgid, gilist)) != NULL)) {
318 exist_ginfo->pss_size += ginfo->pss_size;
319 exist_ginfo->rss_size += ginfo->rss_size;
323 ginfo->next = gilist;
330 static geminfo *load_geminfo(void)
335 drm_fp = fopen(STR_DRM_DBG_DIR "0/gem_info", "r");
336 if (drm_fp == NULL) {
337 drm_fp = fopen(STR_DRM_DBG_DIR "1/gem_info", "r");
339 if (drm_fp == NULL) {
341 "cannot open " STR_DRM_DBG_DIR "%d/gem_info\n",
347 gilist = get_geminfo(drm_fp);
354 static geminfo *load_gpu_geminfo(void)
359 /* Check the render dri card having gpu_gem_info */
360 drm_fp = fopen(STR_DRM_DBG_DIR "128/gpu_gem_info", "r");
361 if (drm_fp == NULL) {
363 "cannot open " STR_DRM_DBG_DIR "128/gpu_gem_info\n");
367 gilist = get_geminfo(drm_fp);
374 static void clear_geminfo(geminfo *glist)
377 geminfo *gelement = glist;
384 /* b6e82000-b6e83000 rw-p 00020000 b3:19 714 /usr/lib/ld-2.20-2014.11.so : TM1
385 * 7f9389d000-7f9389e000 rw-p 0001f000 b3:12 618 /usr/lib64/ld-2.20-2014.11.so : TM2
386 * 7fae2e4b2000-7fae2e4b3000 r--p 00021000 fe:01 603 /usr/lib64/ld-2.20-2014.11.so : x86-64 Emulator
387 * 01234567890123456789012345678901234567890123456789012345678901234567890123456789
390 mapinfo *read_mapinfo(char** smaps)
397 if ((line = cgets(smaps)) == 0)
404 mi = malloc(sizeof(mapinfo));
408 n = sscanf(line, "%lx-%lx %ms %*s %*s %*s %m[^\n]",
409 &mi->start, &mi->end, &mi->perm, &mi->name);
411 if (n == 3 && !mi->name)
412 mi->name = strndup("[anon]", strlen("[anon]"));
414 fprintf(stderr, "Fail to parse smaps\n");
419 while ((line = cgets(smaps))) {
420 if (sscanf(line, "Size: %d kB", &mi->size) == 1)
422 else if (sscanf(line, "Rss: %d kB", &mi->rss) == 1)
424 else if (sscanf(line, "Pss: %d kB", &mi->pss) == 1)
426 else if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) == 1)
428 else if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) == 1)
430 else if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) == 1)
432 else if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) == 1)
434 else if (sscanf(line, "Swap: %d kB", &mi->swap) == 1)
437 /* Drain lines until it meets next VMA address */
439 if ((next >= '0' && next <= '9') || (next >= 'a' && next <= 'f'))
447 static unsigned total_gem_memory(void)
450 unsigned total_gem_mem = 0;
451 unsigned name, size, handles, refcount;
455 gem_fp = fopen(STR_DRM_DBG_DIR "0/gem_names", "r");
457 gem_fp = fopen(STR_DRM_DBG_DIR "1/gem_names", "r");
458 if (gem_fp == NULL) {
460 "cannot open " STR_DRM_DBG_DIR "%d/gem_names\n", dri_card);
464 if (fgets(line, BUF_MAX, gem_fp) == NULL) {
469 while (fgets(line, BUF_MAX, gem_fp) != NULL) {
470 if (sscanf(line, "%d %d %d %d\n",
471 &name, &size, &handles, &refcount) == 4) {
472 if (total_gem_mem <= UINT_MAX - size) {
473 total_gem_mem += size;
479 return total_gem_mem;
482 int get_zram_used(u_int32_t *zram_used)
485 /* only read 3rd value */
486 char *fscanf_format = "%*d %*d %d %*d %*d %*d %*d %*d";
489 f = fopen(ZRAM_MM_STAT_PATH, "r");
492 * ZRAM_USED_PATH is deprecated on latest kernel, but to support
493 * old kernel try with that if fails new node 'mm_stat'.
495 f = fopen(ZRAM_USED_PATH, "r");
497 fprintf(stderr, "Fail to open zram file.\n");
500 fscanf_format = "%u";
503 ret = fscanf(f, fscanf_format, zram_used);
505 fprintf(stderr, "Fail to read file\n");
514 int fread_uint(const char *path, u_int32_t *number)
519 f = fopen(path, "r");
522 fprintf(stderr, "Fail to open %s file.\n", path);
526 ret = fscanf(f, "%u", number);
528 fprintf(stderr, "Fail to read file\n");
537 static int cgroup_read_node(const char *cgroup_name,
538 const char *file_name, unsigned int *value)
540 char buf[PATH_MAX + NAME_MAX];
542 snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name);
543 ret = fread_uint(buf, value);
548 * @desc Provides usage in bytes for provided memory cgroup. Works
549 * with/without swap accounting.
551 * @param memcg_path[in] Full path to memory cgroup
552 * @param swap[in] Boolean value for deciding if account usage with swap
553 * @return current cgroup usage in bytes or 0 on error
555 static unsigned int get_memcg_usage(const char *memcg_path, bool swap)
561 ret = cgroup_read_node(memcg_path,
562 "/memory.memsw.usage_in_bytes", &usage);
564 ret = cgroup_read_node(memcg_path, "/memory.usage_in_bytes",
574 static void get_memcg_info(FILE *output_fp)
578 struct dirent *entry;
579 struct stat path_stat;
581 unsigned long usage, usage_with_swap;
583 fprintf(output_fp, "====================================================================\n");
584 fprintf(output_fp, "MEMORY CGROUPS USAGE INFO\n");
586 pdir = opendir(MEMCG_PATH);
588 fprintf(stderr, "cannot read directory %s", MEMCG_PATH);
593 while ((entry = readdir(pdir)) != NULL && !errno) {
594 snprintf(buf, sizeof(buf), "%s/%s", MEMCG_PATH, entry->d_name);
595 /* If can't stat then ignore */
596 if (stat(buf, &path_stat) != 0)
599 /* If it's not directory or it's parent path then ignore */
600 if (!(S_ISDIR(path_stat.st_mode) &&
601 strncmp(entry->d_name, "..", 3)))
604 usage = get_memcg_usage(buf, false);
605 usage_with_swap = get_memcg_usage(buf, true);
606 /* It is posible by rounding errors to get negative value */
607 usage_swap = usage_with_swap - usage;
611 /* Case of root cgroup in hierarchy */
612 if (!strncmp(entry->d_name, ".", 2))
613 fprintf(output_fp, "%13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB) \n\n",
614 MEMCG_PATH, BYTE_TO_MBYTE(usage),
615 BYTE_TO_KBYTE(usage),
616 BYTE_TO_MBYTE(usage_with_swap),
617 BYTE_TO_KBYTE(usage_with_swap),
618 BYTE_TO_MBYTE(usage_swap),
619 BYTE_TO_KBYTE(usage_swap));
621 fprintf(output_fp, "memcg: %13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB)\n",
622 entry->d_name, BYTE_TO_MBYTE(usage),
623 BYTE_TO_KBYTE(usage),
624 BYTE_TO_MBYTE(usage_with_swap),
625 BYTE_TO_KBYTE(usage_with_swap),
626 BYTE_TO_MBYTE(usage_swap),
627 BYTE_TO_KBYTE(usage_swap));
634 static void get_mem_info(FILE *output_fp)
639 unsigned int free = 0, cached = 0;
640 unsigned int total_mem = 0, available = 0, used;
641 unsigned int swap_total = 0, swap_free = 0, zram_used, swap_used;
642 unsigned int used_ratio;
644 if (output_fp == NULL)
647 fp = fopen("/proc/meminfo", "r");
650 fprintf(stderr, "/proc/meminfo open failed, %p", fp);
654 while (fgets(buf, PATH_MAX, fp) != NULL) {
655 if ((idx = strstr(buf, "MemTotal:"))) {
656 idx += strlen("Memtotal:");
657 while ((idx < buf + PATH_MAX) && (*idx < '0' || *idx > '9'))
659 total_mem = atoi(idx);
660 } else if ((idx = strstr(buf, "MemFree:"))) {
661 idx += strlen("MemFree:");
662 while ((idx < buf + PATH_MAX) && (*idx < '0' || *idx > '9'))
665 } else if ((idx = strstr(buf, "MemAvailable:"))) {
666 idx += strlen("MemAvailable:");
667 while ((idx < buf + PATH_MAX) && (*idx < '0' || *idx > '9'))
669 available = atoi(idx);
670 } else if ((idx = strstr(buf, "Cached:")) && !strstr(buf, "Swap")) {
671 idx += strlen("Cached:");
672 while ((idx < buf + PATH_MAX) && (*idx < '0' || *idx > '9'))
675 } else if ((idx = strstr(buf, "SwapTotal:"))) {
676 idx += strlen("SwapTotal:");
677 while ((idx < buf + PATH_MAX) && (*idx < '0' || *idx > '9'))
679 swap_total = atoi(idx);
680 } else if ((idx = strstr(buf, "SwapFree:"))) {
681 idx += strlen("SwapFree");
682 while ((idx < buf + PATH_MAX) && (*idx < '0' || *idx > '9'))
684 swap_free = atoi(idx);
689 if (total_mem == 0) {
690 fprintf(stderr, "cannot get total memory size\n");
696 available = free + cached;
697 used = total_mem - available;
698 used_ratio = used * 100 / total_mem;
699 swap_used = swap_total - swap_free;
701 if (get_zram_used(&zram_used) < 0)
705 "====================================================================\n");
708 fprintf(output_fp, "Total RAM size: \t%15d MB( %6d kB)\n",
709 total_mem >> 10, total_mem);
711 fprintf(output_fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n",
712 (total_mem - free) >> 10, total_mem - free);
714 fprintf(output_fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n",
717 fprintf(output_fp, "Used (Mem): \t\t%15d MB( %6d kB)\n",
720 fprintf(output_fp, "Used (Swap): \t\t%15d MB( %6d kB)\n",
721 swap_used >> 10, swap_used);
723 fprintf(output_fp, "Used (Zram block device): %13d MB( %6d kB)\n",
724 BYTE_TO_MBYTE(zram_used), BYTE_TO_KBYTE(zram_used));
726 fprintf(output_fp, "Used Ratio: \t\t%15d %%\n", used_ratio);
728 fprintf(output_fp, "Mem Free:\t\t%15d MB( %6d kB)\n",
731 fprintf(output_fp, "Available (Free+Reclaimable):%10d MB( %6d kB)\n",
737 static int get_tmpfs_info(FILE *output_fp)
741 char *tmpfs_mp; /* tmpfs mount point */
742 struct statfs tmpfs_info;
744 if (output_fp == NULL)
747 fp = fopen("/etc/mtab", "r");
752 "====================================================================\n");
753 fprintf(output_fp, "TMPFS INFO\n");
755 while (fgets(line, BUF_MAX, fp) != NULL) {
756 if (sscanf(line, "tmpfs %ms tmpfs", &tmpfs_mp) == 1) {
757 if (statfs(tmpfs_mp, &tmpfs_info) == 0) {
759 #ifndef __USE_FILE_OFFSET64
760 "tmpfs %16s Total %8ld KB, Used %8ld, Avail %8ld\n",
762 "tmpfs %16s Total %8"PRId64" KB, Used %8"PRId64", Avail %8"PRId64"\n",
765 /* 1 block is 4 KB */
766 tmpfs_info.f_blocks * 4,
767 (tmpfs_info.f_blocks - tmpfs_info.f_bfree) * 4,
768 tmpfs_info.f_bfree * 4);
778 static inline mapinfo *__load_maps(const char *path)
791 while ((mi = read_mapinfo(&smaps)) != 0) {
793 if ((!strcmp(mi->name, milist->name)
794 && (mi->name[0] != '['))) {
795 milist->size += mi->size;
796 milist->swap += mi->swap;
797 milist->rss += mi->rss;
798 milist->pss += mi->pss;
799 milist->shared_clean += mi->shared_clean;
800 milist->shared_dirty += mi->shared_dirty;
801 milist->private_clean += mi->private_clean;
802 milist->private_dirty += mi->private_dirty;
804 milist->end = mi->end;
805 strncpy(milist->perm, mi->perm, 4);
819 mapinfo *load_maps_rollup(int pid)
824 * use smaps_rollup instead of traversing smaps
825 * for getting values quickly
827 snprintf(tmp, sizeof(tmp), "/proc/%d/smaps_rollup", pid);
828 tmp[sizeof(tmp) - 1] = '\0';
829 if (access(tmp, F_OK) < 0)
831 return __load_maps(tmp);
834 mapinfo *load_maps(int pid)
838 snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
839 tmp[sizeof(tmp) - 1] = '\0';
840 return __load_maps(tmp);
843 static void init_trib_mapinfo(trib_mapinfo *tmi)
847 tmi->shared_clean = 0;
848 tmi->shared_dirty = 0;
849 tmi->private_clean = 0;
850 tmi->private_dirty = 0;
852 tmi->shared_clean_pss = 0;
853 tmi->shared_dirty_pss = 0;
861 tmi->other_devices = 0;
863 tmi->render_gem_mmap = 0;
866 unsigned int get_graphic_3d_meminfo(unsigned int tgid)
868 char command[256], buf[256];
870 unsigned int size = 0;
874 snprintf(command, sizeof(command), "%s %d", STR_3D_UNIFIED_PATH, tgid);
875 p_gpu = popen(command, "r");
877 if (fgets(buf, 256, p_gpu)) {
878 ret = sscanf(buf, "%d %ms %d %ms", &tid, &tmp[0], &size, &tmp[1]);
890 get_trib_mapinfo(unsigned int tgid, mapinfo *milist,
891 geminfo *gilist, geminfo *gpu_glist,
892 trib_mapinfo *result)
896 mapinfo *temp = NULL;
902 init_trib_mapinfo(result);
904 if (use_gpu_mem_info)
905 result->graphic_3d = get_graphic_3d_meminfo(tgid);
907 for (mi = milist; mi;) {
908 if (!use_gpu_mem_info && strstr(mi->name, STR_SGX_PATH)) {
909 result->graphic_3d += mi->pss;
910 } else if (!use_gpu_mem_info && (strstr(mi->name, STR_3D_PATH1) ||
911 strstr(mi->name, STR_3D_PATH2))) {
912 result->graphic_3d += mi->size;
913 } else if (mi->rss != 0 && mi->pss == 0
914 && mi->shared_clean == 0
915 && mi->shared_dirty == 0
916 && mi->private_clean == 0
917 && mi->private_dirty == 0
919 result->other_devices += mi->size;
920 } else if (!strncmp(mi->name, STR_DRM_PATH1,
921 sizeof(STR_DRM_PATH1)) ||
922 !strncmp(mi->name, STR_DRM_PATH2,
923 sizeof(STR_DRM_PATH2))) {
924 result->gem_mmap += mi->rss;
925 } else if (!strncmp(mi->name, STR_DRM_RENDER_PATH,
926 sizeof(STR_DRM_RENDER_PATH))) {
927 result->render_gem_mmap += mi->rss;
929 result->shared_clean += mi->shared_clean;
930 result->shared_dirty += mi->shared_dirty;
931 result->private_clean += mi->private_clean;
932 result->private_dirty += mi->private_dirty;
933 result->swap += mi->swap;
934 result->rss += mi->rss;
935 result->pss += mi->pss;
936 result->size += mi->size;
938 if (mi->shared_clean != 0)
939 result->shared_clean_pss += mi->pss;
940 else if (mi->shared_dirty != 0)
941 result->shared_dirty_pss += mi->pss;
952 result->peak_rss = get_peak_rss(tgid);
953 if (result->peak_rss < result->rss)
954 result->peak_rss = result->rss;
955 if (result->gem_mmap > 0)
956 result->peak_rss -= result->gem_mmap;
957 if (result->render_gem_mmap > 0)
958 result->peak_rss -= result->render_gem_mmap;
960 gi = find_geminfo(tgid, gilist);
962 result->gem_rss = gi->rss_size;
963 result->gem_pss = gi->pss_size;
968 * drm render has gpu_gem_info, then use gpua gem pss
969 * as graphic 3d memory
971 gi = find_geminfo(tgid, gpu_glist);
973 result->graphic_3d += gi->pss_size;
979 static int get_cmdline(unsigned int pid, char *cmdline)
982 char buf[NAME_MAX] = {0, };
985 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
986 fp = fopen(buf, "r");
990 fprintf(stderr, "cannot file open %s\n", buf);
993 if ((ret = fscanf(fp, "%4095s", cmdline)) < 1) {
1002 static int get_oomscoreadj(unsigned int pid, const char *oom_string)
1008 snprintf(tmp, sizeof(tmp), "/proc/%d/%s", pid, oom_string);
1009 fp = fopen(tmp, "r");
1015 if (fgets(tmp, sizeof(tmp), fp) == NULL) {
1021 oomadj_val = atoi(tmp);
1027 static void get_rss(pid_t pid, unsigned long *result)
1030 char proc_path[PATH_MAX];
1031 unsigned long rss = 0;
1035 snprintf(proc_path, sizeof(proc_path), "/proc/%d/statm", pid);
1036 fp = fopen(proc_path, "r");
1040 if (fscanf(fp, "%*s %ld", &rss) < 1) {
1047 /* convert page to Kb */
1052 static const char* get_readable_oom_path(void)
1057 snprintf(tmp, sizeof(tmp), "/proc/1/%s", OOM_SCORE_ADJ_STR);
1058 fp = fopen(tmp, "r");
1061 return OOM_SCORE_ADJ_STR;
1063 snprintf(tmp, sizeof(tmp), "/proc/1/%s", OOM_SCORE_STR);
1064 fp = fopen(tmp, "r");
1067 return OOM_SCORE_STR;
1072 static void show_rss(int output_type, char *output_path)
1075 struct dirent *curdir;
1077 char cmdline[PATH_MAX];
1078 FILE *output_file = NULL;
1081 const char *oom_path = NULL;
1083 oom_path = get_readable_oom_path();
1085 fprintf(stderr, "there is no readable oom path\n");
1089 pDir = opendir("/proc");
1091 fprintf(stderr, "cannot read directory /proc.\n");
1095 if (output_type == OUTPUT_FILE && output_path) {
1096 output_file = fopen(output_path, "w+");
1098 fprintf(stderr, "cannot open output file(%s)\n",
1104 output_file = stdout;
1107 fprintf(output_file,
1108 " PID RSS %14s COMMAND\n", oom_path);
1111 while ((curdir = readdir(pDir)) != NULL && !errno) {
1112 pid = atoi(curdir->d_name);
1113 if (pid < 1 || pid > pid_max || pid == getpid())
1116 if (get_cmdline(pid, cmdline) < 0)
1119 oom_score_adj = get_oomscoreadj(pid, oom_path);
1121 fprintf(output_file,
1122 "%8d %8lu %8d %s\n",
1129 } /* end of while */
1131 get_tmpfs_info(output_file);
1132 get_mem_info(output_file);
1134 fclose(output_file);
1140 static int show_map_all_new(int output_type, char *output_path, bool use_rollup)
1143 struct dirent *curdir;
1147 unsigned total_pss = 0;
1148 unsigned total_private = 0;
1149 unsigned total_private_code = 0;
1150 unsigned total_private_data = 0;
1151 unsigned total_shared_code = 0;
1152 unsigned total_shared_data = 0;
1153 unsigned total_shared_code_pss = 0;
1154 unsigned total_shared_data_pss = 0;
1155 unsigned total_swap = 0;
1156 unsigned total_rss = 0;
1157 unsigned total_graphic_3d = 0;
1158 unsigned total_gem_rss = 0;
1159 unsigned total_gem_pss = 0;
1160 unsigned total_peak_rss = 0;
1161 unsigned total_allocated_gem = 0;
1163 char cmdline[PATH_MAX];
1164 FILE *output_file = NULL;
1166 const char *oom_path = NULL;
1168 oom_path = get_readable_oom_path();
1170 fprintf(stderr, "there is no readable oom path\n");
1174 use_gpu_mem_info = (!access(STR_3D_UNIFIED_PATH, F_OK | X_OK)) ? 1 : 0;
1177 pDir = opendir("/proc");
1179 fprintf(stderr, "cannot read directory /proc.\n");
1183 if (output_type == OUTPUT_FILE && output_path) {
1184 output_file = fopen(output_path, "w+");
1186 fprintf(stderr, "cannot open output file(%s)\n",
1192 output_file = stdout;
1194 glist = load_geminfo();
1195 gpu_glist = load_gpu_geminfo();
1199 fprintf(output_file,
1200 " PID S(CODE) S(DATA) P(CODE) P(DATA) "
1201 " PEAK PSS 3D GEM(PSS) GEM(RSS) "
1202 " SWAP %14s COMMAND\n", oom_path);
1204 fprintf(output_file,
1205 " PID CODE DATA PEAK PSS "
1206 " 3D GEM(PSS) SWAP COMMAND\n");
1210 while ((curdir = readdir(pDir)) != NULL && !errno) {
1211 mapinfo *milist = 0;
1213 pid = atoi(curdir->d_name);
1214 if (pid < 1 || pid > pid_max || pid == getpid())
1217 if (get_cmdline(pid, cmdline) < 0)
1221 milist = load_maps_rollup(pid);
1224 /* fall back to the default lookup path */
1225 milist = load_maps(pid);
1230 /* get classified map info, milist will be freed */
1231 get_trib_mapinfo(pid, milist, glist, gpu_glist, &tmi);
1232 oom_score_adj = get_oomscoreadj(pid, oom_path);
1236 fprintf(output_file,
1237 "%8d %8d %8d %8d %8d "
1238 "%8d %8d %8d %8d %8d "
1254 fprintf(output_file,
1255 "%8d %8d %8d %8d %8d "
1258 tmi.shared_clean + tmi.private_clean,
1259 tmi.shared_dirty + tmi.private_dirty,
1267 if (tmi.other_devices != 0)
1268 fprintf(output_file,
1269 "%s(%d) %d KB may mapped by device(s).\n",
1270 cmdline, pid, tmi.other_devices);
1273 total_private += (tmi.private_clean + tmi.private_dirty);
1274 total_pss += tmi.pss;
1275 total_rss += tmi.rss;
1276 total_graphic_3d += tmi.graphic_3d;
1277 total_gem_rss += tmi.gem_rss;
1278 total_gem_pss += tmi.gem_pss;
1279 total_private_code += tmi.private_clean;
1280 total_private_data += tmi.private_dirty;
1281 total_swap += tmi.swap;
1282 total_shared_code += tmi.shared_clean;
1283 total_shared_data += tmi.shared_dirty;
1284 total_peak_rss += tmi.peak_rss;
1286 total_shared_code_pss += tmi.shared_clean_pss;
1287 total_shared_data_pss += tmi.shared_dirty_pss;
1289 } /* end of while */
1291 total_allocated_gem = KB(total_gem_memory());
1292 fprintf(output_file,
1293 "==============================================="
1294 "===============================================\n");
1296 fprintf(output_file,
1297 "TOTAL: S(CODE) S(DATA) P(CODE) P(DATA) "
1298 " PEAK PSS 3D GEM(PSS) GEM(RSS) "
1299 "GEM(ALLOC) SWAP TOTAL(KB)\n");
1300 fprintf(output_file,
1302 "%8d %8d %8d %8d %8d "
1304 total_shared_code, total_shared_data,
1305 total_private_code, total_private_data,
1306 total_peak_rss, total_pss, total_graphic_3d,
1307 total_gem_pss, total_gem_rss,
1308 total_allocated_gem, total_swap,
1309 total_pss + total_graphic_3d +
1310 total_allocated_gem);
1312 fprintf(output_file,
1313 "TOTAL: CODE DATA PEAK PSS "
1314 " 3D GEM(PSS) GEM(ALLOC) SWAP TOTAL(KB)\n");
1315 fprintf(output_file,
1317 "%8d %8d %10d %8d %10d\n",
1318 total_shared_code + total_private_code,
1319 total_shared_data + total_private_data,
1320 total_peak_rss, total_pss,
1321 total_graphic_3d, total_gem_pss,
1322 total_allocated_gem, total_swap,
1323 total_pss + total_graphic_3d +
1324 total_allocated_gem);
1329 fprintf(output_file,
1330 "* S(CODE): shared clean memory, it includes"
1331 " duplicated memory\n"
1332 "* S(DATA): shared dirty memory, it includes"
1333 " duplicated memory\n"
1334 "* P(CODE): private clean memory\n"
1335 "* P(DATA): private dirty memory\n"
1336 "* PEAK: peak memory usage of S(CODE) + S(DATA)"
1337 " + P(CODE) + P(DATA)\n"
1338 "* PSS: Proportional Set Size\n"
1339 "* 3D: memory allocated by GPU driver\n"
1340 "* GEM(PSS): GEM memory divided by # of sharers\n"
1341 "* GEM(RSS): GEM memory including duplicated memory\n"
1342 "* GEM(ALLOC): sum of unique gem memory in the system\n"
1343 "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1345 fprintf(output_file,
1346 "* CODE: shared and private clean memory\n"
1347 "* DATA: shared and private dirty memory\n"
1348 "* PEAK: peak memory usage of CODE + DATA\n"
1349 "* PSS: Proportional Set Size\n"
1350 "* 3D: memory allocated by GPU driver\n"
1351 "* GEM(PSS): GEM memory divided by # of sharers\n"
1352 "* GEM(ALLOC): sum of unique GEM memory in the system\n"
1353 "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1355 get_tmpfs_info(output_file);
1356 get_memcg_info(output_file);
1357 get_mem_info(output_file);
1359 fclose(output_file);
1360 clear_geminfo(glist);
1361 clear_geminfo(gpu_glist);
1366 static int show_map_new(int pid)
1370 mapinfo *temp = NULL;
1371 unsigned shared_dirty = 0;
1372 unsigned shared_clean = 0;
1373 unsigned private_dirty = 0;
1374 unsigned private_clean = 0;
1377 unsigned long start = 0;
1378 unsigned long end = 0;
1379 unsigned private_clean_total = 0;
1380 unsigned private_dirty_total = 0;
1381 unsigned shared_clean_total = 0;
1382 unsigned shared_dirty_total = 0;
1383 int duplication = 0;
1385 milist = load_maps(pid);
1388 fprintf(stderr, "cannot get /proc/smaps for pid %d\n", pid);
1393 if (sizeof(unsigned long) == 4) {
1394 /* for 32-bit address */
1395 printf(" S(CODE) S(DATA) P(CODE) P(DATA) PSS SWAP "
1398 printf("-------- -------- -------- -------- -------- -------- "
1399 "----------------- "
1400 "------------------------------\n");
1402 /* for 64-bit address */
1403 printf(" S(CODE) S(DATA) P(CODE) P(DATA) PSS SWAP "
1406 printf("-------- -------- -------- -------- -------- -------- "
1407 "--------------------------------- "
1408 "------------------------------\n");
1411 printf(" S(CODE) S(DATA) P(CODE) P(DATA) PSS SWAP\n");
1412 printf("-------- -------- -------- -------- -------- --------\n");
1414 for (mi = milist; mi;) {
1415 shared_clean += mi->shared_clean;
1416 shared_dirty += mi->shared_dirty;
1417 private_clean += mi->private_clean;
1418 private_dirty += mi->private_dirty;
1422 shared_clean_total += mi->shared_clean;
1423 shared_dirty_total += mi->shared_dirty;
1424 private_clean_total += mi->private_clean;
1425 private_dirty_total += mi->private_dirty;
1430 if ((mi->next && !strcmp(mi->next->name, mi->name)) &&
1431 (mi->next->start == mi->end)) {
1446 if (sizeof(unsigned long) == 4) {
1447 /* for 32-bit address */
1448 printf("%8d %8d %8d %8d %8d %8d %08lx-%08lx %s\n",
1449 shared_clean, shared_dirty,
1450 private_clean, private_dirty, mi->pss, mi->swap,
1451 start, end, mi->name);
1453 /* for 64-bit address */
1454 printf("%8d %8d %8d %8d %8d %8d %016lx-%016lx %s\n",
1455 shared_clean, shared_dirty,
1456 private_clean, private_dirty, mi->pss, mi->swap,
1457 start, end, mi->name);
1473 printf("%8d %8d %8d %8d %8d %8d\n",
1476 private_clean_total,
1477 private_dirty_total,
1485 int main(int argc, char *argv[])
1490 pid_max = get_pid_max();
1493 if (!strncmp(argv[1], "-r", strlen("-r")+1)) {
1495 show_rss(OUTPUT_FILE, argv[2]);
1497 show_rss(OUTPUT_UART, NULL);
1499 } else if (!strncmp(argv[1], "-s", strlen("-s")+1)) {
1501 if (argc == 3 && atoi(argv[2]) > 0) {
1502 show_map_new(atoi(argv[2]));
1505 } else if (!strncmp(argv[1], "-a", strlen("-a")+1)) {
1507 show_map_all_new(OUTPUT_UART, NULL, false);
1509 } else if (!strncmp(argv[1], "-v", strlen("-v")+1)) {
1511 show_map_all_new(OUTPUT_UART, NULL, false);
1513 } else if (!strncmp(argv[1], "-f", strlen("-f")+1)) {
1516 show_map_all_new(OUTPUT_FILE, argv[2], false);
1519 } else if (!strncmp(argv[1], "-q", strlen("-q")+1)) {
1521 show_map_all_new(OUTPUT_UART, NULL, true);
1523 } else if (argc == 2 && atoi(argv[1]) > 0) {
1524 show_map_new(atoi(argv[1]));
1530 "memps [-a] | [-v] | [-q] | [-r] | [-s] <pid> | <pid> | [-f] <output file full path>\n"
1531 " -s = sum (show only sum of each)\n"
1532 " -f = all (show all processes via output file)\n"
1533 " -a = all (show all processes)\n"
1534 " -r = all (show rss of all processes)\n"
1535 " -v = verbose (show all processes in detail)\n"
1536 " -q = verbose (experimental - quickly show all processes in detail)\n");