fix build warning
[platform/core/system/memps.git] / memps.c
1 /* Copyright 2014 Samsung Electronics Co., Ltd All Rights Reserved
2  *
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
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdbool.h>
19 #include <math.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <sys/vfs.h>
27 #include <linux/limits.h>
28
29 #include <ctype.h>
30 #include <stddef.h>
31
32 #include <dirent.h>
33 #include <sys/utsname.h>
34
35 #define STR_SGX_PATH    "/dev/pvrsrvkm"
36 #define STR_3D_PATH1    "/dev/mali"
37 #define STR_3D_PATH2    "/dev/kgsl-3d0"
38 #define STR_DRM_PATH1   "/drm mm object (deleted)"
39 #define STR_DRM_PATH2   "/dev/dri/card0"
40 #define MEMCG_PATH      "/sys/fs/cgroup/memory"
41 #define ZRAM_USED_PATH  "/sys/block/zram0/mem_used_total"
42 #define OOM_SCORE_ADJ_STR       "oom_score_adj"
43 #define OOM_SCORE_STR   "oom_score"
44
45 #define BUF_MAX         (BUFSIZ)            /* most optimal for libc::stdio */
46 #define BUF_INC_SIZE    (512 * 1024)        /* maximal SMAPS I saw 2 MB     */
47 #define KB(bytes)       ((bytes)/1024)
48
49 #define BYTE_TO_KBYTE(b) ((b) >> 10)
50 #define BYTE_TO_MBYTE(b) ((b) >> 20)
51 #define BYTE_TO_GBYTE(b) ((b) >> 30)
52
53 #define KBYTE_TO_BYTE(k) ((k) << 10)
54 #define KBYTE_TO_MBYTE(k) ((k) >> 10)
55 #define KBYTE_TO_GBYTE(k) ((k) >> 20)
56
57 #define GBYTE_TO_BYTE(g) ((g) << 30)
58 #define GBYTE_TO_KBYTE(g) ((g) << 20)
59 #define GBYTE_TO_MBYTE(g) ((g) << 10)
60
61 typedef struct geminfo geminfo;
62 typedef struct mapinfo mapinfo;
63 typedef struct trib_mapinfo trib_mapinfo;
64
65 enum {
66         OUTPUT_UART,
67         OUTPUT_FILE,
68         NUM_OUTPUT_TYPE
69 };
70
71 struct mapinfo {
72         mapinfo *next;
73         unsigned long start;
74         unsigned long end;
75         unsigned size;
76         unsigned swap;
77         unsigned rss;
78         unsigned pss;
79         unsigned shared_clean;
80         unsigned shared_dirty;
81         unsigned private_clean;
82         unsigned private_dirty;
83         char *perm;
84         char *name;
85 };
86
87 /* classify normal, graphic and other devices memory */
88 struct trib_mapinfo {
89         unsigned shared_clean;
90         unsigned shared_dirty;
91         unsigned private_clean;
92         unsigned private_dirty;
93         unsigned shared_clean_pss;
94         unsigned shared_dirty_pss;
95         unsigned swap;
96         unsigned rss;
97         unsigned pss;
98         unsigned size;
99         unsigned graphic_3d;
100         unsigned gem_rss;
101         unsigned gem_pss;
102         unsigned peak_rss;
103         unsigned other_devices;
104         unsigned gem_mmap;
105 };
106
107 struct geminfo {
108         geminfo *next;
109         unsigned int tgid;
110         unsigned rss_size;
111         unsigned pss_size;
112         unsigned hcount;
113 };
114
115 static int sum;
116 static int verbos;
117
118 /* reads file contents into memory */
119 static char* cread(const char* path)
120 {
121         /* once allocated area for reads */
122         static char*    text = NULL;
123         static size_t   size = 0;
124
125         ssize_t ret;
126         char*   ptr = text;
127         size_t  cap = size;
128         int     fd  = open(path, O_RDONLY);
129
130         if (fd < 0)
131                 return NULL;
132
133         do {
134                 /* ensure we have enough space */
135                 if (cap == 0) {
136                         ptr = (char*)realloc(text, size + BUF_INC_SIZE);
137                         if (ptr == NULL) {
138                                 ret = -1;
139                                 break;
140                         }
141
142                         text  = ptr;
143                         ptr   = text + size;
144                         cap   = BUF_INC_SIZE;
145                         size += BUF_INC_SIZE;
146                 }
147                 ret = read(fd, ptr, cap);
148                 if (ret == 0) {
149                         *ptr = 0;
150                 } else if (ret > 0) {
151                         cap -= ret;
152                         ptr += ret;
153                 }
154         } while (ret > 0);
155         close(fd);
156
157         return (ret < 0 ? NULL : text);
158 } /* cread */
159
160 /* like fgets/gets but adjusting contents pointer */
161 static inline char* cgets(char** contents)
162 {
163         if (contents && *contents && **contents) {
164                 char* bos = *contents;          /* begin of string */
165                 char* eos = strchr(bos, '\n');  /* end of string   */
166
167                 if (eos) {
168                         *contents = eos + 1;
169                         *eos      = 0;
170                 } else {
171                         *contents = NULL;
172                 }
173                 return bos;
174         }
175
176         return NULL;
177 } /* cgets */
178
179
180 static unsigned get_peak_rss(unsigned int pid)
181 {
182         static const char field[] = "VmHWM:";
183         char tmp[128];
184         char* line;
185         char* value;
186
187         snprintf(tmp, sizeof(tmp), "/proc/%d/status", pid);
188         line = cread(tmp);
189         if (line == NULL) {
190                 fprintf(stderr, "cannot open %s\n", tmp);
191                 return 0;
192         }
193
194         value = strstr(line, field);
195         if (value) {
196                 value += sizeof(field);
197                 return strtoul(value, NULL, 10);
198         }
199
200         return 0;
201 }
202 #define NUM_GEM_FIELD 6
203
204 static geminfo *read_geminfo(FILE *fp)
205 {
206         geminfo *tgeminfo;
207         char line[BUF_MAX];
208         unsigned int pid, tgid, handle, refcount, hcount;
209         unsigned gem_size;
210
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)
215                         return NULL;
216
217                 if (hcount == 0)
218                         return NULL;
219                 tgeminfo = malloc(sizeof(geminfo));
220                 if (tgeminfo == NULL)
221                         return 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);
226         } else
227                 return NULL;
228
229         return tgeminfo;
230 }
231
232
233 static geminfo *find_geminfo(unsigned int tgid, geminfo *gilist)
234 {
235         geminfo *gi;
236         for (gi = gilist; gi; ) {
237                 if (gi->tgid == tgid)
238                         return gi;
239
240                 gi = gi->next;
241         }
242         return NULL;
243 }
244
245 static geminfo *load_geminfo(void)
246 {
247         geminfo *ginfo;
248         geminfo *gilist = NULL;
249         geminfo *exist_ginfo = NULL;
250
251         FILE *drm_fp;
252         char line[BUF_MAX];
253
254         drm_fp = fopen("/sys/kernel/debug/dri/0/gem_info", "r");
255
256         if (drm_fp == NULL) {
257                 fprintf(stderr,
258                 "cannot open /sys/kernel/debug/dri/0/gem_info\n");
259                 return NULL;
260         }
261
262         if (fgets(line, BUF_MAX, drm_fp) == NULL) {
263                 fclose(drm_fp);
264                 return NULL;
265         } else {
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];
270
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++)) {
275                                 if (in_field) {
276                                         /* end of field */
277                                         in_field = 0;
278                                 }
279                         } else {
280                                 if (!in_field) {
281                                         /* next field started */
282                                         in_field = 1;
283                                         size++;
284                                 }
285                         }
286                 } /* while */
287
288                 if (size != NUM_GEM_FIELD) {
289                         fclose(drm_fp);
290                         return NULL;
291                 }
292         }
293
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;
298                         free(ginfo);
299                         continue;
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;
303                         free(ginfo);
304                         continue;
305                 }
306                 ginfo->next = gilist;
307                 gilist = ginfo;
308         }
309
310         fclose(drm_fp);
311
312         return gilist;
313 }
314
315
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
320  * 0         1         2         3         4         5         6         7
321  */
322 mapinfo *read_mapinfo(char** smaps)
323 {
324         char* line;
325         mapinfo *mi;
326         int len;
327         int n;
328
329         if ((line = cgets(smaps)) == 0)
330                 return 0;
331
332         len = strlen(line);
333         if (len < 1)
334                 return 0;
335
336         mi = malloc(sizeof(mapinfo));
337         if (mi == 0)
338                 return 0;
339
340         n = sscanf(line, "%lx-%lx %ms %*s %*s %*s %m[^\n]",
341                            &mi->start, &mi->end, &mi->perm, &mi->name);
342
343         if (n == 3 && !mi->name)
344                 mi->name = strndup("[anon]", strlen("[anon]"));
345         else if (n <= 3) {
346                 fprintf(stderr, "Fail to parse smaps\n");
347                 free(mi);
348                 return 0;
349         }
350
351         while ((line = cgets(smaps))) {
352                 if (sscanf(line, "Size: %d kB", &mi->size) == 1)
353                         ;
354                 else if (sscanf(line, "Rss: %d kB", &mi->rss) == 1)
355                         ;
356                 else if (sscanf(line, "Pss: %d kB", &mi->pss) == 1)
357                         ;
358                 else if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) == 1)
359                         ;
360                 else if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) == 1)
361                         ;
362                 else if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) == 1)
363                         ;
364                 else if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) == 1)
365                         ;
366                 else if (sscanf(line, "Swap: %d kB", &mi->swap) == 1)
367                         ;
368                 if (*smaps) {
369                         /* Drain lines until it meets next VMA address */
370                         char next = **smaps;
371                         if  ((next >= '0' && next <= '9') || (next >= 'a' && next <= 'f'))
372                                 break;
373                 }
374         }
375
376         return mi;
377 }
378
379 static unsigned total_gem_memory(void)
380 {
381         FILE *gem_fp;
382         unsigned total_gem_mem = 0;
383         unsigned name, size, handles, refcount;
384         char line[BUF_MAX];
385
386         gem_fp = fopen("/sys/kernel/debug/dri/0/gem_names", "r");
387         if (gem_fp == NULL) {
388                 fprintf(stderr,
389                 "cannot open /sys/kernel/debug/dri/0/gem_names\n");
390                 return 0;
391         }
392
393         if (fgets(line, BUF_MAX, gem_fp) == NULL) {
394                 fclose(gem_fp);
395                 return 0;
396         }
397
398         while (fgets(line, BUF_MAX, gem_fp) != NULL)
399                 if (sscanf(line, "%d %d %d %d\n",
400                     &name, &size, &handles, &refcount) == 4)
401                         total_gem_mem += size;
402         fclose(gem_fp);
403
404         return total_gem_mem;
405 }
406
407
408 int fread_uint(const char *path, u_int32_t *number)
409 {
410         FILE *f = NULL;
411         int ret;
412
413         f = fopen(path, "r");
414
415         if (!f) {
416                 fprintf(stderr, "Fail to open %s file.\n", path);
417                 return -1;
418         }
419
420         ret = fscanf(f, "%u", number);
421         if (ret == EOF) {
422                 fprintf(stderr, "Fail to read file\n");
423                 fclose(f);
424                 return -1;
425         }
426
427         fclose(f);
428         return 0;
429 }
430
431 #define MAX_PATH_LENGTH 512
432 static int cgroup_read_node(const char *cgroup_name,
433                 const char *file_name, unsigned int *value)
434 {
435         char buf[MAX_PATH_LENGTH];
436         int ret;
437         snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name);
438         ret = fread_uint(buf, value);
439         return ret;
440 }
441
442 /**
443  * @desc Provides usage in bytes for provided memory cgroup. Works
444  * with/without swap accounting.
445  *
446  * @param memcg_path[in] Full path to memory cgroup
447  * @param swap[in] Boolean value for deciding if account usage with swap
448  * @return current cgroup usage in bytes or 0 on error
449  */
450 static unsigned int get_memcg_usage(const char *memcg_path, bool swap)
451 {
452         int ret;
453         unsigned int usage;
454
455         if (swap) {
456                 ret = cgroup_read_node(memcg_path,
457                                 "/memory.memsw.usage_in_bytes", &usage);
458         } else {
459                 ret = cgroup_read_node(memcg_path, "/memory.usage_in_bytes",
460                                 &usage);
461         }
462
463         if (ret != 0)
464                 usage = 0;
465
466         return usage;
467 }
468
469 static void get_memcg_info(FILE *output_fp)
470 {
471         char buf[PATH_MAX];
472         DIR *pdir = NULL;
473         struct dirent *entry;
474         struct stat path_stat;
475         long usage_swap;
476         unsigned long usage, usage_with_swap;
477
478         fprintf(output_fp, "====================================================================\n");
479         fprintf(output_fp, "MEMORY CGROUPS USAGE INFO\n");
480
481         pdir = opendir(MEMCG_PATH);
482         if (pdir == NULL) {
483                 fprintf(stderr, "cannot read directory %s", MEMCG_PATH);
484                 return;
485         }
486
487         errno = 0;
488         while ((entry = readdir(pdir)) != NULL && !errno) {
489                 snprintf(buf, sizeof(buf), "%s/%s", MEMCG_PATH, entry->d_name);
490                 /* If can't stat then ignore */
491                 if (stat(buf, &path_stat) != 0)
492                         continue;
493
494                 /* If it's not directory or it's parent path then ignore */
495                 if (!(S_ISDIR(path_stat.st_mode) &&
496                         strncmp(entry->d_name, "..", 3)))
497                         continue;
498
499                 usage = get_memcg_usage(buf, false);
500                 usage_with_swap = get_memcg_usage(buf, true);
501                 /* It is posible by rounding errors to get negative value */
502                 usage_swap = usage_with_swap - usage;
503                 if (usage_swap < 0)
504                         usage_swap = 0;
505
506                 /* Case of root cgroup in hierarchy */
507                 if (!strncmp(entry->d_name, ".", 2))
508                         fprintf(output_fp, "%13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB) \n\n",
509                                 MEMCG_PATH, BYTE_TO_MBYTE(usage),
510                                 BYTE_TO_KBYTE(usage),
511                                 BYTE_TO_MBYTE(usage_with_swap),
512                                 BYTE_TO_KBYTE(usage_with_swap),
513                                 BYTE_TO_MBYTE(usage_swap),
514                                 BYTE_TO_KBYTE(usage_swap));
515                 else
516                         fprintf(output_fp, "memcg: %13s  Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB)\n",
517                                 entry->d_name, BYTE_TO_MBYTE(usage),
518                                 BYTE_TO_KBYTE(usage),
519                                 BYTE_TO_MBYTE(usage_with_swap),
520                                 BYTE_TO_KBYTE(usage_with_swap),
521                                 BYTE_TO_MBYTE(usage_swap),
522                                 BYTE_TO_KBYTE(usage_swap));
523
524         }
525
526         closedir(pdir);
527 }
528
529 static void get_mem_info(FILE *output_fp)
530 {
531         char buf[PATH_MAX];
532         FILE *fp;
533         char *idx;
534         unsigned int free = 0, cached = 0;
535         unsigned int total_mem = 0, available = 0, used;
536         unsigned int swap_total = 0, swap_free = 0, zram_used, swap_used;
537         unsigned int used_ratio;
538
539         if (output_fp == NULL)
540                 return;
541
542         fp = fopen("/proc/meminfo", "r");
543
544         if (!fp) {
545                 fprintf(stderr, "%s open failed, %p", buf, fp);
546                 return;
547         }
548
549         while (fgets(buf, PATH_MAX, fp) != NULL) {
550                 if ((idx = strstr(buf, "MemTotal:"))) {
551                         idx += strlen("Memtotal:");
552                         while (*idx < '0' || *idx > '9')
553                                 idx++;
554                         total_mem = atoi(idx);
555                 } else if ((idx = strstr(buf, "MemFree:"))) {
556                         idx += strlen("MemFree:");
557                         while (*idx < '0' || *idx > '9')
558                                 idx++;
559                         free = atoi(idx);
560                 } else if ((idx = strstr(buf, "MemAvailable:"))) {
561                         idx += strlen("MemAvailable:");
562                         while (*idx < '0' || *idx > '9')
563                                 idx++;
564                         available = atoi(idx);
565                 } else if ((idx = strstr(buf, "Cached:")) && !strstr(buf, "Swap")) {
566                         idx += strlen("Cached:");
567                         while (*idx < '0' || *idx > '9')
568                                 idx++;
569                         cached = atoi(idx);
570                 } else if ((idx = strstr(buf, "SwapTotal:"))) {
571                         idx += strlen("SwapTotal:");
572                         while (*idx < '0' || *idx > '9')
573                                 idx++;
574                         swap_total = atoi(idx);
575                 } else if ((idx = strstr(buf, "SwapFree:"))) {
576                         idx += strlen("SwapFree");
577                         while (*idx < '0' || *idx > '9')
578                                 idx++;
579                         swap_free = atoi(idx);
580                         break;
581                 }
582         }
583
584         if (total_mem == 0) {
585                 fprintf(stderr, "cannot get total memory size\n");
586                 fclose(fp);
587                 return;
588         }
589
590         if (available == 0)
591                 available = free + cached;
592         used = total_mem - available;
593         used_ratio = used * 100 / total_mem;
594         swap_used = swap_total - swap_free;
595
596         if (fread_uint(ZRAM_USED_PATH, &zram_used) != 0)
597                 zram_used = 0;
598
599         fprintf(output_fp,
600                 "====================================================================\n");
601
602
603         fprintf(output_fp, "Total RAM size: \t%15d MB( %6d kB)\n",
604                         total_mem >> 10, total_mem);
605
606         fprintf(output_fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n",
607                         (total_mem - free) >> 10, total_mem - free);
608
609         fprintf(output_fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n",
610                         used >> 10, used);
611
612         fprintf(output_fp, "Used (Mem):  \t\t%15d MB( %6d kB)\n",
613                         used >> 10, used);
614
615         fprintf(output_fp, "Used (Swap): \t\t%15d MB( %6d kB)\n",
616                         swap_used >> 10, swap_used);
617
618         fprintf(output_fp, "Used (Zram block device): %13d MB( %6d kB)\n",
619             BYTE_TO_MBYTE(zram_used), BYTE_TO_KBYTE(zram_used));
620
621         fprintf(output_fp, "Used Ratio: \t\t%15d  %%\n", used_ratio);
622
623         fprintf(output_fp, "Mem Free:\t\t%15d MB( %6d kB)\n",
624                         free >> 10, free);
625
626         fprintf(output_fp, "Available (Free+Reclaimable):%10d MB( %6d kB)\n",
627                         available >> 10,
628                         available);
629         fclose(fp);
630 }
631
632 static int get_tmpfs_info(FILE *output_fp)
633 {
634         FILE *fp;
635         char line[BUF_MAX];
636         char *tmpfs_mp; /* tmpfs mount point */
637         struct statfs tmpfs_info;
638
639         if (output_fp == NULL)
640                 return -1;
641
642         fp = fopen("/etc/mtab", "r");
643         if (fp == NULL)
644                 return -1;
645
646         fprintf(output_fp,
647                 "====================================================================\n");
648         fprintf(output_fp, "TMPFS INFO\n");
649
650         while (fgets(line, BUF_MAX, fp) != NULL) {
651                 if (sscanf(line, "tmpfs %ms tmpfs", &tmpfs_mp) == 1) {
652                         statfs(tmpfs_mp, &tmpfs_info);
653                         fprintf(output_fp,
654                                 "tmpfs %16s  Total %8ld KB, Used %8ld, Avail %8ld\n",
655                                 tmpfs_mp,
656                                 /* 1 block is 4 KB */
657                                 tmpfs_info.f_blocks * 4,
658                                 (tmpfs_info.f_blocks - tmpfs_info.f_bfree) * 4,
659                                 tmpfs_info.f_bfree * 4);
660                         free(tmpfs_mp);
661                 }
662         }
663         fclose(fp);
664         return 0;
665 }
666
667 mapinfo *load_maps(int pid)
668 {
669         char* smaps;
670         char tmp[128];
671         mapinfo *milist = 0;
672         mapinfo *mi;
673
674         snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
675         smaps = cread(tmp);
676         if (smaps == NULL)
677                 return 0;
678
679         while ((mi = read_mapinfo(&smaps)) != 0) {
680                 if (milist) {
681                         if ((!strcmp(mi->name, milist->name)
682                              && (mi->name[0] != '['))) {
683                                 milist->size += mi->size;
684                                 milist->swap += mi->swap;
685                                 milist->rss += mi->rss;
686                                 milist->pss += mi->pss;
687                                 milist->shared_clean += mi->shared_clean;
688                                 milist->shared_dirty += mi->shared_dirty;
689                                 milist->private_clean += mi->private_clean;
690                                 milist->private_dirty += mi->private_dirty;
691
692                                 milist->end = mi->end;
693                                 strncpy(milist->perm, mi->perm, 4);
694                                 free(mi->perm);
695                                 free(mi->name);
696                                 free(mi);
697                                 continue;
698                         }
699                 }
700                 mi->next = milist;
701                 milist = mi;
702         }
703
704         return milist;
705 }
706
707 static void init_trib_mapinfo(trib_mapinfo *tmi)
708 {
709         if (!tmi)
710                 return;
711         tmi->shared_clean = 0;
712         tmi->shared_dirty = 0;
713         tmi->private_clean = 0;
714         tmi->private_dirty = 0;
715         tmi->swap = 0;
716         tmi->shared_clean_pss = 0;
717         tmi->shared_dirty_pss = 0;
718         tmi->rss = 0;
719         tmi->pss = 0;
720         tmi->size = 0;
721         tmi->graphic_3d = 0;
722         tmi->gem_rss = 0;
723         tmi->gem_pss = 0;
724         tmi->peak_rss = 0;
725         tmi->other_devices = 0;
726         tmi->gem_mmap = 0;
727 }
728
729 static int
730 get_trib_mapinfo(unsigned int tgid, mapinfo *milist,
731                  geminfo *gilist, trib_mapinfo *result)
732
733 {
734         mapinfo *mi;
735         mapinfo *temp = NULL;
736         geminfo *gi;
737
738         if (!result)
739                 return -EINVAL;
740
741         init_trib_mapinfo(result);
742         for (mi = milist; mi;) {
743                 if (strstr(mi->name, STR_SGX_PATH)) {
744                         result->graphic_3d += mi->pss;
745                 } else if (strstr(mi->name, STR_3D_PATH1) ||
746                         strstr(mi->name, STR_3D_PATH2)) {
747                         result->graphic_3d += mi->size;
748                 } else if (mi->rss != 0 && mi->pss == 0
749                            && mi->shared_clean == 0
750                            && mi->shared_dirty == 0
751                            && mi->private_clean == 0
752                            && mi->private_dirty == 0
753                            && mi->swap == 0) {
754                         result->other_devices += mi->size;
755                 } else if (!strncmp(mi->name, STR_DRM_PATH1,
756                                 sizeof(STR_DRM_PATH1)) ||
757                                 !strncmp(mi->name, STR_DRM_PATH2,
758                                 sizeof(STR_DRM_PATH2))) {
759                         result->gem_mmap += mi->rss;
760                 } else {
761                         result->shared_clean += mi->shared_clean;
762                         result->shared_dirty += mi->shared_dirty;
763                         result->private_clean += mi->private_clean;
764                         result->private_dirty += mi->private_dirty;
765                         result->swap += mi->swap;
766                         result->rss += mi->rss;
767                         result->pss += mi->pss;
768                         result->size += mi->size;
769
770                         if (mi->shared_clean != 0)
771                                 result->shared_clean_pss += mi->pss;
772                         else if (mi->shared_dirty != 0)
773                                 result->shared_dirty_pss += mi->pss;
774                 }
775
776                 temp = mi;
777                 mi = mi->next;
778                 free(temp->perm);
779                 free(temp->name);
780                 free(temp);
781                 temp = NULL;
782         }
783
784         result->peak_rss = get_peak_rss(tgid);
785         if (result->peak_rss < result->rss)
786                 result->peak_rss = result->rss;
787         if (result->gem_mmap > 0)
788                 result->peak_rss -= result->gem_mmap;
789
790         gi = find_geminfo(tgid, gilist);
791         if (gi != NULL) {
792                 result->gem_rss = gi->rss_size;
793                 result->gem_pss = gi->pss_size;
794         }
795
796         return 0;
797 }
798
799 static int get_cmdline(unsigned int pid, char *cmdline)
800 {
801         FILE *fp;
802         char buf[NAME_MAX] = {0, };
803         int ret = -1;
804
805         snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
806         fp = fopen(buf, "r");
807         if (fp == 0) {
808                 fprintf(stderr, "cannot file open %s\n", buf);
809                 return ret;
810         }
811         if ((ret = fscanf(fp, "%4095s", cmdline)) < 1) {
812                 fclose(fp);
813                 return ret;
814         }
815         fclose(fp);
816
817         return ret;
818 }
819
820 static int get_oomscoreadj(unsigned int pid, const char *oom_string)
821 {
822         FILE *fp;
823         char tmp[256];
824         int oomadj_val;
825
826         snprintf(tmp, sizeof(tmp), "/proc/%d/%s", pid, oom_string);
827         fp = fopen(tmp, "r");
828
829         if (fp == NULL) {
830                 oomadj_val = -50;
831                 return oomadj_val;
832         }
833         if (fgets(tmp, sizeof(tmp), fp) == NULL) {
834                 oomadj_val = -100;
835                 fclose(fp);
836                 return oomadj_val;
837         }
838
839         oomadj_val = atoi(tmp);
840
841         fclose(fp);
842         return oomadj_val;
843 }
844
845 static void get_rss(pid_t pid, unsigned long *result)
846 {
847         FILE *fp;
848         char proc_path[PATH_MAX];
849         unsigned long rss = 0;
850
851         *result = 0;
852
853         snprintf(proc_path, sizeof(proc_path), "/proc/%d/statm", pid);
854         fp = fopen(proc_path, "r");
855         if (fp == NULL)
856                 return;
857
858         if (fscanf(fp, "%*s %ld", &rss) < 1) {
859                 fclose(fp);
860                 return;
861         }
862
863         fclose(fp);
864
865         /* convert page to Kb */
866         *result = rss << 2;
867         return;
868 }
869
870 static const char* get_readable_oom_path(void)
871 {
872         FILE *fp = NULL;
873         char tmp[256];
874
875         snprintf(tmp, sizeof(tmp), "/proc/1/%s", OOM_SCORE_ADJ_STR);
876         fp = fopen(tmp, "r");
877         if (fp) {
878                 fclose(fp);
879                 return OOM_SCORE_ADJ_STR;
880         }
881         snprintf(tmp, sizeof(tmp), "/proc/1/%s", OOM_SCORE_STR);
882         fp = fopen(tmp, "r");
883         if (fp) {
884                 fclose(fp);
885                 return OOM_SCORE_STR;
886         }
887         return NULL;
888 }
889
890 static void show_rss(int output_type, char *output_path)
891 {
892         DIR *pDir = NULL;
893         struct dirent *curdir;
894         pid_t pid;
895         char cmdline[PATH_MAX];
896         FILE *output_file = NULL;
897         int oom_score_adj;
898         unsigned long rss;
899         const char *oom_path = NULL;
900
901         oom_path = get_readable_oom_path();
902         if (!oom_path) {
903                 fprintf(stderr, "there is no readable oom path\n");
904                 return;
905         }
906
907         pDir = opendir("/proc");
908         if (pDir == NULL) {
909                 fprintf(stderr, "cannot read directory /proc.\n");
910                 return;
911         }
912
913         if (output_type == OUTPUT_FILE && output_path) {
914                 output_file = fopen(output_path, "w+");
915                 if (!output_file) {
916                         fprintf(stderr, "cannot open output file(%s)\n",
917                                 output_path);
918                         closedir(pDir);
919                         exit(1);
920                 }
921         } else
922                 output_file = stdout;
923
924
925         fprintf(output_file,
926                         "     PID    RSS    %s    COMMAND\n", oom_path);
927
928         errno = 0;
929         while ((curdir = readdir(pDir)) != NULL && !errno) {
930                 pid = atoi(curdir->d_name);
931                 if (pid < 1 || pid > 32768 || pid == getpid())
932                         continue;
933
934                 if (get_cmdline(pid, cmdline) < 0)
935                         continue;
936                 get_rss(pid, &rss);
937                 oom_score_adj = get_oomscoreadj(pid, oom_path);
938
939                 fprintf(output_file,
940                                 "%8d %8lu %8d          %s\n",
941                                 pid,
942                                 rss,
943                                 oom_score_adj,
944                                 cmdline);
945
946
947         } /* end of while */
948
949         get_tmpfs_info(output_file);
950         get_mem_info(output_file);
951
952         fclose(output_file);
953         closedir(pDir);
954
955         return;
956 }
957
958 static int show_map_all_new(int output_type, char *output_path)
959 {
960         DIR *pDir = NULL;
961         struct dirent *curdir;
962         unsigned int pid;
963         mapinfo *milist;
964         geminfo *glist;
965         unsigned total_pss = 0;
966         unsigned total_private = 0;
967         unsigned total_private_code = 0;
968         unsigned total_private_data = 0;
969         unsigned total_shared_code = 0;
970         unsigned total_shared_data = 0;
971         unsigned total_shared_code_pss = 0;
972         unsigned total_shared_data_pss = 0;
973         unsigned total_swap = 0;
974         unsigned total_rss = 0;
975         unsigned total_graphic_3d = 0;
976         unsigned total_gem_rss = 0;
977         unsigned total_gem_pss = 0;
978         unsigned total_peak_rss = 0;
979         unsigned total_allocated_gem = 0;
980         trib_mapinfo tmi;
981         char cmdline[PATH_MAX];
982         FILE *output_file = NULL;
983         int oom_score_adj;
984         const char *oom_path = NULL;
985
986         oom_path = get_readable_oom_path();
987         if (!oom_path) {
988                 fprintf(stderr, "there is no readable oom path\n");
989                 return 0;
990         }
991
992         pDir = opendir("/proc");
993         if (pDir == NULL) {
994                 fprintf(stderr, "cannot read directory /proc.\n");
995                 return 0;
996         }
997
998         if (output_type == OUTPUT_FILE && output_path) {
999                 output_file = fopen(output_path, "w+");
1000                 if (!output_file) {
1001                         fprintf(stderr, "cannot open output file(%s)\n",
1002                                 output_path);
1003                         closedir(pDir);
1004                         exit(1);
1005                 }
1006         } else
1007                 output_file = stdout;
1008
1009         glist = load_geminfo();
1010
1011         if (!sum) {
1012                 if (verbos)
1013                         fprintf(output_file,
1014                                         "     PID  S(CODE)  S(DATA)  P(CODE)  P(DATA)"
1015                                         "     PEAK      PSS       3D"
1016                                         "     GEM(PSS)  GEM(RSS)    SWAP"
1017                                         "     %s    COMMAND\n", oom_path);
1018                 else
1019                         fprintf(output_file,
1020                                         "     PID     CODE     DATA     PEAK     PSS"
1021                                         "     3D      GEM(PSS)      SWAP      COMMAND\n");
1022         }
1023
1024         errno = 0;
1025         while ((curdir = readdir(pDir)) != NULL && !errno) {
1026                 pid = atoi(curdir->d_name);
1027                 if (pid < 1 || pid > 32768 || pid == getpid())
1028                         continue;
1029
1030                 if (get_cmdline(pid, cmdline) < 0)
1031                         continue;
1032
1033                 milist = load_maps(pid);
1034                 if (milist == 0)
1035                         continue;
1036
1037                 /* get classified map info */
1038                 get_trib_mapinfo(pid, milist, glist, &tmi);
1039                 oom_score_adj = get_oomscoreadj(pid, oom_path);
1040
1041                 if (!sum) {
1042                         if (verbos)
1043                                 fprintf(output_file,
1044                                         "%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d %8d"
1045                                         " %8d \t\t%s\n",
1046                                         pid,
1047                                         tmi.shared_clean, tmi.shared_dirty,
1048                                         tmi.private_clean, tmi.private_dirty,
1049                                         tmi.peak_rss, tmi.pss, tmi.graphic_3d,
1050                                         tmi.gem_pss, tmi.gem_rss, tmi.swap,
1051                                         oom_score_adj, cmdline);
1052                         else
1053                                 fprintf(output_file,
1054                                         "%8d %8d %8d %8d %8d %8d %8d %8d      %s\n",
1055                                         pid,
1056                                         tmi.shared_clean +
1057                                         tmi.private_clean,
1058                                         tmi.shared_dirty + tmi.private_dirty,
1059                                         tmi.peak_rss,
1060                                         tmi.pss,
1061                                         tmi.graphic_3d,
1062                                         tmi.gem_pss,
1063                                         tmi.swap, cmdline);
1064
1065                         if (tmi.other_devices != 0)
1066                                 fprintf(output_file,
1067                                         "%s(%d) %d KB may mapped by device(s).\n",
1068                                         cmdline, pid, tmi.other_devices);
1069                 }
1070
1071                 total_private += (tmi.private_clean + tmi.private_dirty);
1072                 total_pss += tmi.pss;
1073                 total_rss += tmi.rss;
1074                 total_graphic_3d += tmi.graphic_3d;
1075                 total_gem_rss += tmi.gem_rss;
1076                 total_gem_pss += tmi.gem_pss;
1077                 total_private_code += tmi.private_clean;
1078                 total_private_data += tmi.private_dirty;
1079                 total_swap += tmi.swap;
1080                 total_shared_code += tmi.shared_clean;
1081                 total_shared_data += tmi.shared_dirty;
1082                 total_peak_rss += tmi.peak_rss;
1083
1084                 total_shared_code_pss += tmi.shared_clean_pss;
1085                 total_shared_data_pss += tmi.shared_dirty_pss;
1086
1087         } /* end of while */
1088
1089         total_allocated_gem = KB(total_gem_memory());
1090         fprintf(output_file,
1091                         "==============================================="
1092                         "===============================================\n");
1093         if (verbos) {
1094                 fprintf(output_file,
1095                                 "TOTAL:      S(CODE) S(DATA) P(CODE)  P(DATA)"
1096                                 "    PEAK     PSS       3D    "
1097                                 "GEM(PSS) GEM(RSS) GEM(ALLOC) SWAP TOTAL(KB)\n");
1098                 fprintf(output_file,
1099                         "         %8d %8d %8d %8d %8d %8d %8d"
1100                         " %8d %8d %8d %8d %8d\n",
1101                         total_shared_code, total_shared_data,
1102                         total_private_code, total_private_data,
1103                         total_peak_rss, total_pss, total_graphic_3d,
1104                         total_gem_pss, total_gem_rss,
1105                         total_allocated_gem, total_swap,
1106                         total_pss + total_graphic_3d +
1107                         total_allocated_gem);
1108         } else {
1109                 fprintf(output_file,
1110                         "TOTAL:        CODE     DATA    PEAK     PSS     "
1111                         "3D    GEM(PSS) GEM(ALLOC)     TOTAL(KB)\n");
1112                 fprintf(output_file, "         %8d %8d %8d %8d %8d %8d %7d %8d %8d\n",
1113                         total_shared_code + total_private_code,
1114                         total_shared_data + total_private_data,
1115                         total_peak_rss, total_pss,
1116                         total_graphic_3d, total_gem_pss,
1117                         total_allocated_gem, total_swap,
1118                         total_pss + total_graphic_3d +
1119                         total_allocated_gem);
1120
1121         }
1122
1123         if (verbos)
1124                 fprintf(output_file,
1125                         "* S(CODE): shared clean memory, it includes"
1126                         " duplicated memory\n"
1127                         "* S(DATA): shared dirty memory, it includes"
1128                         " duplicated memory\n"
1129                         "* P(CODE): private clean memory\n"
1130                         "* P(DATA): private dirty memory\n"
1131                         "* PEAK: peak memory usage of S(CODE) + S(DATA)"
1132                         " + P(CODE) + P(DATA)\n"
1133                         "* PSS: Proportional Set Size\n"
1134                         "* 3D: memory allocated by GPU driver\n"
1135                         "* GEM(PSS): GEM memory divided by # of sharers\n"
1136                         "* GEM(RSS): GEM memory including duplicated memory\n"
1137                         "* GEM(ALLOC): sum of unique gem memory in the system\n"
1138                         "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1139         else
1140                 fprintf(output_file,
1141                         "* CODE: shared and private clean memory\n"
1142                         "* DATA: shared and private dirty memory\n"
1143                         "* PEAK: peak memory usage of CODE + DATA\n"
1144                         "* PSS: Proportional Set Size\n"
1145                         "* 3D: memory allocated by GPU driver\n"
1146                         "* GEM(PSS): GEM memory divided by # of sharers\n"
1147                         "* GEM(ALLOC): sum of unique GEM memory in the system\n"
1148                         "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1149
1150         get_tmpfs_info(output_file);
1151         get_memcg_info(output_file);
1152         get_mem_info(output_file);
1153
1154         fclose(output_file);
1155         if (glist)
1156                 free(glist);
1157         closedir(pDir);
1158         return 1;
1159 }
1160
1161 static int show_map_new(int pid)
1162 {
1163         mapinfo *milist;
1164         mapinfo *mi;
1165         unsigned shared_dirty = 0;
1166         unsigned shared_clean = 0;
1167         unsigned private_dirty = 0;
1168         unsigned private_clean = 0;
1169         unsigned pss = 0;
1170         unsigned long start = 0;
1171         unsigned long end = 0;
1172         unsigned private_clean_total = 0;
1173         unsigned private_dirty_total = 0;
1174         unsigned shared_clean_total = 0;
1175         unsigned shared_dirty_total = 0;
1176         int duplication = 0;
1177
1178         milist = load_maps(pid);
1179
1180         if (milist == 0) {
1181                 fprintf(stderr, "cannot get /proc/smaps for pid %d\n", pid);
1182                 return 1;
1183         }
1184
1185         if (!sum) {
1186                 printf(" S(CODE)  S(DATA)  P(CODE)  P(DATA)  ADDR(start-end)"
1187                         "  OBJECT NAME\n");
1188                 printf("-------- -------- -------- -------- -----------------"
1189                         "------------------------------\n");
1190         } else {
1191                 printf(" S(CODE)  S(DATA)  P(CODE)  P(DATA)  PSS\n");
1192                 printf("-------- -------- -------------------"
1193                         "------------------\n");
1194         }
1195         for (mi = milist; mi; mi = mi->next) {
1196                 shared_clean += mi->shared_clean;
1197                 shared_dirty += mi->shared_dirty;
1198                 private_clean += mi->private_clean;
1199                 private_dirty += mi->private_dirty;
1200                 pss += mi->pss;
1201
1202                 shared_clean_total += mi->shared_clean;
1203                 shared_dirty_total += mi->shared_dirty;
1204                 private_clean_total += mi->private_clean;
1205                 private_dirty_total += mi->private_dirty;
1206
1207                 if (!duplication)
1208                         start = mi->start;
1209
1210                 if ((mi->next && !strcmp(mi->next->name, mi->name)) &&
1211                     (mi->next->start == mi->end)) {
1212                         duplication = 1;
1213                         continue;
1214                 }
1215                 end = mi->end;
1216                 duplication = 0;
1217
1218                 if (!sum) {
1219                         printf("%8d %8d %8d %8d %08lx-%08lx %s\n",
1220                                 shared_clean, shared_dirty, private_clean,
1221                                 private_dirty, start, end, mi->name);
1222                 }
1223                 shared_clean = 0;
1224                 shared_dirty = 0;
1225                 private_clean = 0;
1226                 private_dirty = 0;
1227         }
1228         if (sum) {
1229                 printf("%8d %8d %8d %8d %18d\n",
1230                        shared_clean_total,
1231                        shared_dirty_total,
1232                        private_clean_total,
1233                        private_dirty_total,
1234                        pss);
1235         }
1236
1237         return 1;
1238 }
1239
1240 int main(int argc, char *argv[])
1241 {
1242         int usage = 1;
1243         sum = 0;
1244
1245         if (argc > 1) {
1246                 if (!strncmp(argv[1], "-r", strlen("-r")+1)) {
1247                         if (argc >= 3)
1248                                 show_rss(OUTPUT_FILE, argv[2]);
1249                         else
1250                                 show_rss(OUTPUT_UART, NULL);
1251                         usage = 0;
1252                 } else if (!strncmp(argv[1], "-s", strlen("-s")+1)) {
1253                         sum = 1;
1254                         if (argc == 3 && atoi(argv[2]) > 0) {
1255                                 show_map_new(atoi(argv[2]));
1256                                 usage = 0;
1257                         }
1258                 } else if (!strncmp(argv[1], "-a", strlen("-a")+1)) {
1259                         verbos = 0;
1260                         show_map_all_new(OUTPUT_UART, NULL);
1261                         usage = 0;
1262                 } else if (!strncmp(argv[1], "-v", strlen("-v")+1)) {
1263                         verbos = 1;
1264                         show_map_all_new(OUTPUT_UART, NULL);
1265                         usage = 0;
1266                 } else if (!strncmp(argv[1], "-f", strlen("-f")+1)) {
1267                         if (argc >= 3) {
1268                                 verbos = 1;
1269                                 show_map_all_new(OUTPUT_FILE, argv[2]);
1270                                 usage = 0;
1271                         }
1272                 } else if (argc == 2 && atoi(argv[1]) > 0) {
1273                         show_map_new(atoi(argv[1]));
1274                         usage = 0;
1275                 }
1276         }
1277         if (usage) {
1278                 fprintf(stderr,
1279                         "memps [-a] | [-v] | [-s] <pid> | [-f] <output file full path>\n"
1280                         "        -s = sum (show only sum of each)\n"
1281                         "        -f = all (show all processes via output file)\n"
1282                         "        -a = all (show all processes)\n"
1283                         "        -v = verbos (show all processes in detail)\n");
1284         }
1285
1286         return 0;
1287 }