1 /* Copyright 2014 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>
33 #include <sys/utsname.h>
35 #define STR_SGX_PATH "/dev/pvrsrvkm"
36 #define STR_3D_PATH1 "/dev/mali"
37 #define STR_3D_PATH2 "/dev/kgsl-3d0"
38 #define STR_DRM_PATH1 "/drm mm object (deleted)"
39 #define STR_DRM_PATH2 "/dev/dri/card0"
40 #define MEMCG_PATH "/sys/fs/cgroup/memory"
41 #define ZRAM_USED_PATH "/sys/block/zram0/mem_used_total"
42 #define OOM_SCORE_ADJ_STR "oom_score_adj"
43 #define OOM_SCORE_STR "oom_score"
45 #define BUF_MAX (BUFSIZ) /* most optimal for libc::stdio */
46 #define BUF_INC_SIZE (512 * 1024) /* maximal SMAPS I saw 2 MB */
47 #define KB(bytes) ((bytes)/1024)
49 #define BYTE_TO_KBYTE(b) ((b) >> 10)
50 #define BYTE_TO_MBYTE(b) ((b) >> 20)
51 #define BYTE_TO_GBYTE(b) ((b) >> 30)
53 #define KBYTE_TO_BYTE(k) ((k) << 10)
54 #define KBYTE_TO_MBYTE(k) ((k) >> 10)
55 #define KBYTE_TO_GBYTE(k) ((k) >> 20)
57 #define GBYTE_TO_BYTE(g) ((g) << 30)
58 #define GBYTE_TO_KBYTE(g) ((g) << 20)
59 #define GBYTE_TO_MBYTE(g) ((g) << 10)
61 typedef struct geminfo geminfo;
62 typedef struct mapinfo mapinfo;
63 typedef struct trib_mapinfo trib_mapinfo;
79 unsigned shared_clean;
80 unsigned shared_dirty;
81 unsigned private_clean;
82 unsigned private_dirty;
87 /* classify normal, graphic and other devices memory */
89 unsigned shared_clean;
90 unsigned shared_dirty;
91 unsigned private_clean;
92 unsigned private_dirty;
93 unsigned shared_clean_pss;
94 unsigned shared_dirty_pss;
103 unsigned other_devices;
118 /* reads file contents into memory */
119 static char* cread(const char* path)
121 /* once allocated area for reads */
122 static char* text = NULL;
123 static size_t size = 0;
128 int fd = open(path, O_RDONLY);
134 /* ensure we have enough space */
136 ptr = (char*)realloc(text, size + BUF_INC_SIZE);
145 size += BUF_INC_SIZE;
147 ret = read(fd, ptr, cap);
150 } else if (ret > 0) {
157 return (ret < 0 ? NULL : text);
160 /* like fgets/gets but adjusting contents pointer */
161 static inline char* cgets(char** contents)
163 if (contents && *contents && **contents) {
164 char* bos = *contents; /* begin of string */
165 char* eos = strchr(bos, '\n'); /* end of string */
180 static unsigned get_peak_rss(unsigned int pid)
182 static const char field[] = "VmHWM:";
187 snprintf(tmp, sizeof(tmp), "/proc/%d/status", pid);
190 fprintf(stderr, "cannot open %s\n", tmp);
194 value = strstr(line, field);
196 value += sizeof(field);
197 return strtoul(value, NULL, 10);
202 #define NUM_GEM_FIELD 6
204 static geminfo *read_geminfo(FILE *fp)
208 unsigned int pid, tgid, handle, refcount, hcount;
211 if (fgets(line, BUF_MAX, fp) != NULL) {
212 if (sscanf(line, "%d %d %d %d %d 0x%x",
213 &pid, &tgid, &handle, &refcount,
214 &hcount, &gem_size) != NUM_GEM_FIELD)
219 tgeminfo = malloc(sizeof(geminfo));
220 if (tgeminfo == NULL)
222 tgeminfo->tgid = tgid;
223 tgeminfo->hcount = hcount;
224 tgeminfo->rss_size = KB(gem_size);
225 tgeminfo->pss_size = KB(gem_size/tgeminfo->hcount);
233 static geminfo *find_geminfo(unsigned int tgid, geminfo *gilist)
236 for (gi = gilist; gi; ) {
237 if (gi->tgid == tgid)
245 static geminfo *load_geminfo(void)
248 geminfo *gilist = NULL;
249 geminfo *exist_ginfo = NULL;
254 drm_fp = fopen("/sys/kernel/debug/dri/0/gem_info", "r");
256 if (drm_fp == NULL) {
258 "cannot open /sys/kernel/debug/dri/0/gem_info\n");
262 if (fgets(line, BUF_MAX, drm_fp) == NULL) {
266 /* we should count a number of whitespace separated fields */
267 int in_field = (line[0] && !isblank(line[0]));
268 unsigned int size = (unsigned)in_field;
269 const char* ptr = &line[1];
271 /* sscanf() was used in original code, so number of fields */
272 /* in string is expected to be at least NUM_GEM_FIELD */
273 while (*ptr && size < NUM_GEM_FIELD) {
274 if (isblank(*ptr++)) {
281 /* next field started */
288 if (size != NUM_GEM_FIELD) {
294 while ((ginfo = read_geminfo(drm_fp)) != NULL) {
295 if (gilist && ginfo->tgid == gilist->tgid) {
296 gilist->pss_size += ginfo->pss_size;
297 gilist->rss_size += ginfo->rss_size;
300 } else if (gilist && ((exist_ginfo = find_geminfo(ginfo->tgid, gilist)) != NULL)) {
301 exist_ginfo->pss_size += ginfo->pss_size;
302 exist_ginfo->rss_size += ginfo->rss_size;
306 ginfo->next = gilist;
316 /* b6e82000-b6e83000 rw-p 00020000 b3:19 714 /usr/lib/ld-2.20-2014.11.so : TM1
317 * 7f9389d000-7f9389e000 rw-p 0001f000 b3:12 618 /usr/lib64/ld-2.20-2014.11.so : TM2
318 * 7fae2e4b2000-7fae2e4b3000 r--p 00021000 fe:01 603 /usr/lib64/ld-2.20-2014.11.so : x86-64 Emulator
319 * 01234567890123456789012345678901234567890123456789012345678901234567890123456789
322 mapinfo *read_mapinfo(char** smaps)
329 if ((line = cgets(smaps)) == 0)
336 mi = malloc(sizeof(mapinfo));
340 n = sscanf(line, "%lx-%lx %ms %*s %*s %*s %m[^\n]",
341 &mi->start, &mi->end, &mi->perm, &mi->name);
343 if (n == 3 && !mi->name)
344 mi->name = strndup("[anon]", strlen("[anon]"));
346 fprintf(stderr, "Fail to parse smaps\n");
351 while ((line = cgets(smaps))) {
352 if (sscanf(line, "Size: %d kB", &mi->size) == 1)
354 else if (sscanf(line, "Rss: %d kB", &mi->rss) == 1)
356 else if (sscanf(line, "Pss: %d kB", &mi->pss) == 1)
358 else if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) == 1)
360 else if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) == 1)
362 else if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) == 1)
364 else if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) == 1)
366 else if (sscanf(line, "Swap: %d kB", &mi->swap) == 1)
369 /* Drain lines until it meets next VMA address */
371 if ((next >= '0' && next <= '9') || (next >= 'a' && next <= 'f'))
379 static unsigned total_gem_memory(void)
382 unsigned total_gem_mem = 0;
383 unsigned name, size, handles, refcount;
386 gem_fp = fopen("/sys/kernel/debug/dri/0/gem_names", "r");
387 if (gem_fp == NULL) {
389 "cannot open /sys/kernel/debug/dri/0/gem_names\n");
393 if (fgets(line, BUF_MAX, gem_fp) == NULL) {
398 while (fgets(line, BUF_MAX, gem_fp) != NULL)
399 if (sscanf(line, "%d %d %d %d\n",
400 &name, &size, &handles, &refcount) == 4)
401 total_gem_mem += size;
404 return total_gem_mem;
408 int fread_uint(const char *path, u_int32_t *number)
413 f = fopen(path, "r");
416 fprintf(stderr, "Fail to open %s file.\n", path);
420 ret = fscanf(f, "%u", number);
422 fprintf(stderr, "Fail to read file\n");
431 #define MAX_PATH_LENGTH 512
432 static int cgroup_read_node(const char *cgroup_name,
433 const char *file_name, unsigned int *value)
435 char buf[MAX_PATH_LENGTH];
437 snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name);
438 ret = fread_uint(buf, value);
443 * @desc Provides usage in bytes for provided memory cgroup. Works
444 * with/without swap accounting.
446 * @param memcg_path[in] Full path to memory cgroup
447 * @param swap[in] Boolean value for deciding if account usage with swap
448 * @return current cgroup usage in bytes or 0 on error
450 static unsigned int get_memcg_usage(const char *memcg_path, bool swap)
456 ret = cgroup_read_node(memcg_path,
457 "/memory.memsw.usage_in_bytes", &usage);
459 ret = cgroup_read_node(memcg_path, "/memory.usage_in_bytes",
469 static void get_memcg_info(FILE *output_fp)
473 struct dirent *entry;
474 struct stat path_stat;
476 unsigned long usage, usage_with_swap;
478 fprintf(output_fp, "====================================================================\n");
479 fprintf(output_fp, "MEMORY CGROUPS USAGE INFO\n");
481 pdir = opendir(MEMCG_PATH);
483 fprintf(stderr, "cannot read directory %s", MEMCG_PATH);
488 while ((entry = readdir(pdir)) != NULL && !errno) {
489 snprintf(buf, sizeof(buf), "%s/%s", MEMCG_PATH, entry->d_name);
490 /* If can't stat then ignore */
491 if (stat(buf, &path_stat) != 0)
494 /* If it's not directory or it's parent path then ignore */
495 if (!(S_ISDIR(path_stat.st_mode) &&
496 strncmp(entry->d_name, "..", 3)))
499 usage = get_memcg_usage(buf, false);
500 usage_with_swap = get_memcg_usage(buf, true);
501 /* It is posible by rounding errors to get negative value */
502 usage_swap = usage_with_swap - usage;
506 /* Case of root cgroup in hierarchy */
507 if (!strncmp(entry->d_name, ".", 2))
508 fprintf(output_fp, "%13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB) \n\n",
509 MEMCG_PATH, BYTE_TO_MBYTE(usage),
510 BYTE_TO_KBYTE(usage),
511 BYTE_TO_MBYTE(usage_with_swap),
512 BYTE_TO_KBYTE(usage_with_swap),
513 BYTE_TO_MBYTE(usage_swap),
514 BYTE_TO_KBYTE(usage_swap));
516 fprintf(output_fp, "memcg: %13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB)\n",
517 entry->d_name, BYTE_TO_MBYTE(usage),
518 BYTE_TO_KBYTE(usage),
519 BYTE_TO_MBYTE(usage_with_swap),
520 BYTE_TO_KBYTE(usage_with_swap),
521 BYTE_TO_MBYTE(usage_swap),
522 BYTE_TO_KBYTE(usage_swap));
529 static void get_mem_info(FILE *output_fp)
534 unsigned int free = 0, cached = 0;
535 unsigned int total_mem = 0, available = 0, used;
536 unsigned int swap_total = 0, swap_free = 0, zram_used, swap_used;
537 unsigned int used_ratio;
539 if (output_fp == NULL)
542 fp = fopen("/proc/meminfo", "r");
545 fprintf(stderr, "%s open failed, %p", buf, fp);
549 while (fgets(buf, PATH_MAX, fp) != NULL) {
550 if ((idx = strstr(buf, "MemTotal:"))) {
551 idx += strlen("Memtotal:");
552 while (*idx < '0' || *idx > '9')
554 total_mem = atoi(idx);
555 } else if ((idx = strstr(buf, "MemFree:"))) {
556 idx += strlen("MemFree:");
557 while (*idx < '0' || *idx > '9')
560 } else if ((idx = strstr(buf, "MemAvailable:"))) {
561 idx += strlen("MemAvailable:");
562 while (*idx < '0' || *idx > '9')
564 available = atoi(idx);
565 } else if ((idx = strstr(buf, "Cached:")) && !strstr(buf, "Swap")) {
566 idx += strlen("Cached:");
567 while (*idx < '0' || *idx > '9')
570 } else if ((idx = strstr(buf, "SwapTotal:"))) {
571 idx += strlen("SwapTotal:");
572 while (*idx < '0' || *idx > '9')
574 swap_total = atoi(idx);
575 } else if ((idx = strstr(buf, "SwapFree:"))) {
576 idx += strlen("SwapFree");
577 while (*idx < '0' || *idx > '9')
579 swap_free = atoi(idx);
584 if (total_mem == 0) {
585 fprintf(stderr, "cannot get total memory size\n");
591 available = free + cached;
592 used = total_mem - available;
593 used_ratio = used * 100 / total_mem;
594 swap_used = swap_total - swap_free;
596 if (fread_uint(ZRAM_USED_PATH, &zram_used) != 0)
600 "====================================================================\n");
603 fprintf(output_fp, "Total RAM size: \t%15d MB( %6d kB)\n",
604 total_mem >> 10, total_mem);
606 fprintf(output_fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n",
607 (total_mem - free) >> 10, total_mem - free);
609 fprintf(output_fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n",
612 fprintf(output_fp, "Used (Mem): \t\t%15d MB( %6d kB)\n",
615 fprintf(output_fp, "Used (Swap): \t\t%15d MB( %6d kB)\n",
616 swap_used >> 10, swap_used);
618 fprintf(output_fp, "Used (Zram block device): %13d MB( %6d kB)\n",
619 BYTE_TO_MBYTE(zram_used), BYTE_TO_KBYTE(zram_used));
621 fprintf(output_fp, "Used Ratio: \t\t%15d %%\n", used_ratio);
623 fprintf(output_fp, "Mem Free:\t\t%15d MB( %6d kB)\n",
626 fprintf(output_fp, "Available (Free+Reclaimable):%10d MB( %6d kB)\n",
632 static int get_tmpfs_info(FILE *output_fp)
636 char *tmpfs_mp; /* tmpfs mount point */
637 struct statfs tmpfs_info;
639 if (output_fp == NULL)
642 fp = fopen("/etc/mtab", "r");
647 "====================================================================\n");
648 fprintf(output_fp, "TMPFS INFO\n");
650 while (fgets(line, BUF_MAX, fp) != NULL) {
651 if (sscanf(line, "tmpfs %ms tmpfs", &tmpfs_mp) == 1) {
652 statfs(tmpfs_mp, &tmpfs_info);
654 "tmpfs %16s Total %8ld KB, Used %8ld, Avail %8ld\n",
656 /* 1 block is 4 KB */
657 tmpfs_info.f_blocks * 4,
658 (tmpfs_info.f_blocks - tmpfs_info.f_bfree) * 4,
659 tmpfs_info.f_bfree * 4);
667 mapinfo *load_maps(int pid)
674 snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
679 while ((mi = read_mapinfo(&smaps)) != 0) {
681 if ((!strcmp(mi->name, milist->name)
682 && (mi->name[0] != '['))) {
683 milist->size += mi->size;
684 milist->swap += mi->swap;
685 milist->rss += mi->rss;
686 milist->pss += mi->pss;
687 milist->shared_clean += mi->shared_clean;
688 milist->shared_dirty += mi->shared_dirty;
689 milist->private_clean += mi->private_clean;
690 milist->private_dirty += mi->private_dirty;
692 milist->end = mi->end;
693 strncpy(milist->perm, mi->perm, 4);
707 static void init_trib_mapinfo(trib_mapinfo *tmi)
711 tmi->shared_clean = 0;
712 tmi->shared_dirty = 0;
713 tmi->private_clean = 0;
714 tmi->private_dirty = 0;
716 tmi->shared_clean_pss = 0;
717 tmi->shared_dirty_pss = 0;
725 tmi->other_devices = 0;
730 get_trib_mapinfo(unsigned int tgid, mapinfo *milist,
731 geminfo *gilist, trib_mapinfo *result)
735 mapinfo *temp = NULL;
741 init_trib_mapinfo(result);
742 for (mi = milist; mi;) {
743 if (strstr(mi->name, STR_SGX_PATH)) {
744 result->graphic_3d += mi->pss;
745 } else if (strstr(mi->name, STR_3D_PATH1) ||
746 strstr(mi->name, STR_3D_PATH2)) {
747 result->graphic_3d += mi->size;
748 } else if (mi->rss != 0 && mi->pss == 0
749 && mi->shared_clean == 0
750 && mi->shared_dirty == 0
751 && mi->private_clean == 0
752 && mi->private_dirty == 0
754 result->other_devices += mi->size;
755 } else if (!strncmp(mi->name, STR_DRM_PATH1,
756 sizeof(STR_DRM_PATH1)) ||
757 !strncmp(mi->name, STR_DRM_PATH2,
758 sizeof(STR_DRM_PATH2))) {
759 result->gem_mmap += mi->rss;
761 result->shared_clean += mi->shared_clean;
762 result->shared_dirty += mi->shared_dirty;
763 result->private_clean += mi->private_clean;
764 result->private_dirty += mi->private_dirty;
765 result->swap += mi->swap;
766 result->rss += mi->rss;
767 result->pss += mi->pss;
768 result->size += mi->size;
770 if (mi->shared_clean != 0)
771 result->shared_clean_pss += mi->pss;
772 else if (mi->shared_dirty != 0)
773 result->shared_dirty_pss += mi->pss;
784 result->peak_rss = get_peak_rss(tgid);
785 if (result->peak_rss < result->rss)
786 result->peak_rss = result->rss;
787 if (result->gem_mmap > 0)
788 result->peak_rss -= result->gem_mmap;
790 gi = find_geminfo(tgid, gilist);
792 result->gem_rss = gi->rss_size;
793 result->gem_pss = gi->pss_size;
799 static int get_cmdline(unsigned int pid, char *cmdline)
802 char buf[NAME_MAX] = {0, };
805 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
806 fp = fopen(buf, "r");
808 fprintf(stderr, "cannot file open %s\n", buf);
811 if ((ret = fscanf(fp, "%4095s", cmdline)) < 1) {
820 static int get_oomscoreadj(unsigned int pid, const char *oom_string)
826 snprintf(tmp, sizeof(tmp), "/proc/%d/%s", pid, oom_string);
827 fp = fopen(tmp, "r");
833 if (fgets(tmp, sizeof(tmp), fp) == NULL) {
839 oomadj_val = atoi(tmp);
845 static void get_rss(pid_t pid, unsigned long *result)
848 char proc_path[PATH_MAX];
849 unsigned long rss = 0;
853 snprintf(proc_path, sizeof(proc_path), "/proc/%d/statm", pid);
854 fp = fopen(proc_path, "r");
858 if (fscanf(fp, "%*s %ld", &rss) < 1) {
865 /* convert page to Kb */
870 static const char* get_readable_oom_path(void)
875 snprintf(tmp, sizeof(tmp), "/proc/1/%s", OOM_SCORE_ADJ_STR);
876 fp = fopen(tmp, "r");
879 return OOM_SCORE_ADJ_STR;
881 snprintf(tmp, sizeof(tmp), "/proc/1/%s", OOM_SCORE_STR);
882 fp = fopen(tmp, "r");
885 return OOM_SCORE_STR;
890 static void show_rss(int output_type, char *output_path)
893 struct dirent *curdir;
895 char cmdline[PATH_MAX];
896 FILE *output_file = NULL;
899 const char *oom_path = NULL;
901 oom_path = get_readable_oom_path();
903 fprintf(stderr, "there is no readable oom path\n");
907 pDir = opendir("/proc");
909 fprintf(stderr, "cannot read directory /proc.\n");
913 if (output_type == OUTPUT_FILE && output_path) {
914 output_file = fopen(output_path, "w+");
916 fprintf(stderr, "cannot open output file(%s)\n",
922 output_file = stdout;
926 " PID RSS %s COMMAND\n", oom_path);
929 while ((curdir = readdir(pDir)) != NULL && !errno) {
930 pid = atoi(curdir->d_name);
931 if (pid < 1 || pid > 32768 || pid == getpid())
934 if (get_cmdline(pid, cmdline) < 0)
937 oom_score_adj = get_oomscoreadj(pid, oom_path);
949 get_tmpfs_info(output_file);
950 get_mem_info(output_file);
958 static int show_map_all_new(int output_type, char *output_path)
961 struct dirent *curdir;
965 unsigned total_pss = 0;
966 unsigned total_private = 0;
967 unsigned total_private_code = 0;
968 unsigned total_private_data = 0;
969 unsigned total_shared_code = 0;
970 unsigned total_shared_data = 0;
971 unsigned total_shared_code_pss = 0;
972 unsigned total_shared_data_pss = 0;
973 unsigned total_swap = 0;
974 unsigned total_rss = 0;
975 unsigned total_graphic_3d = 0;
976 unsigned total_gem_rss = 0;
977 unsigned total_gem_pss = 0;
978 unsigned total_peak_rss = 0;
979 unsigned total_allocated_gem = 0;
981 char cmdline[PATH_MAX];
982 FILE *output_file = NULL;
984 const char *oom_path = NULL;
986 oom_path = get_readable_oom_path();
988 fprintf(stderr, "there is no readable oom path\n");
992 pDir = opendir("/proc");
994 fprintf(stderr, "cannot read directory /proc.\n");
998 if (output_type == OUTPUT_FILE && output_path) {
999 output_file = fopen(output_path, "w+");
1001 fprintf(stderr, "cannot open output file(%s)\n",
1007 output_file = stdout;
1009 glist = load_geminfo();
1013 fprintf(output_file,
1014 " PID S(CODE) S(DATA) P(CODE) P(DATA)"
1016 " GEM(PSS) GEM(RSS) SWAP"
1017 " %s COMMAND\n", oom_path);
1019 fprintf(output_file,
1020 " PID CODE DATA PEAK PSS"
1021 " 3D GEM(PSS) SWAP COMMAND\n");
1025 while ((curdir = readdir(pDir)) != NULL && !errno) {
1026 pid = atoi(curdir->d_name);
1027 if (pid < 1 || pid > 32768 || pid == getpid())
1030 if (get_cmdline(pid, cmdline) < 0)
1033 milist = load_maps(pid);
1037 /* get classified map info */
1038 get_trib_mapinfo(pid, milist, glist, &tmi);
1039 oom_score_adj = get_oomscoreadj(pid, oom_path);
1043 fprintf(output_file,
1044 "%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d %8d"
1047 tmi.shared_clean, tmi.shared_dirty,
1048 tmi.private_clean, tmi.private_dirty,
1049 tmi.peak_rss, tmi.pss, tmi.graphic_3d,
1050 tmi.gem_pss, tmi.gem_rss, tmi.swap,
1051 oom_score_adj, cmdline);
1053 fprintf(output_file,
1054 "%8d %8d %8d %8d %8d %8d %8d %8d %s\n",
1058 tmi.shared_dirty + tmi.private_dirty,
1065 if (tmi.other_devices != 0)
1066 fprintf(output_file,
1067 "%s(%d) %d KB may mapped by device(s).\n",
1068 cmdline, pid, tmi.other_devices);
1071 total_private += (tmi.private_clean + tmi.private_dirty);
1072 total_pss += tmi.pss;
1073 total_rss += tmi.rss;
1074 total_graphic_3d += tmi.graphic_3d;
1075 total_gem_rss += tmi.gem_rss;
1076 total_gem_pss += tmi.gem_pss;
1077 total_private_code += tmi.private_clean;
1078 total_private_data += tmi.private_dirty;
1079 total_swap += tmi.swap;
1080 total_shared_code += tmi.shared_clean;
1081 total_shared_data += tmi.shared_dirty;
1082 total_peak_rss += tmi.peak_rss;
1084 total_shared_code_pss += tmi.shared_clean_pss;
1085 total_shared_data_pss += tmi.shared_dirty_pss;
1087 } /* end of while */
1089 total_allocated_gem = KB(total_gem_memory());
1090 fprintf(output_file,
1091 "==============================================="
1092 "===============================================\n");
1094 fprintf(output_file,
1095 "TOTAL: S(CODE) S(DATA) P(CODE) P(DATA)"
1097 "GEM(PSS) GEM(RSS) GEM(ALLOC) SWAP TOTAL(KB)\n");
1098 fprintf(output_file,
1099 " %8d %8d %8d %8d %8d %8d %8d"
1100 " %8d %8d %8d %8d %8d\n",
1101 total_shared_code, total_shared_data,
1102 total_private_code, total_private_data,
1103 total_peak_rss, total_pss, total_graphic_3d,
1104 total_gem_pss, total_gem_rss,
1105 total_allocated_gem, total_swap,
1106 total_pss + total_graphic_3d +
1107 total_allocated_gem);
1109 fprintf(output_file,
1110 "TOTAL: CODE DATA PEAK PSS "
1111 "3D GEM(PSS) GEM(ALLOC) TOTAL(KB)\n");
1112 fprintf(output_file, " %8d %8d %8d %8d %8d %8d %7d %8d %8d\n",
1113 total_shared_code + total_private_code,
1114 total_shared_data + total_private_data,
1115 total_peak_rss, total_pss,
1116 total_graphic_3d, total_gem_pss,
1117 total_allocated_gem, total_swap,
1118 total_pss + total_graphic_3d +
1119 total_allocated_gem);
1124 fprintf(output_file,
1125 "* S(CODE): shared clean memory, it includes"
1126 " duplicated memory\n"
1127 "* S(DATA): shared dirty memory, it includes"
1128 " duplicated memory\n"
1129 "* P(CODE): private clean memory\n"
1130 "* P(DATA): private dirty memory\n"
1131 "* PEAK: peak memory usage of S(CODE) + S(DATA)"
1132 " + P(CODE) + P(DATA)\n"
1133 "* PSS: Proportional Set Size\n"
1134 "* 3D: memory allocated by GPU driver\n"
1135 "* GEM(PSS): GEM memory divided by # of sharers\n"
1136 "* GEM(RSS): GEM memory including duplicated memory\n"
1137 "* GEM(ALLOC): sum of unique gem memory in the system\n"
1138 "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1140 fprintf(output_file,
1141 "* CODE: shared and private clean memory\n"
1142 "* DATA: shared and private dirty memory\n"
1143 "* PEAK: peak memory usage of CODE + DATA\n"
1144 "* PSS: Proportional Set Size\n"
1145 "* 3D: memory allocated by GPU driver\n"
1146 "* GEM(PSS): GEM memory divided by # of sharers\n"
1147 "* GEM(ALLOC): sum of unique GEM memory in the system\n"
1148 "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1150 get_tmpfs_info(output_file);
1151 get_memcg_info(output_file);
1152 get_mem_info(output_file);
1154 fclose(output_file);
1161 static int show_map_new(int pid)
1165 unsigned shared_dirty = 0;
1166 unsigned shared_clean = 0;
1167 unsigned private_dirty = 0;
1168 unsigned private_clean = 0;
1170 unsigned long start = 0;
1171 unsigned long end = 0;
1172 unsigned private_clean_total = 0;
1173 unsigned private_dirty_total = 0;
1174 unsigned shared_clean_total = 0;
1175 unsigned shared_dirty_total = 0;
1176 int duplication = 0;
1178 milist = load_maps(pid);
1181 fprintf(stderr, "cannot get /proc/smaps for pid %d\n", pid);
1186 printf(" S(CODE) S(DATA) P(CODE) P(DATA) ADDR(start-end)"
1188 printf("-------- -------- -------- -------- -----------------"
1189 "------------------------------\n");
1191 printf(" S(CODE) S(DATA) P(CODE) P(DATA) PSS\n");
1192 printf("-------- -------- -------------------"
1193 "------------------\n");
1195 for (mi = milist; mi; mi = mi->next) {
1196 shared_clean += mi->shared_clean;
1197 shared_dirty += mi->shared_dirty;
1198 private_clean += mi->private_clean;
1199 private_dirty += mi->private_dirty;
1202 shared_clean_total += mi->shared_clean;
1203 shared_dirty_total += mi->shared_dirty;
1204 private_clean_total += mi->private_clean;
1205 private_dirty_total += mi->private_dirty;
1210 if ((mi->next && !strcmp(mi->next->name, mi->name)) &&
1211 (mi->next->start == mi->end)) {
1219 printf("%8d %8d %8d %8d %08lx-%08lx %s\n",
1220 shared_clean, shared_dirty, private_clean,
1221 private_dirty, start, end, mi->name);
1229 printf("%8d %8d %8d %8d %18d\n",
1232 private_clean_total,
1233 private_dirty_total,
1240 int main(int argc, char *argv[])
1246 if (!strncmp(argv[1], "-r", strlen("-r")+1)) {
1248 show_rss(OUTPUT_FILE, argv[2]);
1250 show_rss(OUTPUT_UART, NULL);
1252 } else if (!strncmp(argv[1], "-s", strlen("-s")+1)) {
1254 if (argc == 3 && atoi(argv[2]) > 0) {
1255 show_map_new(atoi(argv[2]));
1258 } else if (!strncmp(argv[1], "-a", strlen("-a")+1)) {
1260 show_map_all_new(OUTPUT_UART, NULL);
1262 } else if (!strncmp(argv[1], "-v", strlen("-v")+1)) {
1264 show_map_all_new(OUTPUT_UART, NULL);
1266 } else if (!strncmp(argv[1], "-f", strlen("-f")+1)) {
1269 show_map_all_new(OUTPUT_FILE, argv[2]);
1272 } else if (argc == 2 && atoi(argv[1]) > 0) {
1273 show_map_new(atoi(argv[1]));
1279 "memps [-a] | [-v] | [-s] <pid> | [-f] <output file full path>\n"
1280 " -s = sum (show only sum of each)\n"
1281 " -f = all (show all processes via output file)\n"
1282 " -a = all (show all processes)\n"
1283 " -v = verbos (show all processes in detail)\n");