2 Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
7 Unless required by applicable law or agreed to in writing, software
8 distributed under the License is distributed on an "AS IS" BASIS,
9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 See the License for the specific language governing permissions and
11 limitations under the License
23 #include <sys/types.h>
25 #include <linux/limits.h>
31 #include <sys/utsname.h>
33 #define STR_SGX_PATH "/dev/pvrsrvkm"
34 #define STR_3D_PATH1 "/dev/mali"
35 #define STR_3D_PATH2 "/dev/kgsl-3d0"
36 #define STR_DRM_PATH1 "/drm mm object (deleted)"
37 #define STR_DRM_PATH2 "/dev/dri/card0"
38 #define MEMCG_PATH "/sys/fs/cgroup/memory"
39 #define ZRAM_USED_PATH "/sys/block/zram0/mem_used_total"
41 #define BUF_MAX (BUFSIZ) /* most optimal for libc::stdio */
42 #define BUF_INC_SIZE (512 * 1024) /* maximal SMAPS I saw 2 MB */
43 #define KB(bytes) ((bytes)/1024)
45 #define BYTE_TO_KBYTE(b) ((b) >> 10)
46 #define BYTE_TO_MBYTE(b) ((b) >> 20)
47 #define BYTE_TO_GBYTE(b) ((b) >> 30)
49 #define KBYTE_TO_BYTE(k) ((k) << 10)
50 #define KBYTE_TO_MBYTE(k) ((k) >> 10)
51 #define KBYTE_TO_GBYTE(k) ((k) >> 20)
53 #define GBYTE_TO_BYTE(g) ((g) << 30)
54 #define GBYTE_TO_KBYTE(g) ((g) << 20)
55 #define GBYTE_TO_MBYTE(g) ((g) << 10)
57 typedef struct geminfo geminfo;
58 typedef struct mapinfo mapinfo;
59 typedef struct trib_mapinfo trib_mapinfo;
75 unsigned shared_clean;
76 unsigned shared_dirty;
77 unsigned private_clean;
78 unsigned private_dirty;
83 /* classify normal, graphic and other devices memory */
85 unsigned shared_clean;
86 unsigned shared_dirty;
87 unsigned private_clean;
88 unsigned private_dirty;
89 unsigned shared_clean_pss;
90 unsigned shared_dirty_pss;
99 unsigned other_devices;
111 static int ignore_smaps_field;
115 /* reads file contents into memory */
116 static char* cread(const char* path)
118 /* once allocated area for reads */
119 static char* text = NULL;
120 static size_t size = 0;
125 int fd = open(path, O_RDONLY);
132 /* ensure we have enough space */
134 ptr = (char*)realloc(text, size + BUF_INC_SIZE);
143 size += BUF_INC_SIZE;
145 ret = read(fd, ptr, cap);
148 } else if (ret > 0) {
155 return (ret < 0 ? NULL : text);
158 /* like fgets/gets but adjusting contents pointer */
159 static inline char* cgets(char** contents)
161 if (contents && *contents && **contents) {
162 char* bos = *contents; /* begin of string */
163 char* eos = strchr(bos, '\n'); /* end of string */
178 static unsigned get_peak_rss(unsigned int pid)
180 static const char field[] = "VmHWM:";
185 sprintf(tmp, "/proc/%d/status", pid);
188 fprintf(stderr, "cannot open %s\n", tmp);
192 value = strstr(line, field);
194 value += sizeof(field);
195 return strtoul(value, NULL, 10);
200 #define NUM_GEM_FIELD 6
202 static geminfo *read_geminfo(FILE *fp)
206 unsigned int pid, tgid, handle, refcount, hcount;
209 if (fgets(line, BUF_MAX, fp) != NULL) {
210 if (sscanf(line, "%d %d %d %d %d 0x%x",
211 &pid, &tgid, &handle, &refcount,
212 &hcount, &gem_size) != NUM_GEM_FIELD)
217 tgeminfo = malloc(sizeof(geminfo));
218 if (tgeminfo == NULL)
220 tgeminfo->tgid = tgid;
221 tgeminfo->hcount = hcount;
222 tgeminfo->rss_size = KB(gem_size);
223 tgeminfo->pss_size = KB(gem_size/tgeminfo->hcount);
231 static geminfo *load_geminfo(void)
234 geminfo *gilist = NULL;
238 drm_fp = fopen("/sys/kernel/debug/dri/0/gem_info", "r");
240 if (drm_fp == NULL) {
242 "cannot open /sys/kernel/debug/dri/0/gem_info\n");
246 if (fgets(line, BUF_MAX, drm_fp) == NULL) {
250 /* we should count a number of whitespace separated fields */
251 int in_field = (line[0] && !isblank(line[0]));
252 unsigned int size = (unsigned)in_field;
253 const char* ptr = &line[1];
255 /* sscanf() was used in original code, so number of fields */
256 /* in string is expected to be at least NUM_GEM_FIELD */
257 while (*ptr && size < NUM_GEM_FIELD) {
258 if (isblank(*ptr++)) {
265 /* next field started */
272 if (size != NUM_GEM_FIELD) {
278 while ((ginfo = read_geminfo(drm_fp)) != NULL) {
279 if (gilist && ginfo->tgid == gilist->tgid) {
280 gilist->pss_size += ginfo->pss_size;
281 gilist->rss_size += ginfo->rss_size;
285 ginfo->next = gilist;
295 /* 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so
296 * 012345678901234567890123456789012345678901234567890123456789
300 mapinfo *read_mapinfo(char** smaps, int rest_line)
307 if ((line = cgets(smaps)) == 0)
314 mi = malloc(sizeof(mapinfo) + len + 16);
318 mi->start = strtoul(line, 0, 16);
319 mi->end = strtoul(line + 9, 0, 16);
321 mi->perm[0] = line[18]; /* read */
322 mi->perm[1] = line[19]; /* write */
323 mi->perm[2] = line[20]; /* execute */
324 mi->perm[3] = line[21]; /* may share or private */
327 strcpy(mi->name, "[anon]");
329 strcpy(mi->name, line + 49);
331 if ((line = cgets(smaps)) == 0)
333 if (sscanf(line, "Size: %d kB", &mi->size) != 1)
335 if ((line = cgets(smaps)) == 0)
337 if (sscanf(line, "Rss: %d kB", &mi->rss) != 1)
339 if ((line = cgets(smaps)) == 0)
341 if (sscanf(line, "Pss: %d kB", &mi->pss) == 1)
342 if ((line = cgets(smaps)) == 0)
344 if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1)
346 if ((line = cgets(smaps)) == 0)
348 if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1)
350 if ((line = cgets(smaps)) == 0)
352 if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1)
354 if ((line = cgets(smaps)) == 0)
356 if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1)
359 while (rest_line-- && (line = cgets(smaps))) {
360 if (sscanf(line, "Swap: %d kB", &tmp) == 1) {
364 if (sscanf(line, "PSwap: %d kB", &tmp) == 1)
370 printf("mi get error\n");
375 static unsigned total_gem_memory(void)
378 unsigned total_gem_mem = 0;
379 unsigned name, size, handles, refcount;
382 gem_fp = fopen("/proc/dri/0/gem_names", "r");
385 "cannot open /proc/dir/0/gem_names\n");
389 if (fgets(line, BUF_MAX, gem_fp) == NULL) {
394 while (fgets(line, BUF_MAX, gem_fp) != NULL)
395 if (sscanf(line, "%d %d %d %d\n",
396 &name, &size, &handles, &refcount) == 4)
397 total_gem_mem += size;
400 return total_gem_mem;
404 int fread_uint(const char *path, u_int32_t *number)
409 f = fopen(path, "r");
412 fprintf(stderr,"Fail to open %s file.\n", path);
416 ret = fscanf(f, "%u", number);
418 fprintf(stderr,"Fail to read file\n");
427 #define MAX_PATH_LENGTH 512
428 static int cgroup_read_node(const char *cgroup_name,
429 const char *file_name, unsigned int *value)
431 char buf[MAX_PATH_LENGTH];
433 snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name);
434 ret = fread_uint(buf, value);
439 * @desc Provides usage in bytes for provided memory cgroup. Works
440 * with/without swap accounting.
442 * @param memcg_path[in] Full path to memory cgroup
443 * @param swap[in] Boolean value for deciding if account usage with swap
444 * @return current cgroup usage in bytes or 0 on error
446 static unsigned int get_memcg_usage(const char *memcg_path, bool swap)
452 ret = cgroup_read_node(memcg_path,
453 "/memory.memsw.usage_in_bytes", &usage);
455 ret = cgroup_read_node(memcg_path, "/memory.usage_in_bytes",
465 static void get_memcg_info(FILE *output_fp)
470 struct dirent *result;
471 struct stat path_stat;
473 unsigned long usage, usage_with_swap;
476 fprintf(output_fp,"====================================================================\n");
477 fprintf(output_fp,"MEMORY CGROUPS USAGE INFO\n");
479 pdir = opendir(MEMCG_PATH);
481 fprintf(stderr,"cannot read directory %s", MEMCG_PATH);
485 while (!(ret = readdir_r(pdir, &entry, &result)) && result != NULL) {
486 snprintf(buf, sizeof(buf), "%s/%s", MEMCG_PATH, entry.d_name);
487 /* If can't stat then ignore */
488 if (stat(buf, &path_stat) != 0)
491 /* If it's not directory or it's parent path then ignore */
492 if (!(S_ISDIR(path_stat.st_mode) &&
493 strncmp(entry.d_name, "..", 3)))
496 usage = get_memcg_usage(buf, false);
497 usage_with_swap = get_memcg_usage(buf, true);
498 /* It is posible by rounding errors to get negative value */
499 usage_swap = usage_with_swap - usage;
503 /* Case of root cgroup in hierarchy */
504 if (!strncmp(entry.d_name, ".", 2))
505 fprintf(output_fp, "%13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB) \n\n",
506 MEMCG_PATH, BYTE_TO_MBYTE(usage),
507 BYTE_TO_KBYTE(usage),
508 BYTE_TO_MBYTE(usage_with_swap),
509 BYTE_TO_KBYTE(usage_with_swap),
510 BYTE_TO_MBYTE(usage_swap),
511 BYTE_TO_KBYTE(usage_swap));
513 fprintf(output_fp, "memcg: %13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB)\n",
514 entry.d_name, BYTE_TO_MBYTE(usage),
515 BYTE_TO_KBYTE(usage),
516 BYTE_TO_MBYTE(usage_with_swap),
517 BYTE_TO_KBYTE(usage_with_swap),
518 BYTE_TO_MBYTE(usage_swap),
519 BYTE_TO_KBYTE(usage_swap));
526 static void get_mem_info(FILE *output_fp)
531 unsigned int free = 0, cached = 0;
532 unsigned int total_mem = 0, available = 0, used;
533 unsigned int swap_total = 0, swap_free = 0, zram_used, swap_used;
534 unsigned int used_ratio;
536 if (output_fp == NULL)
539 fp = fopen("/proc/meminfo", "r");
542 fprintf(stderr, "%s open failed, %p", buf, fp);
546 while (fgets(buf, PATH_MAX, fp) != NULL) {
547 if ((idx = strstr(buf, "MemTotal:"))) {
548 idx += strlen("Memtotal:");
549 while (*idx < '0' || *idx > '9')
551 total_mem = atoi(idx);
552 } else if ((idx = strstr(buf, "MemFree:"))) {
553 idx += strlen("MemFree:");
554 while (*idx < '0' || *idx > '9')
557 } else if ((idx = strstr(buf, "MemAvailable:"))) {
558 idx += strlen("MemAvailable:");
559 while (*idx < '0' || *idx > '9')
561 available = atoi(idx);
562 } else if((idx = strstr(buf, "Cached:")) && !strstr(buf, "Swap")) {
563 idx += strlen("Cached:");
564 while (*idx < '0' || *idx > '9')
567 } else if((idx = strstr(buf, "SwapTotal:"))) {
568 idx += strlen("SwapTotal:");
569 while (*idx < '0' || *idx > '9')
571 swap_total = atoi(idx);
572 } else if((idx = strstr(buf, "SwapFree:"))) {
573 idx += strlen("SwapFree");
574 while (*idx < '0' || *idx > '9')
576 swap_free = atoi(idx);
581 if (total_mem == 0) {
582 fprintf(stderr, "cannot get total memory size\n");
588 available = free + cached;
589 used = total_mem - available;
590 used_ratio = used * 100 / total_mem;
591 swap_used = swap_total - swap_free;
593 if (fread_uint(ZRAM_USED_PATH, &zram_used) != 0)
597 "====================================================================\n");
600 fprintf(output_fp, "Total RAM size: \t%15d MB( %6d kB)\n",
601 total_mem >> 10, total_mem);
603 fprintf(output_fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n",
604 (total_mem - free) >> 10, total_mem - free);
606 fprintf(output_fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n",
609 fprintf(output_fp, "Used (Mem): \t\t%15d MB( %6d kB)\n",
612 fprintf(output_fp, "Used (Swap): \t\t%15d MB( %6d kB)\n",
613 swap_used >> 10, swap_used);
615 fprintf(output_fp, "Used (Zram block device): %13d MB( %6d kB)\n",
616 BYTE_TO_MBYTE(zram_used), BYTE_TO_KBYTE(zram_used));
618 fprintf(output_fp, "Used Ratio: \t\t%15d %%\n", used_ratio);
620 fprintf(output_fp, "Mem Free:\t\t%15d MB( %6d kB)\n",
623 fprintf(output_fp, "Available (Free+Reclaimable):%10d MB( %6d kB)\n",
629 static int get_tmpfs_info(FILE *output_fp)
633 char tmpfs_mp[NAME_MAX]; /* tmpfs mount point */
634 struct statfs tmpfs_info;
636 if (output_fp == NULL)
639 fp = fopen("/etc/mtab", "r");
644 "====================================================================\n");
645 fprintf(output_fp, "TMPFS INFO\n");
647 while (fgets(line, BUF_MAX, fp) != NULL) {
648 if (sscanf(line, "tmpfs %s tmpfs", tmpfs_mp) == 1) {
649 statfs(tmpfs_mp, &tmpfs_info);
651 "tmpfs %16s Total %8ld KB, Used %8ld, Avail %8ld\n",
653 /* 1 block is 4 KB */
654 tmpfs_info.f_blocks * 4,
655 (tmpfs_info.f_blocks - tmpfs_info.f_bfree) * 4,
656 tmpfs_info.f_bfree * 4);
663 mapinfo *load_maps(int pid)
670 sprintf(tmp, "/proc/%d/smaps", pid);
675 while ((mi = read_mapinfo(&smaps, ignore_smaps_field)) != 0) {
677 if ((!strcmp(mi->name, milist->name)
678 && (mi->name[0] != '['))) {
679 milist->size += mi->size;
680 milist->swap += mi->swap;
681 milist->rss += mi->rss;
682 milist->pss += mi->pss;
683 milist->shared_clean += mi->shared_clean;
684 milist->shared_dirty += mi->shared_dirty;
685 milist->private_clean += mi->private_clean;
686 milist->private_dirty += mi->private_dirty;
688 milist->perm[0] = mi->perm[0];
689 milist->perm[1] = mi->perm[1];
690 milist->perm[2] = mi->perm[2];
691 milist->perm[3] = mi->perm[3];
692 milist->end = mi->end;
693 strncpy(milist->perm, mi->perm, 4);
705 static geminfo *find_geminfo(unsigned int tgid, geminfo *gilist)
708 for (gi = gilist; gi; ) {
709 if (gi->tgid == tgid)
717 static void init_trib_mapinfo(trib_mapinfo *tmi)
721 tmi->shared_clean = 0;
722 tmi->shared_dirty = 0;
723 tmi->private_clean = 0;
724 tmi->private_dirty = 0;
726 tmi->shared_clean_pss = 0;
727 tmi->shared_dirty_pss = 0;
735 tmi->other_devices = 0;
740 get_trib_mapinfo(unsigned int tgid, mapinfo *milist,
741 geminfo *gilist, trib_mapinfo *result)
745 mapinfo *temp = NULL;
751 init_trib_mapinfo(result);
752 for (mi = milist; mi;) {
753 if (strstr(mi->name, STR_SGX_PATH)) {
754 result->graphic_3d += mi->pss;
755 } else if (strstr(mi->name, STR_3D_PATH1) ||
756 strstr(mi->name, STR_3D_PATH2)) {
757 result->graphic_3d += mi->size;
758 } else if (mi->rss != 0 && mi->pss == 0
759 && mi->shared_clean == 0
760 && mi->shared_dirty == 0
761 && mi->private_clean == 0
762 && mi->private_dirty == 0
764 result->other_devices += mi->size;
765 } else if (!strncmp(mi->name, STR_DRM_PATH1,
766 sizeof(STR_DRM_PATH1)) ||
767 !strncmp(mi->name, STR_DRM_PATH2,
768 sizeof(STR_DRM_PATH2))) {
769 result->gem_mmap += mi->rss;
771 result->shared_clean += mi->shared_clean;
772 result->shared_dirty += mi->shared_dirty;
773 result->private_clean += mi->private_clean;
774 result->private_dirty += mi->private_dirty;
775 result->swap += mi->swap;
776 result->rss += mi->rss;
777 result->pss += mi->pss;
778 result->size += mi->size;
780 if(mi->shared_clean != 0)
781 result->shared_clean_pss += mi->pss;
782 else if (mi->shared_dirty != 0)
783 result->shared_dirty_pss += mi->pss;
792 result->peak_rss = get_peak_rss(tgid);
793 if (result->peak_rss < result->rss)
794 result->peak_rss = result->rss;
795 if (result->gem_mmap > 0)
796 result->peak_rss -= result->gem_mmap;
798 gi = find_geminfo(tgid, gilist);
800 result->gem_rss = gi->rss_size;
801 result->gem_pss = gi->pss_size;
807 static int get_cmdline(unsigned int pid, char *cmdline)
810 char buf[NAME_MAX] = {0, };
813 sprintf(buf, "/proc/%d/cmdline", pid);
814 fp = fopen(buf, "r");
816 fprintf(stderr, "cannot file open %s\n", buf);
819 if ((ret = fscanf(fp, "%s", cmdline)) < 1) {
828 static int get_oomscoreadj(unsigned int pid)
834 sprintf(tmp, "/proc/%d/oom_score_adj", pid);
835 fp = fopen(tmp, "r");
841 if (fgets(tmp, sizeof(tmp), fp) == NULL) {
847 oomadj_val = atoi(tmp);
853 static void get_rss(pid_t pid, unsigned int *result)
856 char proc_path[PATH_MAX];
861 sprintf(proc_path, "/proc/%d/statm", pid);
862 fp = fopen(proc_path, "r");
866 if (fscanf(fp, "%*s %d", &rss) < 1) {
873 /* convert page to Kb */
878 static void show_rss(int output_type, char *output_path)
881 struct dirent *curdir;
883 char cmdline[PATH_MAX];
884 FILE *output_file = NULL;
888 pDir = opendir("/proc");
890 fprintf(stderr, "cannot read directory /proc.\n");
894 if (output_type == OUTPUT_FILE && output_path) {
895 output_file = fopen(output_path, "w+");
897 fprintf(stderr, "cannot open output file(%s)\n",
903 output_file = stdout;
907 " PID RSS OOM_SCORE COMMAND\n");
909 while ((curdir = readdir(pDir)) != NULL) {
910 pid = atoi(curdir->d_name);
911 if (pid < 1 || pid > 32768 || pid == getpid())
914 if (get_cmdline(pid, cmdline) < 0)
917 oom_score_adj = get_oomscoreadj(pid);
929 get_tmpfs_info(output_file);
930 get_mem_info(output_file);
938 static int show_map_all_new(int output_type, char *output_path)
941 struct dirent *curdir;
945 unsigned total_pss = 0;
946 unsigned total_private = 0;
947 unsigned total_private_code = 0;
948 unsigned total_private_data = 0;
949 unsigned total_shared_code = 0;
950 unsigned total_shared_data = 0;
951 unsigned total_shared_code_pss = 0;
952 unsigned total_shared_data_pss = 0;
953 unsigned total_swap = 0;
954 unsigned total_rss = 0;
955 unsigned total_graphic_3d = 0;
956 unsigned total_gem_rss = 0;
957 unsigned total_gem_pss = 0;
958 unsigned total_peak_rss = 0;
959 unsigned total_allocated_gem = 0;
961 char cmdline[PATH_MAX];
962 FILE *output_file = NULL;
965 pDir = opendir("/proc");
967 fprintf(stderr, "cannot read directory /proc.\n");
971 if (output_type == OUTPUT_FILE && output_path) {
972 output_file = fopen(output_path, "w+");
974 fprintf(stderr, "cannot open output file(%s)\n",
980 output_file = stdout;
982 glist = load_geminfo();
987 " PID S(CODE) S(DATA) P(CODE) P(DATA)"
989 " GEM(PSS) GEM(RSS) SWAP"
990 " OOM_SCORE_ADJ COMMAND\n");
993 " PID CODE DATA PEAK PSS"
994 " 3D GEM(PSS) SWAP COMMAND\n");
997 while ((curdir = readdir(pDir)) != NULL) {
998 pid = atoi(curdir->d_name);
999 if (pid < 1 || pid > 32768 || pid == getpid())
1002 if (get_cmdline(pid, cmdline) < 0)
1005 milist = load_maps(pid);
1009 /* get classified map info */
1010 get_trib_mapinfo(pid, milist, glist, &tmi);
1011 oom_score_adj = get_oomscoreadj(pid);
1015 fprintf(output_file,
1016 "%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d %8d"
1019 tmi.shared_clean, tmi.shared_dirty,
1020 tmi.private_clean, tmi.private_dirty,
1021 tmi.peak_rss, tmi.pss, tmi.graphic_3d,
1022 tmi.gem_pss, tmi.gem_rss, tmi.swap,
1023 oom_score_adj, cmdline);
1025 fprintf(output_file,
1026 "%8d %8d %8d %8d %8d %8d %8d %8d %s\n",
1030 tmi.shared_dirty + tmi.private_dirty,
1037 if (tmi.other_devices != 0)
1038 fprintf(output_file,
1039 "%s(%d) %d KB may mapped by device(s).\n",
1040 cmdline, pid, tmi.other_devices);
1043 total_private += (tmi.private_clean + tmi.private_dirty);
1044 total_pss += tmi.pss;
1045 total_rss += tmi.rss;
1046 total_graphic_3d += tmi.graphic_3d;
1047 total_gem_rss += tmi.gem_rss;
1048 total_gem_pss += tmi.gem_pss;
1049 total_private_code += tmi.private_clean;
1050 total_private_data += tmi.private_dirty;
1051 total_swap += tmi.swap;
1052 total_shared_code += tmi.shared_clean;
1053 total_shared_data += tmi.shared_dirty;
1054 total_peak_rss += tmi.peak_rss;
1056 total_shared_code_pss += tmi.shared_clean_pss;
1057 total_shared_data_pss += tmi.shared_dirty_pss;
1059 } /* end of while */
1061 total_allocated_gem = KB(total_gem_memory());
1062 fprintf(output_file,
1063 "==============================================="
1064 "===============================================\n");
1066 fprintf(output_file,
1067 "TOTAL: S(CODE) S(DATA) P(CODE) P(DATA)"
1069 "GEM(PSS) GEM(RSS) GEM(ALLOC) SWAP TOTAL(KB)\n");
1070 fprintf(output_file,
1071 " %8d %8d %8d %8d %8d %8d %8d"
1072 " %8d %8d %8d %8d %8d\n",
1073 total_shared_code, total_shared_data,
1074 total_private_code, total_private_data,
1075 total_peak_rss, total_pss, total_graphic_3d,
1076 total_gem_pss, total_gem_rss,
1077 total_allocated_gem, total_swap,
1078 total_pss + total_graphic_3d +
1079 total_allocated_gem);
1081 fprintf(output_file,
1082 "TOTAL: CODE DATA PEAK PSS "
1083 "3D GEM(PSS) GEM(ALLOC) TOTAL(KB)\n");
1084 fprintf(output_file, " %8d %8d %8d %8d %8d %8d %7d %8d %8d\n",
1085 total_shared_code + total_private_code,
1086 total_shared_data + total_private_data,
1087 total_peak_rss, total_pss,
1088 total_graphic_3d, total_gem_pss,
1089 total_allocated_gem, total_swap,
1090 total_pss + total_graphic_3d +
1091 total_allocated_gem);
1096 fprintf(output_file,
1097 "* S(CODE): shared clean memory, it includes"
1098 " duplicated memory\n"
1099 "* S(DATA): shared dirty memory, it includes"
1100 " duplicated memory\n"
1101 "* P(CODE): private clean memory\n"
1102 "* P(DATA): private dirty memory\n"
1103 "* PEAK: peak memory usage of S(CODE) + S(DATA)"
1104 " + P(CODE) + P(DATA)\n"
1105 "* PSS: Proportional Set Size\n"
1106 "* 3D: memory allocated by GPU driver\n"
1107 "* GEM(PSS): GEM memory devided by # of sharers\n"
1108 "* GEM(RSS): GEM memory including duplicated memory\n"
1109 "* GEM(ALLOC): sum of unique gem memory in the system\n"
1110 "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1112 fprintf(output_file,
1113 "* CODE: shared and private clean memory\n"
1114 "* DATA: shared and private dirty memory\n"
1115 "* PEAK: peak memory usage of CODE + DATA\n"
1116 "* PSS: Proportional Set Size\n"
1117 "* 3D: memory allocated by GPU driver\n"
1118 "* GEM(PSS): GEM memory deviced by # of sharers\n"
1119 "* GEM(ALLOC): sum of unique GEM memory in the system\n"
1120 "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1122 get_tmpfs_info(output_file);
1123 get_memcg_info(output_file);
1124 get_mem_info(output_file);
1126 fclose(output_file);
1132 static int show_map_new(int pid)
1136 unsigned shared_dirty = 0;
1137 unsigned shared_clean = 0;
1138 unsigned private_dirty = 0;
1139 unsigned private_clean = 0;
1143 unsigned private_clean_total = 0;
1144 unsigned private_dirty_total = 0;
1145 unsigned shared_clean_total = 0;
1146 unsigned shared_dirty_total = 0;
1147 int duplication = 0;
1149 milist = load_maps(pid);
1152 fprintf(stderr, "cannot get /proc/smaps for pid %d\n", pid);
1157 printf(" S(CODE) S(DATA) P(CODE) P(DATA) ADDR(start-end)"
1159 printf("-------- -------- -------- -------- -----------------"
1160 "------------------------------\n");
1162 printf(" S(CODE) S(DATA) P(CODE) P(DATA) PSS\n");
1163 printf("-------- -------- -------------------"
1164 "------------------\n");
1166 for (mi = milist; mi; mi = mi->next) {
1167 shared_clean += mi->shared_clean;
1168 shared_dirty += mi->shared_dirty;
1169 private_clean += mi->private_clean;
1170 private_dirty += mi->private_dirty;
1173 shared_clean_total += mi->shared_clean;
1174 shared_dirty_total += mi->shared_dirty;
1175 private_clean_total += mi->private_clean;
1176 private_dirty_total += mi->private_dirty;
1181 if ((mi->next && !strcmp(mi->next->name, mi->name)) &&
1182 (mi->next->start == mi->end)) {
1190 printf("%8d %8d %8d %8d %08x-%08x %s\n",
1191 shared_clean, shared_dirty, private_clean, private_dirty,
1192 start, end, mi->name);
1200 printf("%8d %8d %8d %8d %18d\n",
1203 private_clean_total,
1204 private_dirty_total,
1211 void check_kernel_version(void)
1219 if (buf.release[0] == '3') {
1223 pch = strstr(buf.release, ".");
1224 strncpy(str, pch+1, 2);
1225 sub_version = atoi(str);
1227 if (sub_version >= 10)
1228 ignore_smaps_field = 8; /* Referenced, Anonymous, AnonHugePages,
1229 Swap, KernelPageSize, MMUPageSize,
1233 ignore_smaps_field = 7; /* Referenced, Anonymous, AnonHugePages,
1234 Swap, KernelPageSize, MMUPageSize,
1237 ignore_smaps_field = 4; /* Referenced, Swap, KernelPageSize,
1243 int main(int argc, char *argv[])
1249 check_kernel_version();
1251 if (!strcmp(argv[1], "-r")) {
1253 show_rss(OUTPUT_FILE, argv[2]);
1255 show_rss(OUTPUT_UART, NULL);
1257 } else if (!strcmp(argv[1], "-s")) {
1259 if (argc == 3 && atoi(argv[2]) > 0) {
1260 show_map_new(atoi(argv[2]));
1263 } else if (!strcmp(argv[1], "-a")) {
1265 show_map_all_new(OUTPUT_UART, NULL);
1267 } else if (!strcmp(argv[1], "-v")) {
1269 show_map_all_new(OUTPUT_UART, NULL);
1271 } else if (!strcmp(argv[1], "-f")) {
1274 show_map_all_new(OUTPUT_FILE, argv[2]);
1277 } else if (argc == 2 && atoi(argv[1]) > 0) {
1278 show_map_new(atoi(argv[1]));
1284 "memps [-a] | [-v] | [-s] <pid> | [-f] <output file full path>\n"
1285 " -s = sum (show only sum of each)\n"
1286 " -f = all (show all processes via output file)\n"
1287 " -a = all (show all processes)\n"
1288 " -v = verbos (show all processes in detail)\n");