Merged latest memps code providing swap and cgroup usage
[platform/core/system/memps.git] / memps.c
1 /*
2    Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
3
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6
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
12 */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdbool.h>
17 #include <math.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <sys/vfs.h>
25 #include <linux/limits.h>
26
27 #include <ctype.h>
28 #include <stddef.h>
29
30 #include <dirent.h>
31 #include <sys/utsname.h>
32
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"
40
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)
44
45 #define BYTE_TO_KBYTE(b) ((b) >> 10)
46 #define BYTE_TO_MBYTE(b) ((b) >> 20)
47 #define BYTE_TO_GBYTE(b) ((b) >> 30)
48
49 #define KBYTE_TO_BYTE(k) ((k) << 10)
50 #define KBYTE_TO_MBYTE(k) ((k) >> 10)
51 #define KBYTE_TO_GBYTE(k) ((k) >> 20)
52
53 #define GBYTE_TO_BYTE(g) ((g) << 30)
54 #define GBYTE_TO_KBYTE(g) ((g) << 20)
55 #define GBYTE_TO_MBYTE(g) ((g) << 10)
56
57 typedef struct geminfo geminfo;
58 typedef struct mapinfo mapinfo;
59 typedef struct trib_mapinfo trib_mapinfo;
60
61 enum {
62         OUTPUT_UART,
63         OUTPUT_FILE,
64         NUM_OUTPUT_TYPE
65 };
66
67 struct mapinfo {
68         mapinfo *next;
69         unsigned start;
70         unsigned end;
71         unsigned size;
72         unsigned swap;
73         unsigned rss;
74         unsigned pss;
75         unsigned shared_clean;
76         unsigned shared_dirty;
77         unsigned private_clean;
78         unsigned private_dirty;
79         char perm[4];
80         char name[1];
81 };
82
83 /* classify normal, graphic and other devices memory */
84 struct trib_mapinfo {
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;
91         unsigned swap;
92         unsigned rss;
93         unsigned pss;
94         unsigned size;
95         unsigned graphic_3d;
96         unsigned gem_rss;
97         unsigned gem_pss;
98         unsigned peak_rss;
99         unsigned other_devices;
100         unsigned gem_mmap;
101 };
102
103 struct geminfo {
104         geminfo *next;
105         unsigned int tgid;
106         unsigned rss_size;
107         unsigned pss_size;
108         unsigned hcount;
109 };
110
111 static int ignore_smaps_field;
112 static int sum;
113 static int verbos;
114
115 /* reads file contents into memory */
116 static char* cread(const char* path)
117 {
118         /* once allocated area for reads */
119         static char*    text = NULL;
120         static size_t   size = 0;
121
122         ssize_t ret;
123         char*   ptr = text;
124         size_t  cap = size;
125         int     fd  = open(path, O_RDONLY);
126
127         if (fd < 0) {
128                 return NULL;
129         }
130
131         do {
132                 /* ensure we have enough space */
133                 if (cap == 0) {
134                         ptr = (char*)realloc(text, size + BUF_INC_SIZE);
135                         if (ptr == NULL) {
136                                 ret = -1;
137                                 break;
138                         }
139
140                         text  = ptr;
141                         ptr   = text + size;
142                         cap   = BUF_INC_SIZE;
143                         size += BUF_INC_SIZE;
144                 }
145                 ret = read(fd, ptr, cap);
146                 if (ret == 0) {
147                         *ptr = 0;
148                 } else if (ret > 0) {
149                         cap -= ret;
150                         ptr += ret;
151                 }
152         } while (ret > 0);
153         close(fd);
154
155         return (ret < 0 ? NULL : text);
156 } /* cread */
157
158 /* like fgets/gets but adjusting contents pointer */
159 static inline char* cgets(char** contents)
160 {
161         if (contents && *contents && **contents) {
162                 char* bos = *contents;          /* begin of string */
163                 char* eos = strchr(bos, '\n');  /* end of string   */
164
165                 if (eos) {
166                         *contents = eos + 1;
167                         *eos      = 0;
168                 } else {
169                         *contents = NULL;
170                 }
171                 return bos;
172         }
173
174         return NULL;
175 } /* cgets */
176
177
178 static unsigned get_peak_rss(unsigned int pid)
179 {
180         static const char field[] = "VmHWM:";
181         char tmp[128];
182         char* line;
183         char* value;
184
185         sprintf(tmp, "/proc/%d/status", pid);
186         line = cread(tmp);
187         if (line == NULL) {
188                 fprintf(stderr, "cannot open %s\n", tmp);
189                 return 0;
190         }
191
192         value = strstr(line, field);
193         if (value) {
194                 value += sizeof(field);
195                 return strtoul(value, NULL, 10);
196         }
197
198         return 0;
199 }
200 #define NUM_GEM_FIELD 6
201
202 static geminfo *read_geminfo(FILE *fp)
203 {
204         geminfo *tgeminfo;
205         char line[BUF_MAX];
206         unsigned int pid, tgid, handle, refcount, hcount;
207         unsigned gem_size;
208
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)
213                         return NULL;
214
215                 if (hcount == 0)
216                         return NULL;
217                 tgeminfo = malloc(sizeof(geminfo));
218                 if (tgeminfo == NULL)
219                         return 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);
224         } else
225                 return NULL;
226
227         return tgeminfo;
228 }
229
230
231 static geminfo *load_geminfo(void)
232 {
233         geminfo *ginfo;
234         geminfo *gilist = NULL;
235         FILE *drm_fp;
236         char line[BUF_MAX];
237
238         drm_fp = fopen("/sys/kernel/debug/dri/0/gem_info", "r");
239
240         if (drm_fp == NULL) {
241                 fprintf(stderr,
242                 "cannot open /sys/kernel/debug/dri/0/gem_info\n");
243                 return NULL;
244         }
245
246         if (fgets(line, BUF_MAX, drm_fp) == NULL) {
247                 fclose(drm_fp);
248                 return NULL;
249         } else {
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];
254
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++)) {
259                                 if (in_field) {
260                                         /* end of field */
261                                         in_field = 0;
262                                 }
263                         } else {
264                                 if (!in_field) {
265                                         /* next field started */
266                                         in_field = 1;
267                                         size++;
268                                 }
269                         }
270                 } /* while */
271
272                 if (size != NUM_GEM_FIELD) {
273                         fclose(drm_fp);
274                         return NULL;
275                 }
276         }
277
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;
282                         free(ginfo);
283                         continue;
284                 }
285                 ginfo->next = gilist;
286                 gilist = ginfo;
287         }
288
289         fclose(drm_fp);
290
291         return gilist;
292 }
293
294
295 /* 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /android/lib/libcomposer.so
296  * 012345678901234567890123456789012345678901234567890123456789
297  * 0         1         2         3         4         5
298  */
299
300 mapinfo *read_mapinfo(char** smaps, int rest_line)
301 {
302         char* line;
303         mapinfo *mi;
304         int len;
305         int tmp;
306
307         if ((line = cgets(smaps)) == 0)
308                 return 0;
309
310         len    = strlen(line);
311         if (len < 1)
312                 return 0;
313
314         mi = malloc(sizeof(mapinfo) + len + 16);
315         if (mi == 0)
316                 return 0;
317
318         mi->start = strtoul(line, 0, 16);
319         mi->end = strtoul(line + 9, 0, 16);
320
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 */
325
326         if (len < 50)
327                 strcpy(mi->name, "[anon]");
328         else
329                 strcpy(mi->name, line + 49);
330
331         if ((line = cgets(smaps)) == 0)
332                 goto oops;
333         if (sscanf(line, "Size: %d kB", &mi->size) != 1)
334                 goto oops;
335         if ((line = cgets(smaps)) == 0)
336                 goto oops;
337         if (sscanf(line, "Rss: %d kB", &mi->rss) != 1)
338                 goto oops;
339         if ((line = cgets(smaps)) == 0)
340                 goto oops;
341         if (sscanf(line, "Pss: %d kB", &mi->pss) == 1)
342                 if ((line = cgets(smaps)) == 0)
343                         goto oops;
344         if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1)
345                 goto oops;
346         if ((line = cgets(smaps)) == 0)
347                 goto oops;
348         if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1)
349                 goto oops;
350         if ((line = cgets(smaps)) == 0)
351                 goto oops;
352         if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1)
353                 goto oops;
354         if ((line = cgets(smaps)) == 0)
355                 goto oops;
356         if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1)
357                 goto oops;
358
359         while (rest_line-- && (line = cgets(smaps))) {
360                 if (sscanf(line, "Swap: %d kB", &tmp) == 1) {
361                         mi->swap = tmp;
362                         //rest_line++;
363                 }
364                 if (sscanf(line, "PSwap: %d kB", &tmp) == 1)
365                         rest_line++;
366         }
367
368         return mi;
369  oops:
370         printf("mi get error\n");
371         free(mi);
372         return 0;
373 }
374
375 static unsigned total_gem_memory(void)
376 {
377         FILE *gem_fp;
378         unsigned total_gem_mem = 0;
379         unsigned name, size, handles, refcount;
380         char line[BUF_MAX];
381
382         gem_fp = fopen("/proc/dri/0/gem_names", "r");
383         if(gem_fp == NULL) {
384                 fprintf(stderr,
385                 "cannot open /proc/dir/0/gem_names\n");
386                 return 0;
387         }
388
389         if (fgets(line, BUF_MAX, gem_fp) == NULL) {
390                 fclose(gem_fp);
391                 return 0;
392         }
393
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;
398         fclose(gem_fp);
399
400         return total_gem_mem;
401 }
402
403
404 int fread_uint(const char *path, u_int32_t *number)
405 {
406         FILE *f = NULL;
407         int ret;
408
409         f = fopen(path, "r");
410
411         if(!f) {
412                 fprintf(stderr,"Fail to open %s file.\n", path);
413                 return -1;
414         }
415
416         ret = fscanf(f, "%u", number);
417         if(ret == EOF) {
418                 fprintf(stderr,"Fail to read file\n");
419                 fclose(f);
420                 return -1;
421         }
422
423         fclose(f);
424         return 0;
425 }
426
427 #define MAX_PATH_LENGTH 512
428 static int cgroup_read_node(const char *cgroup_name,
429                 const char *file_name, unsigned int *value)
430 {
431         char buf[MAX_PATH_LENGTH];
432         int ret;
433         snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name);
434         ret = fread_uint(buf, value);
435         return ret;
436 }
437
438 /**
439  * @desc Provides usage in bytes for provided memory cgroup. Works
440  * with/without swap accounting.
441  *
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
445  */
446 static unsigned int get_memcg_usage(const char *memcg_path, bool swap)
447 {
448         int ret;
449         unsigned int usage;
450
451         if (swap) {
452                 ret = cgroup_read_node(memcg_path,
453                                 "/memory.memsw.usage_in_bytes", &usage);
454         } else {
455                 ret = cgroup_read_node(memcg_path, "/memory.usage_in_bytes",
456                                 &usage);
457         }
458
459         if (ret != 0)
460                 usage = 0;
461
462         return usage;
463 }
464
465 static void get_memcg_info(FILE *output_fp)
466 {
467         char buf[PATH_MAX];
468         DIR *pdir = NULL;
469         struct dirent entry;
470         struct dirent *result;
471         struct stat path_stat;
472         long usage_swap;
473         unsigned long usage, usage_with_swap;
474         int ret;
475
476         fprintf(output_fp,"====================================================================\n");
477         fprintf(output_fp,"MEMORY CGROUPS USAGE INFO\n");
478
479         pdir = opendir(MEMCG_PATH);
480         if (pdir == NULL) {
481                 fprintf(stderr,"cannot read directory %s", MEMCG_PATH);
482                 return;
483         }
484
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)
489                         continue;
490
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)))
494                         continue;
495
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;
500                 if (usage_swap < 0)
501                         usage_swap = 0;
502
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));
512                 else
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));
520
521         }
522
523         closedir(pdir);
524 }
525
526 static void get_mem_info(FILE *output_fp)
527 {
528         char buf[PATH_MAX];
529         FILE *fp;
530         char *idx;
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;
535
536         if (output_fp == NULL)
537                 return;
538
539         fp = fopen("/proc/meminfo", "r");
540
541         if (!fp) {
542                 fprintf(stderr, "%s open failed, %p", buf, fp);
543                 return;
544         }
545
546         while (fgets(buf, PATH_MAX, fp) != NULL) {
547                 if ((idx = strstr(buf, "MemTotal:"))) {
548                         idx += strlen("Memtotal:");
549                         while (*idx < '0' || *idx > '9')
550                                 idx++;
551                         total_mem = atoi(idx);
552                 } else if ((idx = strstr(buf, "MemFree:"))) {
553                         idx += strlen("MemFree:");
554                         while (*idx < '0' || *idx > '9')
555                                 idx++;
556                         free = atoi(idx);
557                 } else if ((idx = strstr(buf, "MemAvailable:"))) {
558                         idx += strlen("MemAvailable:");
559                         while (*idx < '0' || *idx > '9')
560                                 idx++;
561                         available = atoi(idx);
562                 } else if((idx = strstr(buf, "Cached:")) && !strstr(buf, "Swap")) {
563                         idx += strlen("Cached:");
564                         while (*idx < '0' || *idx > '9')
565                                 idx++;
566                         cached = atoi(idx);
567                 } else if((idx = strstr(buf, "SwapTotal:"))) {
568                         idx += strlen("SwapTotal:");
569                         while (*idx < '0' || *idx > '9')
570                                 idx++;
571                         swap_total = atoi(idx);
572                 } else if((idx = strstr(buf, "SwapFree:"))) {
573                         idx += strlen("SwapFree");
574                         while (*idx < '0' || *idx > '9')
575                                 idx++;
576                         swap_free = atoi(idx);
577                         break;
578                 }
579         }
580
581         if (total_mem == 0) {
582                 fprintf(stderr, "cannot get total memory size\n");
583                 fclose(fp);
584                 return;
585         }
586
587         if (available == 0)
588                 available = free + cached;
589         used = total_mem - available;
590         used_ratio = used * 100 / total_mem;
591         swap_used = swap_total - swap_free;
592
593         if (fread_uint(ZRAM_USED_PATH, &zram_used) != 0)
594                 zram_used = 0;
595
596         fprintf(output_fp,
597                 "====================================================================\n");
598
599
600         fprintf(output_fp, "Total RAM size: \t%15d MB( %6d kB)\n",
601                         total_mem >> 10, total_mem);
602
603         fprintf(output_fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n",
604                         (total_mem - free) >> 10, total_mem - free);
605
606         fprintf(output_fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n",
607                         used >> 10, used);
608
609         fprintf(output_fp, "Used (Mem):  \t\t%15d MB( %6d kB)\n",
610                         used >> 10, used);
611
612         fprintf(output_fp, "Used (Swap): \t\t%15d MB( %6d kB)\n",
613                         swap_used >> 10, swap_used);
614
615         fprintf(output_fp, "Used (Zram block device): %13d MB( %6d kB)\n",
616             BYTE_TO_MBYTE(zram_used), BYTE_TO_KBYTE(zram_used));
617
618         fprintf(output_fp, "Used Ratio: \t\t%15d  %%\n", used_ratio);
619
620         fprintf(output_fp, "Mem Free:\t\t%15d MB( %6d kB)\n",
621                         free >> 10, free);
622
623         fprintf(output_fp, "Available (Free+Reclaimable):%10d MB( %6d kB)\n",
624                         available >> 10,
625                         available);
626         fclose(fp);
627 }
628
629 static int get_tmpfs_info(FILE *output_fp)
630 {
631         FILE *fp;
632         char line[BUF_MAX];
633         char tmpfs_mp[NAME_MAX];        /* tmpfs mount point */
634         struct statfs tmpfs_info;
635
636         if (output_fp == NULL)
637                 return -1;
638
639         fp = fopen("/etc/mtab", "r");
640         if (fp == NULL)
641                 return -1;
642
643         fprintf(output_fp,
644                 "====================================================================\n");
645         fprintf(output_fp, "TMPFS INFO\n");
646
647         while (fgets(line, BUF_MAX, fp) != NULL) {
648                 if (sscanf(line, "tmpfs %s tmpfs", tmpfs_mp) == 1) {
649                         statfs(tmpfs_mp, &tmpfs_info);
650                         fprintf(output_fp,
651                                 "tmpfs %16s  Total %8ld KB, Used %8ld, Avail %8ld\n",
652                                 tmpfs_mp,
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);
657                 }
658         }
659         fclose(fp);
660         return 0;
661 }
662
663 mapinfo *load_maps(int pid)
664 {
665         char* smaps;
666         char tmp[128];
667         mapinfo *milist = 0;
668         mapinfo *mi;
669
670         sprintf(tmp, "/proc/%d/smaps", pid);
671         smaps = cread(tmp);
672         if (smaps == NULL)
673                 return 0;
674
675         while ((mi = read_mapinfo(&smaps, ignore_smaps_field)) != 0) {
676                 if (milist) {
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;
687
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);
694                                 free(mi);
695                                 continue;
696                         }
697                 }
698                 mi->next = milist;
699                 milist = mi;
700         }
701
702         return milist;
703 }
704
705 static geminfo *find_geminfo(unsigned int tgid, geminfo *gilist)
706 {
707         geminfo *gi;
708         for (gi = gilist; gi; ) {
709                 if (gi->tgid == tgid)
710                         return gi;
711
712                 gi = gi->next;
713         }
714         return NULL;
715 }
716
717 static void init_trib_mapinfo(trib_mapinfo *tmi)
718 {
719         if (!tmi)
720                 return;
721         tmi->shared_clean = 0;
722         tmi->shared_dirty = 0;
723         tmi->private_clean = 0;
724         tmi->private_dirty = 0;
725         tmi->swap = 0;
726         tmi->shared_clean_pss = 0;
727         tmi->shared_dirty_pss = 0;
728         tmi->rss = 0;
729         tmi->pss = 0;
730         tmi->size = 0;
731         tmi->graphic_3d = 0;
732         tmi->gem_rss = 0;
733         tmi->gem_pss = 0;
734         tmi->peak_rss = 0;
735         tmi->other_devices = 0;
736         tmi->gem_mmap = 0;
737 }
738
739 static int
740 get_trib_mapinfo(unsigned int tgid, mapinfo *milist,
741                  geminfo *gilist, trib_mapinfo *result)
742
743 {
744         mapinfo *mi;
745         mapinfo *temp = NULL;
746         geminfo *gi;
747
748         if (!result)
749                 return -EINVAL;
750
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
763                            && mi->swap == 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;
770                 } else {
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;
779
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;
784                 }
785
786                 temp = mi;
787                 mi = mi->next;
788                 free(temp);
789                 temp = NULL;
790         }
791
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;
797
798         gi = find_geminfo(tgid, gilist);
799         if (gi != NULL) {
800                 result->gem_rss = gi->rss_size;
801                 result->gem_pss = gi->pss_size;
802         }
803
804         return 0;
805 }
806
807 static int get_cmdline(unsigned int pid, char *cmdline)
808 {
809         FILE *fp;
810         char buf[NAME_MAX] = {0, };
811         int ret = -1;
812
813         sprintf(buf, "/proc/%d/cmdline", pid);
814         fp = fopen(buf, "r");
815         if (fp == 0) {
816                 fprintf(stderr, "cannot file open %s\n", buf);
817                 return ret;
818         }
819         if ((ret = fscanf(fp, "%s", cmdline)) < 1) {
820                 fclose(fp);
821                 return ret;
822         }
823         fclose(fp);
824
825         return ret;
826 }
827
828 static int get_oomscoreadj(unsigned int pid)
829 {
830         FILE *fp;
831         char tmp[256];
832         int oomadj_val;
833
834         sprintf(tmp, "/proc/%d/oom_score_adj", pid);
835         fp = fopen(tmp, "r");
836
837         if (fp == NULL) {
838                 oomadj_val = -50;
839                 return oomadj_val;
840         }
841         if (fgets(tmp, sizeof(tmp), fp) == NULL) {
842                 oomadj_val = -100;
843                 fclose(fp);
844                 return oomadj_val;
845         }
846
847         oomadj_val = atoi(tmp);
848
849         fclose(fp);
850         return oomadj_val;
851 }
852
853 static void get_rss(pid_t pid, unsigned int *result)
854 {
855         FILE *fp;
856         char proc_path[PATH_MAX];
857         int rss = 0;
858
859         *result = 0;
860
861         sprintf(proc_path, "/proc/%d/statm", pid);
862         fp = fopen(proc_path, "r");
863         if (fp == NULL)
864                 return;
865
866         if (fscanf(fp, "%*s %d", &rss) < 1) {
867                 fclose(fp);
868                 return;
869         }
870
871         fclose(fp);
872
873         /* convert page to Kb */
874         *result = rss * 4;
875         return;
876 }
877
878 static void show_rss(int output_type, char *output_path)
879 {
880         DIR *pDir = NULL;
881         struct dirent *curdir;
882         pid_t pid;
883         char cmdline[PATH_MAX];
884         FILE *output_file = NULL;
885         int oom_score_adj;
886         unsigned int rss;
887
888         pDir = opendir("/proc");
889         if (pDir == NULL) {
890                 fprintf(stderr, "cannot read directory /proc.\n");
891                 return;
892         }
893
894         if (output_type == OUTPUT_FILE && output_path) {
895                 output_file = fopen(output_path, "w+");
896                 if (!output_file) {
897                         fprintf(stderr, "cannot open output file(%s)\n",
898                                 output_path);
899                         closedir(pDir);
900                         exit(1);
901                 }
902         } else
903                 output_file = stdout;
904
905
906         fprintf(output_file,
907                         "     PID    RSS    OOM_SCORE    COMMAND\n");
908
909         while ((curdir = readdir(pDir)) != NULL) {
910                 pid = atoi(curdir->d_name);
911                 if (pid < 1 || pid > 32768 || pid == getpid())
912                         continue;
913
914                 if (get_cmdline(pid, cmdline) < 0)
915                         continue;
916                 get_rss(pid, &rss);
917                 oom_score_adj = get_oomscoreadj(pid);
918
919                 fprintf(output_file,
920                                 "%8d %8u %8d          %s\n",
921                                 pid,
922                                 rss,
923                                 oom_score_adj,
924                                 cmdline);
925
926
927         } /* end of while */
928
929         get_tmpfs_info(output_file);
930         get_mem_info(output_file);
931
932         fclose(output_file);
933         closedir(pDir);
934
935         return;
936 }
937
938 static int show_map_all_new(int output_type, char *output_path)
939 {
940         DIR *pDir = NULL;
941         struct dirent *curdir;
942         unsigned int pid;
943         mapinfo *milist;
944         geminfo *glist;
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;
960         trib_mapinfo tmi;
961         char cmdline[PATH_MAX];
962         FILE *output_file = NULL;
963         int oom_score_adj;
964
965         pDir = opendir("/proc");
966         if (pDir == NULL) {
967                 fprintf(stderr, "cannot read directory /proc.\n");
968                 return 0;
969         }
970
971         if (output_type == OUTPUT_FILE && output_path) {
972                 output_file = fopen(output_path, "w+");
973                 if (!output_file) {
974                         fprintf(stderr, "cannot open output file(%s)\n",
975                                 output_path);
976                         closedir(pDir);
977                         exit(1);
978                 }
979         } else
980                 output_file = stdout;
981
982         glist = load_geminfo();
983
984         if (!sum) {
985                 if (verbos)
986                         fprintf(output_file,
987                                         "     PID  S(CODE)  S(DATA)  P(CODE)  P(DATA)"
988                                         "     PEAK      PSS       3D"
989                                         "     GEM(PSS)  GEM(RSS)    SWAP"
990                                         "     OOM_SCORE_ADJ    COMMAND\n");
991                 else
992                         fprintf(output_file,
993                                         "     PID     CODE     DATA     PEAK     PSS"
994                                         "     3D      GEM(PSS)      SWAP      COMMAND\n");
995         }
996
997         while ((curdir = readdir(pDir)) != NULL) {
998                 pid = atoi(curdir->d_name);
999                 if (pid < 1 || pid > 32768 || pid == getpid())
1000                         continue;
1001
1002                 if (get_cmdline(pid, cmdline) < 0)
1003                         continue;
1004
1005                 milist = load_maps(pid);
1006                 if (milist == 0)
1007                         continue;
1008
1009                 /* get classified map info */
1010                 get_trib_mapinfo(pid, milist, glist, &tmi);
1011                 oom_score_adj = get_oomscoreadj(pid);
1012
1013                 if (!sum) {
1014                         if (verbos)
1015                                 fprintf(output_file,
1016                                         "%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d %8d"
1017                                         " %8d \t\t%s\n",
1018                                         pid,
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);
1024                         else
1025                                 fprintf(output_file,
1026                                         "%8d %8d %8d %8d %8d %8d %8d %8d      %s\n",
1027                                         pid,
1028                                         tmi.shared_clean +
1029                                         tmi.private_clean,
1030                                         tmi.shared_dirty + tmi.private_dirty,
1031                                         tmi.peak_rss,
1032                                         tmi.pss,
1033                                         tmi.graphic_3d,
1034                                         tmi.gem_pss,
1035                                         tmi.swap, cmdline);
1036
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);
1041                 }
1042
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;
1055
1056                 total_shared_code_pss += tmi.shared_clean_pss;
1057                 total_shared_data_pss += tmi.shared_dirty_pss;
1058
1059         } /* end of while */
1060
1061         total_allocated_gem = KB(total_gem_memory());
1062         fprintf(output_file,
1063                         "==============================================="
1064                         "===============================================\n");
1065         if (verbos) {
1066                 fprintf(output_file,
1067                                 "TOTAL:      S(CODE) S(DATA) P(CODE)  P(DATA)"
1068                                 "    PEAK     PSS       3D    "
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);
1080         } else {
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);
1092
1093         }
1094
1095         if (verbos)
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");
1111         else
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");
1121
1122         get_tmpfs_info(output_file);
1123         get_memcg_info(output_file);
1124         get_mem_info(output_file);
1125
1126         fclose(output_file);
1127         free(glist);
1128         closedir(pDir);
1129         return 1;
1130 }
1131
1132 static int show_map_new(int pid)
1133 {
1134         mapinfo *milist;
1135         mapinfo *mi;
1136         unsigned shared_dirty = 0;
1137         unsigned shared_clean = 0;
1138         unsigned private_dirty = 0;
1139         unsigned private_clean = 0;
1140         unsigned pss = 0;
1141         unsigned start = 0;
1142         unsigned end = 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;
1148
1149         milist = load_maps(pid);
1150
1151         if (milist == 0) {
1152                 fprintf(stderr, "cannot get /proc/smaps for pid %d\n", pid);
1153                 return 1;
1154         }
1155
1156         if (!sum) {
1157                 printf(" S(CODE)  S(DATA)  P(CODE)  P(DATA)  ADDR(start-end)"
1158                         "OBJECT NAME\n");
1159                 printf("-------- -------- -------- -------- -----------------"
1160                         "------------------------------\n");
1161         } else {
1162                 printf(" S(CODE)  S(DATA)  P(CODE)  P(DATA)  PSS\n");
1163                 printf("-------- -------- -------------------"
1164                         "------------------\n");
1165         }
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;
1171                 pss += mi->pss;
1172
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;
1177
1178                 if (!duplication)
1179                         start = mi->start;
1180
1181                 if ((mi->next && !strcmp(mi->next->name, mi->name)) &&
1182                     (mi->next->start == mi->end)) {
1183                         duplication = 1;
1184                         continue;
1185                 }
1186                 end = mi->end;
1187                 duplication = 0;
1188
1189                 if (!sum) {
1190                         printf("%8d %8d %8d %8d %08x-%08x %s\n",
1191                                shared_clean, shared_dirty, private_clean, private_dirty,
1192                                start, end, mi->name);
1193                 }
1194                 shared_clean = 0;
1195                 shared_dirty = 0;
1196                 private_clean = 0;
1197                 private_dirty = 0;
1198         }
1199         if (sum) {
1200                 printf("%8d %8d %8d %8d %18d\n",
1201                        shared_clean_total,
1202                        shared_dirty_total,
1203                        private_clean_total,
1204                        private_dirty_total,
1205                        pss);
1206         }
1207
1208         return 1;
1209 }
1210
1211 void check_kernel_version(void)
1212 {
1213         struct utsname buf;
1214         int ret;
1215
1216         ret = uname(&buf);
1217
1218         if (!ret) {
1219                 if (buf.release[0] == '3') {
1220                         char *pch;
1221                         char str[3];
1222                         int sub_version;
1223                         pch = strstr(buf.release, ".");
1224                         strncpy(str, pch+1, 2);
1225                         sub_version = atoi(str);
1226
1227                         if (sub_version >= 10)
1228                                 ignore_smaps_field = 8; /* Referenced, Anonymous, AnonHugePages,
1229                                                    Swap, KernelPageSize, MMUPageSize,
1230                                                    Locked, VmFlags */
1231
1232                         else
1233                                 ignore_smaps_field = 7; /* Referenced, Anonymous, AnonHugePages,
1234                                                    Swap, KernelPageSize, MMUPageSize,
1235                                                    Locked */
1236                 } else {
1237                         ignore_smaps_field = 4; /* Referenced, Swap, KernelPageSize,
1238                                                    MMUPageSize */
1239                 }
1240         }
1241 }
1242
1243 int main(int argc, char *argv[])
1244 {
1245         int usage = 1;
1246         sum = 0;
1247
1248         if (argc > 1) {
1249                 check_kernel_version();
1250
1251                 if (!strcmp(argv[1], "-r")) {
1252                         if (argc >= 3)
1253                                 show_rss(OUTPUT_FILE, argv[2]);
1254                         else
1255                                 show_rss(OUTPUT_UART, NULL);
1256                         usage = 0;
1257                 } else if (!strcmp(argv[1], "-s")) {
1258                         sum = 1;
1259                         if (argc == 3 && atoi(argv[2]) > 0) {
1260                                 show_map_new(atoi(argv[2]));
1261                                 usage = 0;
1262                         }
1263                 } else if (!strcmp(argv[1], "-a")) {
1264                         verbos = 0;
1265                         show_map_all_new(OUTPUT_UART, NULL);
1266                         usage = 0;
1267                 } else if (!strcmp(argv[1], "-v")) {
1268                         verbos = 1;
1269                         show_map_all_new(OUTPUT_UART, NULL);
1270                         usage = 0;
1271                 } else if (!strcmp(argv[1], "-f")) {
1272                         if (argc >= 3) {
1273                                 verbos = 1;
1274                                 show_map_all_new(OUTPUT_FILE, argv[2]);
1275                                 usage = 0;
1276                         }
1277                 } else if (argc == 2 && atoi(argv[1]) > 0) {
1278                         show_map_new(atoi(argv[1]));
1279                         usage = 0;
1280                 }
1281         }
1282         if (usage) {
1283                 fprintf(stderr,
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");
1289         }
1290
1291         return 0;
1292 }