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"
43 #define BUF_MAX (BUFSIZ) /* most optimal for libc::stdio */
44 #define BUF_INC_SIZE (512 * 1024) /* maximal SMAPS I saw 2 MB */
45 #define KB(bytes) ((bytes)/1024)
47 #define BYTE_TO_KBYTE(b) ((b) >> 10)
48 #define BYTE_TO_MBYTE(b) ((b) >> 20)
49 #define BYTE_TO_GBYTE(b) ((b) >> 30)
51 #define KBYTE_TO_BYTE(k) ((k) << 10)
52 #define KBYTE_TO_MBYTE(k) ((k) >> 10)
53 #define KBYTE_TO_GBYTE(k) ((k) >> 20)
55 #define GBYTE_TO_BYTE(g) ((g) << 30)
56 #define GBYTE_TO_KBYTE(g) ((g) << 20)
57 #define GBYTE_TO_MBYTE(g) ((g) << 10)
59 typedef struct geminfo geminfo;
60 typedef struct mapinfo mapinfo;
61 typedef struct trib_mapinfo trib_mapinfo;
77 unsigned shared_clean;
78 unsigned shared_dirty;
79 unsigned private_clean;
80 unsigned private_dirty;
85 /* classify normal, graphic and other devices memory */
87 unsigned shared_clean;
88 unsigned shared_dirty;
89 unsigned private_clean;
90 unsigned private_dirty;
91 unsigned shared_clean_pss;
92 unsigned shared_dirty_pss;
101 unsigned other_devices;
113 static int smaps_lcnt;
117 /* reads file contents into memory */
118 static char* cread(const char* path)
120 /* once allocated area for reads */
121 static char* text = NULL;
122 static size_t size = 0;
127 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, int line_cnt)
329 if ((--line_cnt <= 0) || (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 while (line_cnt-- && (line = cgets(smaps))) {
347 if (sscanf(line, "Size: %d kB", &mi->size) == 1) {}
348 else if (sscanf(line, "Rss: %d kB", &mi->rss) == 1) {}
349 else if (sscanf(line, "Pss: %d kB", &mi->pss) == 1) {}
350 else if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) == 1) {}
351 else if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) == 1) {}
352 else if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) == 1) {}
353 else if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) == 1) {}
354 else if (sscanf(line, "Swap: %d kB", &mi->swap) == 1) {}
360 static unsigned total_gem_memory(void)
363 unsigned total_gem_mem = 0;
364 unsigned name, size, handles, refcount;
367 gem_fp = fopen("/proc/dri/0/gem_names", "r");
370 "cannot open /proc/dir/0/gem_names\n");
374 if (fgets(line, BUF_MAX, gem_fp) == NULL) {
379 while (fgets(line, BUF_MAX, gem_fp) != NULL)
380 if (sscanf(line, "%d %d %d %d\n",
381 &name, &size, &handles, &refcount) == 4)
382 total_gem_mem += size;
385 return total_gem_mem;
389 int fread_uint(const char *path, u_int32_t *number)
394 f = fopen(path, "r");
397 fprintf(stderr,"Fail to open %s file.\n", path);
401 ret = fscanf(f, "%u", number);
403 fprintf(stderr,"Fail to read file\n");
412 #define MAX_PATH_LENGTH 512
413 static int cgroup_read_node(const char *cgroup_name,
414 const char *file_name, unsigned int *value)
416 char buf[MAX_PATH_LENGTH];
418 snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name);
419 ret = fread_uint(buf, value);
424 * @desc Provides usage in bytes for provided memory cgroup. Works
425 * with/without swap accounting.
427 * @param memcg_path[in] Full path to memory cgroup
428 * @param swap[in] Boolean value for deciding if account usage with swap
429 * @return current cgroup usage in bytes or 0 on error
431 static unsigned int get_memcg_usage(const char *memcg_path, bool swap)
437 ret = cgroup_read_node(memcg_path,
438 "/memory.memsw.usage_in_bytes", &usage);
440 ret = cgroup_read_node(memcg_path, "/memory.usage_in_bytes",
450 static void get_memcg_info(FILE *output_fp)
455 struct dirent *result;
456 struct stat path_stat;
458 unsigned long usage, usage_with_swap;
461 fprintf(output_fp,"====================================================================\n");
462 fprintf(output_fp,"MEMORY CGROUPS USAGE INFO\n");
464 pdir = opendir(MEMCG_PATH);
466 fprintf(stderr,"cannot read directory %s", MEMCG_PATH);
470 while (!(ret = readdir_r(pdir, &entry, &result)) && result != NULL) {
471 snprintf(buf, sizeof(buf), "%s/%s", MEMCG_PATH, entry.d_name);
472 /* If can't stat then ignore */
473 if (stat(buf, &path_stat) != 0)
476 /* If it's not directory or it's parent path then ignore */
477 if (!(S_ISDIR(path_stat.st_mode) &&
478 strncmp(entry.d_name, "..", 3)))
481 usage = get_memcg_usage(buf, false);
482 usage_with_swap = get_memcg_usage(buf, true);
483 /* It is posible by rounding errors to get negative value */
484 usage_swap = usage_with_swap - usage;
488 /* Case of root cgroup in hierarchy */
489 if (!strncmp(entry.d_name, ".", 2))
490 fprintf(output_fp, "%13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB) \n\n",
491 MEMCG_PATH, BYTE_TO_MBYTE(usage),
492 BYTE_TO_KBYTE(usage),
493 BYTE_TO_MBYTE(usage_with_swap),
494 BYTE_TO_KBYTE(usage_with_swap),
495 BYTE_TO_MBYTE(usage_swap),
496 BYTE_TO_KBYTE(usage_swap));
498 fprintf(output_fp, "memcg: %13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB)\n",
499 entry.d_name, BYTE_TO_MBYTE(usage),
500 BYTE_TO_KBYTE(usage),
501 BYTE_TO_MBYTE(usage_with_swap),
502 BYTE_TO_KBYTE(usage_with_swap),
503 BYTE_TO_MBYTE(usage_swap),
504 BYTE_TO_KBYTE(usage_swap));
511 static void get_mem_info(FILE *output_fp)
516 unsigned int free = 0, cached = 0;
517 unsigned int total_mem = 0, available = 0, used;
518 unsigned int swap_total = 0, swap_free = 0, zram_used, swap_used;
519 unsigned int used_ratio;
521 if (output_fp == NULL)
524 fp = fopen("/proc/meminfo", "r");
527 fprintf(stderr, "%s open failed, %p", buf, fp);
531 while (fgets(buf, PATH_MAX, fp) != NULL) {
532 if ((idx = strstr(buf, "MemTotal:"))) {
533 idx += strlen("Memtotal:");
534 while (*idx < '0' || *idx > '9')
536 total_mem = atoi(idx);
537 } else if ((idx = strstr(buf, "MemFree:"))) {
538 idx += strlen("MemFree:");
539 while (*idx < '0' || *idx > '9')
542 } else if ((idx = strstr(buf, "MemAvailable:"))) {
543 idx += strlen("MemAvailable:");
544 while (*idx < '0' || *idx > '9')
546 available = atoi(idx);
547 } else if((idx = strstr(buf, "Cached:")) && !strstr(buf, "Swap")) {
548 idx += strlen("Cached:");
549 while (*idx < '0' || *idx > '9')
552 } else if((idx = strstr(buf, "SwapTotal:"))) {
553 idx += strlen("SwapTotal:");
554 while (*idx < '0' || *idx > '9')
556 swap_total = atoi(idx);
557 } else if((idx = strstr(buf, "SwapFree:"))) {
558 idx += strlen("SwapFree");
559 while (*idx < '0' || *idx > '9')
561 swap_free = atoi(idx);
566 if (total_mem == 0) {
567 fprintf(stderr, "cannot get total memory size\n");
573 available = free + cached;
574 used = total_mem - available;
575 used_ratio = used * 100 / total_mem;
576 swap_used = swap_total - swap_free;
578 if (fread_uint(ZRAM_USED_PATH, &zram_used) != 0)
582 "====================================================================\n");
585 fprintf(output_fp, "Total RAM size: \t%15d MB( %6d kB)\n",
586 total_mem >> 10, total_mem);
588 fprintf(output_fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n",
589 (total_mem - free) >> 10, total_mem - free);
591 fprintf(output_fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n",
594 fprintf(output_fp, "Used (Mem): \t\t%15d MB( %6d kB)\n",
597 fprintf(output_fp, "Used (Swap): \t\t%15d MB( %6d kB)\n",
598 swap_used >> 10, swap_used);
600 fprintf(output_fp, "Used (Zram block device): %13d MB( %6d kB)\n",
601 BYTE_TO_MBYTE(zram_used), BYTE_TO_KBYTE(zram_used));
603 fprintf(output_fp, "Used Ratio: \t\t%15d %%\n", used_ratio);
605 fprintf(output_fp, "Mem Free:\t\t%15d MB( %6d kB)\n",
608 fprintf(output_fp, "Available (Free+Reclaimable):%10d MB( %6d kB)\n",
614 static int get_tmpfs_info(FILE *output_fp)
618 char tmpfs_mp[NAME_MAX]; /* tmpfs mount point */
619 struct statfs tmpfs_info;
621 if (output_fp == NULL)
624 fp = fopen("/etc/mtab", "r");
629 "====================================================================\n");
630 fprintf(output_fp, "TMPFS INFO\n");
632 while (fgets(line, BUF_MAX, fp) != NULL) {
633 if (sscanf(line, "tmpfs %s tmpfs", tmpfs_mp) == 1) {
634 statfs(tmpfs_mp, &tmpfs_info);
636 "tmpfs %16s Total %8ld KB, Used %8ld, Avail %8ld\n",
638 /* 1 block is 4 KB */
639 tmpfs_info.f_blocks * 4,
640 (tmpfs_info.f_blocks - tmpfs_info.f_bfree) * 4,
641 tmpfs_info.f_bfree * 4);
648 mapinfo *load_maps(int pid)
655 snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
660 while ((mi = read_mapinfo(&smaps, smaps_lcnt)) != 0) {
662 if ((!strcmp(mi->name, milist->name)
663 && (mi->name[0] != '['))) {
664 milist->size += mi->size;
665 milist->swap += mi->swap;
666 milist->rss += mi->rss;
667 milist->pss += mi->pss;
668 milist->shared_clean += mi->shared_clean;
669 milist->shared_dirty += mi->shared_dirty;
670 milist->private_clean += mi->private_clean;
671 milist->private_dirty += mi->private_dirty;
673 milist->end = mi->end;
674 strncpy(milist->perm, mi->perm, 4);
688 static void init_trib_mapinfo(trib_mapinfo *tmi)
692 tmi->shared_clean = 0;
693 tmi->shared_dirty = 0;
694 tmi->private_clean = 0;
695 tmi->private_dirty = 0;
697 tmi->shared_clean_pss = 0;
698 tmi->shared_dirty_pss = 0;
706 tmi->other_devices = 0;
711 get_trib_mapinfo(unsigned int tgid, mapinfo *milist,
712 geminfo *gilist, trib_mapinfo *result)
716 mapinfo *temp = NULL;
722 init_trib_mapinfo(result);
723 for (mi = milist; mi;) {
724 if (strstr(mi->name, STR_SGX_PATH)) {
725 result->graphic_3d += mi->pss;
726 } else if (strstr(mi->name, STR_3D_PATH1) ||
727 strstr(mi->name, STR_3D_PATH2)) {
728 result->graphic_3d += mi->size;
729 } else if (mi->rss != 0 && mi->pss == 0
730 && mi->shared_clean == 0
731 && mi->shared_dirty == 0
732 && mi->private_clean == 0
733 && mi->private_dirty == 0
735 result->other_devices += mi->size;
736 } else if (!strncmp(mi->name, STR_DRM_PATH1,
737 sizeof(STR_DRM_PATH1)) ||
738 !strncmp(mi->name, STR_DRM_PATH2,
739 sizeof(STR_DRM_PATH2))) {
740 result->gem_mmap += mi->rss;
742 result->shared_clean += mi->shared_clean;
743 result->shared_dirty += mi->shared_dirty;
744 result->private_clean += mi->private_clean;
745 result->private_dirty += mi->private_dirty;
746 result->swap += mi->swap;
747 result->rss += mi->rss;
748 result->pss += mi->pss;
749 result->size += mi->size;
751 if(mi->shared_clean != 0)
752 result->shared_clean_pss += mi->pss;
753 else if (mi->shared_dirty != 0)
754 result->shared_dirty_pss += mi->pss;
765 result->peak_rss = get_peak_rss(tgid);
766 if (result->peak_rss < result->rss)
767 result->peak_rss = result->rss;
768 if (result->gem_mmap > 0)
769 result->peak_rss -= result->gem_mmap;
771 gi = find_geminfo(tgid, gilist);
773 result->gem_rss = gi->rss_size;
774 result->gem_pss = gi->pss_size;
780 static int get_cmdline(unsigned int pid, char *cmdline)
783 char buf[NAME_MAX] = {0, };
786 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
787 fp = fopen(buf, "r");
789 fprintf(stderr, "cannot file open %s\n", buf);
792 if ((ret = fscanf(fp, "%4095s", cmdline)) < 1) {
801 static int get_oomscoreadj(unsigned int pid)
807 snprintf(tmp, sizeof(tmp), "/proc/%d/oom_score_adj", pid);
808 fp = fopen(tmp, "r");
814 if (fgets(tmp, sizeof(tmp), fp) == NULL) {
820 oomadj_val = atoi(tmp);
826 static void get_rss(pid_t pid, unsigned long *result)
829 char proc_path[PATH_MAX];
830 unsigned long rss = 0;
834 snprintf(proc_path, sizeof(proc_path), "/proc/%d/statm", pid);
835 fp = fopen(proc_path, "r");
839 if (fscanf(fp, "%*s %ld", &rss) < 1) {
846 /* convert page to Kb */
851 static void show_rss(int output_type, char *output_path)
854 struct dirent curdir;
855 struct dirent *result;
857 char cmdline[PATH_MAX];
858 FILE *output_file = NULL;
863 pDir = opendir("/proc");
865 fprintf(stderr, "cannot read directory /proc.\n");
869 if (output_type == OUTPUT_FILE && output_path) {
870 output_file = fopen(output_path, "w+");
872 fprintf(stderr, "cannot open output file(%s)\n",
878 output_file = stdout;
882 " PID RSS OOM_SCORE COMMAND\n");
884 while (!(ret = readdir_r(pDir, &curdir, &result)) && result != NULL) {
885 pid = atoi(curdir.d_name);
886 if (pid < 1 || pid > 32768 || pid == getpid())
889 if (get_cmdline(pid, cmdline) < 0)
892 oom_score_adj = get_oomscoreadj(pid);
904 get_tmpfs_info(output_file);
905 get_mem_info(output_file);
913 static int show_map_all_new(int output_type, char *output_path)
916 struct dirent curdir;
917 struct dirent *result;
921 unsigned total_pss = 0;
922 unsigned total_private = 0;
923 unsigned total_private_code = 0;
924 unsigned total_private_data = 0;
925 unsigned total_shared_code = 0;
926 unsigned total_shared_data = 0;
927 unsigned total_shared_code_pss = 0;
928 unsigned total_shared_data_pss = 0;
929 unsigned total_swap = 0;
930 unsigned total_rss = 0;
931 unsigned total_graphic_3d = 0;
932 unsigned total_gem_rss = 0;
933 unsigned total_gem_pss = 0;
934 unsigned total_peak_rss = 0;
935 unsigned total_allocated_gem = 0;
937 char cmdline[PATH_MAX];
938 FILE *output_file = NULL;
943 pDir = opendir("/proc");
945 fprintf(stderr, "cannot read directory /proc.\n");
949 if (output_type == OUTPUT_FILE && output_path) {
950 output_file = fopen(output_path, "w+");
952 fprintf(stderr, "cannot open output file(%s)\n",
958 output_file = stdout;
960 glist = load_geminfo();
965 " PID S(CODE) S(DATA) P(CODE) P(DATA)"
967 " GEM(PSS) GEM(RSS) SWAP"
968 " OOM_SCORE_ADJ COMMAND\n");
971 " PID CODE DATA PEAK PSS"
972 " 3D GEM(PSS) SWAP COMMAND\n");
975 while (!(r = readdir_r(pDir, &curdir, &result)) && result != NULL) {
976 pid = atoi(curdir.d_name);
977 if (pid < 1 || pid > 32768 || pid == getpid())
980 if (get_cmdline(pid, cmdline) < 0)
983 milist = load_maps(pid);
987 /* get classified map info */
988 get_trib_mapinfo(pid, milist, glist, &tmi);
989 oom_score_adj = get_oomscoreadj(pid);
994 "%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d %8d"
997 tmi.shared_clean, tmi.shared_dirty,
998 tmi.private_clean, tmi.private_dirty,
999 tmi.peak_rss, tmi.pss, tmi.graphic_3d,
1000 tmi.gem_pss, tmi.gem_rss, tmi.swap,
1001 oom_score_adj, cmdline);
1003 fprintf(output_file,
1004 "%8d %8d %8d %8d %8d %8d %8d %8d %s\n",
1008 tmi.shared_dirty + tmi.private_dirty,
1015 if (tmi.other_devices != 0)
1016 fprintf(output_file,
1017 "%s(%d) %d KB may mapped by device(s).\n",
1018 cmdline, pid, tmi.other_devices);
1021 total_private += (tmi.private_clean + tmi.private_dirty);
1022 total_pss += tmi.pss;
1023 total_rss += tmi.rss;
1024 total_graphic_3d += tmi.graphic_3d;
1025 total_gem_rss += tmi.gem_rss;
1026 total_gem_pss += tmi.gem_pss;
1027 total_private_code += tmi.private_clean;
1028 total_private_data += tmi.private_dirty;
1029 total_swap += tmi.swap;
1030 total_shared_code += tmi.shared_clean;
1031 total_shared_data += tmi.shared_dirty;
1032 total_peak_rss += tmi.peak_rss;
1034 total_shared_code_pss += tmi.shared_clean_pss;
1035 total_shared_data_pss += tmi.shared_dirty_pss;
1037 } /* end of while */
1039 total_allocated_gem = KB(total_gem_memory());
1040 fprintf(output_file,
1041 "==============================================="
1042 "===============================================\n");
1044 fprintf(output_file,
1045 "TOTAL: S(CODE) S(DATA) P(CODE) P(DATA)"
1047 "GEM(PSS) GEM(RSS) GEM(ALLOC) SWAP TOTAL(KB)\n");
1048 fprintf(output_file,
1049 " %8d %8d %8d %8d %8d %8d %8d"
1050 " %8d %8d %8d %8d %8d\n",
1051 total_shared_code, total_shared_data,
1052 total_private_code, total_private_data,
1053 total_peak_rss, total_pss, total_graphic_3d,
1054 total_gem_pss, total_gem_rss,
1055 total_allocated_gem, total_swap,
1056 total_pss + total_graphic_3d +
1057 total_allocated_gem);
1059 fprintf(output_file,
1060 "TOTAL: CODE DATA PEAK PSS "
1061 "3D GEM(PSS) GEM(ALLOC) TOTAL(KB)\n");
1062 fprintf(output_file, " %8d %8d %8d %8d %8d %8d %7d %8d %8d\n",
1063 total_shared_code + total_private_code,
1064 total_shared_data + total_private_data,
1065 total_peak_rss, total_pss,
1066 total_graphic_3d, total_gem_pss,
1067 total_allocated_gem, total_swap,
1068 total_pss + total_graphic_3d +
1069 total_allocated_gem);
1074 fprintf(output_file,
1075 "* S(CODE): shared clean memory, it includes"
1076 " duplicated memory\n"
1077 "* S(DATA): shared dirty memory, it includes"
1078 " duplicated memory\n"
1079 "* P(CODE): private clean memory\n"
1080 "* P(DATA): private dirty memory\n"
1081 "* PEAK: peak memory usage of S(CODE) + S(DATA)"
1082 " + P(CODE) + P(DATA)\n"
1083 "* PSS: Proportional Set Size\n"
1084 "* 3D: memory allocated by GPU driver\n"
1085 "* GEM(PSS): GEM memory devided by # of sharers\n"
1086 "* GEM(RSS): GEM memory including duplicated memory\n"
1087 "* GEM(ALLOC): sum of unique gem memory in the system\n"
1088 "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1090 fprintf(output_file,
1091 "* CODE: shared and private clean memory\n"
1092 "* DATA: shared and private dirty memory\n"
1093 "* PEAK: peak memory usage of CODE + DATA\n"
1094 "* PSS: Proportional Set Size\n"
1095 "* 3D: memory allocated by GPU driver\n"
1096 "* GEM(PSS): GEM memory deviced by # of sharers\n"
1097 "* GEM(ALLOC): sum of unique GEM memory in the system\n"
1098 "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1100 get_tmpfs_info(output_file);
1101 get_memcg_info(output_file);
1102 get_mem_info(output_file);
1104 fclose(output_file);
1110 static int show_map_new(int pid)
1114 unsigned shared_dirty = 0;
1115 unsigned shared_clean = 0;
1116 unsigned private_dirty = 0;
1117 unsigned private_clean = 0;
1119 unsigned long start = 0;
1120 unsigned long end = 0;
1121 unsigned private_clean_total = 0;
1122 unsigned private_dirty_total = 0;
1123 unsigned shared_clean_total = 0;
1124 unsigned shared_dirty_total = 0;
1125 int duplication = 0;
1127 milist = load_maps(pid);
1130 fprintf(stderr, "cannot get /proc/smaps for pid %d\n", pid);
1135 printf(" S(CODE) S(DATA) P(CODE) P(DATA) ADDR(start-end)"
1137 printf("-------- -------- -------- -------- -----------------"
1138 "------------------------------\n");
1140 printf(" S(CODE) S(DATA) P(CODE) P(DATA) PSS\n");
1141 printf("-------- -------- -------------------"
1142 "------------------\n");
1144 for (mi = milist; mi; mi = mi->next) {
1145 shared_clean += mi->shared_clean;
1146 shared_dirty += mi->shared_dirty;
1147 private_clean += mi->private_clean;
1148 private_dirty += mi->private_dirty;
1151 shared_clean_total += mi->shared_clean;
1152 shared_dirty_total += mi->shared_dirty;
1153 private_clean_total += mi->private_clean;
1154 private_dirty_total += mi->private_dirty;
1159 if ((mi->next && !strcmp(mi->next->name, mi->name)) &&
1160 (mi->next->start == mi->end)) {
1168 printf("%8d %8d %8d %8d %08lx-%08lx %s\n",
1169 shared_clean, shared_dirty, private_clean,
1170 private_dirty, start, end, mi->name);
1178 printf("%8d %8d %8d %8d %18d\n",
1181 private_clean_total,
1182 private_dirty_total,
1189 int get_fixed_smaps_lcnt(void)
1200 pch = strstr(buf.release, ".");
1201 strncpy(str, pch+1, 2);
1202 sub_version = atoi(str);
1204 if (buf.release[0] >= '4') {
1205 if (sub_version >= 4)
1207 /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty,
1208 * Private_Clean, Private_Drity,
1209 * Referenced, Anonymous, AnonHugePages, Shared_Hugetlb,
1210 * Private_Hugetlb, Swap, SwapPss, KernelPageSize,
1211 * MMUPageSize, Locked, VmFlags */
1212 else if (sub_version == 3)
1214 /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty,
1215 * Private_Clean, Private_Drity,
1216 * Referenced, Anonymous, AnonHugePages, Swap, SwapPss,
1217 * KernelPageSize, MMUPageSize, Locked, VmFlags */
1220 /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty,
1221 * Private_Clean, Private_Drity,
1222 * Referenced, Anonymous, AnonHugePages, Swap,
1223 * KernelPageSize, MMUPageSize, Locked, VmFlags */
1224 } else if (buf.release[0] == '3') {
1225 if (sub_version >= 10)
1227 /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty,
1228 * Private_Clean, Private_Drity,
1229 * Referenced, Anonymous, AnonHugePages, Swap,
1230 * KernelPageSize, MMUPageSize, Locked, VmFlags */
1233 /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty,
1234 * Private_Clean, Private_Drity,
1235 * Referenced, Anonymous, AnonHugePages, Swap,
1236 * KernelPageSize, MMUPageSize, Locked */
1239 /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty,
1240 * Private_Clean, Private_Drity,
1241 * Referenced, Swap, KernelPageSize, MMUPageSize */
1247 int get_smaps_lcnt(void)
1249 char *buf, *buf_start, *line;
1250 char cmd[64] = "/proc/self/smaps";
1252 long start_addr, end_addr;
1253 unsigned long pg_off;
1257 buf_start = cread(cmd);
1258 if (buf_start == NULL) {
1263 if ((line = cgets(&buf)) == 0)
1266 if (sscanf(line, "%lx-%lx %s %lx %x:%x",
1267 &start_addr, &end_addr, flags, &pg_off, &major, &minor) != 6) {
1271 while ((line = cgets(&buf)) != 0 ) {
1273 if (sscanf(line, "%lx-%lx %s %lx %x:%x",
1274 &start_addr, &end_addr, flags, &pg_off, &major, &minor) == 6)
1281 return get_fixed_smaps_lcnt();
1284 int main(int argc, char *argv[])
1290 smaps_lcnt = get_smaps_lcnt();
1292 if (!strncmp(argv[1], "-r", strlen("-r")+1)) {
1294 show_rss(OUTPUT_FILE, argv[2]);
1296 show_rss(OUTPUT_UART, NULL);
1298 } else if (!strncmp(argv[1], "-s", strlen("-s")+1)) {
1300 if (argc == 3 && atoi(argv[2]) > 0) {
1301 show_map_new(atoi(argv[2]));
1304 } else if (!strncmp(argv[1], "-a", strlen("-a")+1)) {
1306 show_map_all_new(OUTPUT_UART, NULL);
1308 } else if (!strncmp(argv[1], "-v", strlen("-v")+1)) {
1310 show_map_all_new(OUTPUT_UART, NULL);
1312 } else if (!strncmp(argv[1], "-f", strlen("-f")+1)) {
1315 show_map_all_new(OUTPUT_FILE, argv[2]);
1318 } else if (argc == 2 && atoi(argv[1]) > 0) {
1319 show_map_new(atoi(argv[1]));
1325 "memps [-a] | [-v] | [-s] <pid> | [-f] <output file full path>\n"
1326 " -s = sum (show only sum of each)\n"
1327 " -f = all (show all processes via output file)\n"
1328 " -a = all (show all processes)\n"
1329 " -v = verbos (show all processes in detail)\n");