Surpress Svace 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
43 #define BUF_MAX         (BUFSIZ)            /* most optimal for libc::stdio */
44 #define BUF_INC_SIZE    (512 * 1024)        /* maximal SMAPS I saw 2 MB     */
45 #define KB(bytes)       ((bytes)/1024)
46
47 #define BYTE_TO_KBYTE(b) ((b) >> 10)
48 #define BYTE_TO_MBYTE(b) ((b) >> 20)
49 #define BYTE_TO_GBYTE(b) ((b) >> 30)
50
51 #define KBYTE_TO_BYTE(k) ((k) << 10)
52 #define KBYTE_TO_MBYTE(k) ((k) >> 10)
53 #define KBYTE_TO_GBYTE(k) ((k) >> 20)
54
55 #define GBYTE_TO_BYTE(g) ((g) << 30)
56 #define GBYTE_TO_KBYTE(g) ((g) << 20)
57 #define GBYTE_TO_MBYTE(g) ((g) << 10)
58
59 typedef struct geminfo geminfo;
60 typedef struct mapinfo mapinfo;
61 typedef struct trib_mapinfo trib_mapinfo;
62
63 enum {
64         OUTPUT_UART,
65         OUTPUT_FILE,
66         NUM_OUTPUT_TYPE
67 };
68
69 struct mapinfo {
70         mapinfo *next;
71         unsigned long start;
72         unsigned long end;
73         unsigned size;
74         unsigned swap;
75         unsigned rss;
76         unsigned pss;
77         unsigned shared_clean;
78         unsigned shared_dirty;
79         unsigned private_clean;
80         unsigned private_dirty;
81         char *perm;
82         char *name;
83 };
84
85 /* classify normal, graphic and other devices memory */
86 struct trib_mapinfo {
87         unsigned shared_clean;
88         unsigned shared_dirty;
89         unsigned private_clean;
90         unsigned private_dirty;
91         unsigned shared_clean_pss;
92         unsigned shared_dirty_pss;
93         unsigned swap;
94         unsigned rss;
95         unsigned pss;
96         unsigned size;
97         unsigned graphic_3d;
98         unsigned gem_rss;
99         unsigned gem_pss;
100         unsigned peak_rss;
101         unsigned other_devices;
102         unsigned gem_mmap;
103 };
104
105 struct geminfo {
106         geminfo *next;
107         unsigned int tgid;
108         unsigned rss_size;
109         unsigned pss_size;
110         unsigned hcount;
111 };
112
113 static int smaps_lcnt;
114 static int sum;
115 static int verbos;
116
117 /* reads file contents into memory */
118 static char* cread(const char* path)
119 {
120         /* once allocated area for reads */
121         static char*    text = NULL;
122         static size_t   size = 0;
123
124         ssize_t ret;
125         char*   ptr = text;
126         size_t  cap = size;
127         int     fd  = open(path, O_RDONLY);
128
129         if (fd < 0) {
130                 return NULL;
131         }
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, int line_cnt)
323 {
324         char* line;
325         mapinfo *mi;
326         int len;
327         int n;
328
329         if ((--line_cnt <= 0) || (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
346         while (line_cnt-- && (line = cgets(smaps))) {
347                 if (sscanf(line, "Size: %d kB", &mi->size) == 1) {}
348                 else if (sscanf(line, "Rss: %d kB", &mi->rss) == 1) {}
349                 else if (sscanf(line, "Pss: %d kB", &mi->pss) == 1) {}
350                 else if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) == 1) {}
351                 else if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) == 1) {}
352                 else if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) == 1) {}
353                 else if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) == 1) {}
354                 else if (sscanf(line, "Swap: %d kB", &mi->swap) == 1) {}
355         }
356
357         return mi;
358 }
359
360 static unsigned total_gem_memory(void)
361 {
362         FILE *gem_fp;
363         unsigned total_gem_mem = 0;
364         unsigned name, size, handles, refcount;
365         char line[BUF_MAX];
366
367         gem_fp = fopen("/proc/dri/0/gem_names", "r");
368         if(gem_fp == NULL) {
369                 fprintf(stderr,
370                 "cannot open /proc/dir/0/gem_names\n");
371                 return 0;
372         }
373
374         if (fgets(line, BUF_MAX, gem_fp) == NULL) {
375                 fclose(gem_fp);
376                 return 0;
377         }
378
379         while (fgets(line, BUF_MAX, gem_fp) != NULL)
380                 if (sscanf(line, "%d %d %d %d\n",
381                     &name, &size, &handles, &refcount) == 4)
382                         total_gem_mem += size;
383         fclose(gem_fp);
384
385         return total_gem_mem;
386 }
387
388
389 int fread_uint(const char *path, u_int32_t *number)
390 {
391         FILE *f = NULL;
392         int ret;
393
394         f = fopen(path, "r");
395
396         if(!f) {
397                 fprintf(stderr,"Fail to open %s file.\n", path);
398                 return -1;
399         }
400
401         ret = fscanf(f, "%u", number);
402         if(ret == EOF) {
403                 fprintf(stderr,"Fail to read file\n");
404                 fclose(f);
405                 return -1;
406         }
407
408         fclose(f);
409         return 0;
410 }
411
412 #define MAX_PATH_LENGTH 512
413 static int cgroup_read_node(const char *cgroup_name,
414                 const char *file_name, unsigned int *value)
415 {
416         char buf[MAX_PATH_LENGTH];
417         int ret;
418         snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name);
419         ret = fread_uint(buf, value);
420         return ret;
421 }
422
423 /**
424  * @desc Provides usage in bytes for provided memory cgroup. Works
425  * with/without swap accounting.
426  *
427  * @param memcg_path[in] Full path to memory cgroup
428  * @param swap[in] Boolean value for deciding if account usage with swap
429  * @return current cgroup usage in bytes or 0 on error
430  */
431 static unsigned int get_memcg_usage(const char *memcg_path, bool swap)
432 {
433         int ret;
434         unsigned int usage;
435
436         if (swap) {
437                 ret = cgroup_read_node(memcg_path,
438                                 "/memory.memsw.usage_in_bytes", &usage);
439         } else {
440                 ret = cgroup_read_node(memcg_path, "/memory.usage_in_bytes",
441                                 &usage);
442         }
443
444         if (ret != 0)
445                 usage = 0;
446
447         return usage;
448 }
449
450 static void get_memcg_info(FILE *output_fp)
451 {
452         char buf[PATH_MAX];
453         DIR *pdir = NULL;
454         struct dirent entry;
455         struct dirent *result;
456         struct stat path_stat;
457         long usage_swap;
458         unsigned long usage, usage_with_swap;
459         int ret;
460
461         fprintf(output_fp,"====================================================================\n");
462         fprintf(output_fp,"MEMORY CGROUPS USAGE INFO\n");
463
464         pdir = opendir(MEMCG_PATH);
465         if (pdir == NULL) {
466                 fprintf(stderr,"cannot read directory %s", MEMCG_PATH);
467                 return;
468         }
469
470         while (!(ret = readdir_r(pdir, &entry, &result)) && result != NULL) {
471                 snprintf(buf, sizeof(buf), "%s/%s", MEMCG_PATH, entry.d_name);
472                 /* If can't stat then ignore */
473                 if (stat(buf, &path_stat) != 0)
474                         continue;
475
476                 /* If it's not directory or it's parent path then ignore */
477                 if (!(S_ISDIR(path_stat.st_mode) &&
478                         strncmp(entry.d_name, "..", 3)))
479                         continue;
480
481                 usage = get_memcg_usage(buf, false);
482                 usage_with_swap = get_memcg_usage(buf, true);
483                 /* It is posible by rounding errors to get negative value */
484                 usage_swap = usage_with_swap - usage;
485                 if (usage_swap < 0)
486                         usage_swap = 0;
487
488                 /* Case of root cgroup in hierarchy */
489                 if (!strncmp(entry.d_name, ".", 2))
490                         fprintf(output_fp, "%13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB) \n\n",
491                                 MEMCG_PATH, BYTE_TO_MBYTE(usage),
492                                 BYTE_TO_KBYTE(usage),
493                                 BYTE_TO_MBYTE(usage_with_swap),
494                                 BYTE_TO_KBYTE(usage_with_swap),
495                                 BYTE_TO_MBYTE(usage_swap),
496                                 BYTE_TO_KBYTE(usage_swap));
497                 else
498                         fprintf(output_fp, "memcg: %13s  Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB)\n",
499                                 entry.d_name, BYTE_TO_MBYTE(usage),
500                                 BYTE_TO_KBYTE(usage),
501                                 BYTE_TO_MBYTE(usage_with_swap),
502                                 BYTE_TO_KBYTE(usage_with_swap),
503                                 BYTE_TO_MBYTE(usage_swap),
504                                 BYTE_TO_KBYTE(usage_swap));
505
506         }
507
508         closedir(pdir);
509 }
510
511 static void get_mem_info(FILE *output_fp)
512 {
513         char buf[PATH_MAX];
514         FILE *fp;
515         char *idx;
516         unsigned int free = 0, cached = 0;
517         unsigned int total_mem = 0, available = 0, used;
518         unsigned int swap_total = 0, swap_free = 0, zram_used, swap_used;
519         unsigned int used_ratio;
520
521         if (output_fp == NULL)
522                 return;
523
524         fp = fopen("/proc/meminfo", "r");
525
526         if (!fp) {
527                 fprintf(stderr, "%s open failed, %p", buf, fp);
528                 return;
529         }
530
531         while (fgets(buf, PATH_MAX, fp) != NULL) {
532                 if ((idx = strstr(buf, "MemTotal:"))) {
533                         idx += strlen("Memtotal:");
534                         while (*idx < '0' || *idx > '9')
535                                 idx++;
536                         total_mem = atoi(idx);
537                 } else if ((idx = strstr(buf, "MemFree:"))) {
538                         idx += strlen("MemFree:");
539                         while (*idx < '0' || *idx > '9')
540                                 idx++;
541                         free = atoi(idx);
542                 } else if ((idx = strstr(buf, "MemAvailable:"))) {
543                         idx += strlen("MemAvailable:");
544                         while (*idx < '0' || *idx > '9')
545                                 idx++;
546                         available = atoi(idx);
547                 } else if((idx = strstr(buf, "Cached:")) && !strstr(buf, "Swap")) {
548                         idx += strlen("Cached:");
549                         while (*idx < '0' || *idx > '9')
550                                 idx++;
551                         cached = atoi(idx);
552                 } else if((idx = strstr(buf, "SwapTotal:"))) {
553                         idx += strlen("SwapTotal:");
554                         while (*idx < '0' || *idx > '9')
555                                 idx++;
556                         swap_total = atoi(idx);
557                 } else if((idx = strstr(buf, "SwapFree:"))) {
558                         idx += strlen("SwapFree");
559                         while (*idx < '0' || *idx > '9')
560                                 idx++;
561                         swap_free = atoi(idx);
562                         break;
563                 }
564         }
565
566         if (total_mem == 0) {
567                 fprintf(stderr, "cannot get total memory size\n");
568                 fclose(fp);
569                 return;
570         }
571
572         if (available == 0)
573                 available = free + cached;
574         used = total_mem - available;
575         used_ratio = used * 100 / total_mem;
576         swap_used = swap_total - swap_free;
577
578         if (fread_uint(ZRAM_USED_PATH, &zram_used) != 0)
579                 zram_used = 0;
580
581         fprintf(output_fp,
582                 "====================================================================\n");
583
584
585         fprintf(output_fp, "Total RAM size: \t%15d MB( %6d kB)\n",
586                         total_mem >> 10, total_mem);
587
588         fprintf(output_fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n",
589                         (total_mem - free) >> 10, total_mem - free);
590
591         fprintf(output_fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n",
592                         used >> 10, used);
593
594         fprintf(output_fp, "Used (Mem):  \t\t%15d MB( %6d kB)\n",
595                         used >> 10, used);
596
597         fprintf(output_fp, "Used (Swap): \t\t%15d MB( %6d kB)\n",
598                         swap_used >> 10, swap_used);
599
600         fprintf(output_fp, "Used (Zram block device): %13d MB( %6d kB)\n",
601             BYTE_TO_MBYTE(zram_used), BYTE_TO_KBYTE(zram_used));
602
603         fprintf(output_fp, "Used Ratio: \t\t%15d  %%\n", used_ratio);
604
605         fprintf(output_fp, "Mem Free:\t\t%15d MB( %6d kB)\n",
606                         free >> 10, free);
607
608         fprintf(output_fp, "Available (Free+Reclaimable):%10d MB( %6d kB)\n",
609                         available >> 10,
610                         available);
611         fclose(fp);
612 }
613
614 static int get_tmpfs_info(FILE *output_fp)
615 {
616         FILE *fp;
617         char line[BUF_MAX];
618         char tmpfs_mp[NAME_MAX];        /* tmpfs mount point */
619         struct statfs tmpfs_info;
620
621         if (output_fp == NULL)
622                 return -1;
623
624         fp = fopen("/etc/mtab", "r");
625         if (fp == NULL)
626                 return -1;
627
628         fprintf(output_fp,
629                 "====================================================================\n");
630         fprintf(output_fp, "TMPFS INFO\n");
631
632         while (fgets(line, BUF_MAX, fp) != NULL) {
633                 if (sscanf(line, "tmpfs %s tmpfs", tmpfs_mp) == 1) {
634                         statfs(tmpfs_mp, &tmpfs_info);
635                         fprintf(output_fp,
636                                 "tmpfs %16s  Total %8ld KB, Used %8ld, Avail %8ld\n",
637                                 tmpfs_mp,
638                                 /* 1 block is 4 KB */
639                                 tmpfs_info.f_blocks * 4,
640                                 (tmpfs_info.f_blocks - tmpfs_info.f_bfree) * 4,
641                                 tmpfs_info.f_bfree * 4);
642                 }
643         }
644         fclose(fp);
645         return 0;
646 }
647
648 mapinfo *load_maps(int pid)
649 {
650         char* smaps;
651         char tmp[128];
652         mapinfo *milist = 0;
653         mapinfo *mi;
654
655         snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
656         smaps = cread(tmp);
657         if (smaps == NULL)
658                 return 0;
659
660         while ((mi = read_mapinfo(&smaps, smaps_lcnt)) != 0) {
661                 if (milist) {
662                         if ((!strcmp(mi->name, milist->name)
663                              && (mi->name[0] != '['))) {
664                                 milist->size += mi->size;
665                                 milist->swap += mi->swap;
666                                 milist->rss += mi->rss;
667                                 milist->pss += mi->pss;
668                                 milist->shared_clean += mi->shared_clean;
669                                 milist->shared_dirty += mi->shared_dirty;
670                                 milist->private_clean += mi->private_clean;
671                                 milist->private_dirty += mi->private_dirty;
672
673                                 milist->end = mi->end;
674                                 strncpy(milist->perm, mi->perm, 4);
675                                 free(mi->perm);
676                                 free(mi->name);
677                                 free(mi);
678                                 continue;
679                         }
680                 }
681                 mi->next = milist;
682                 milist = mi;
683         }
684
685         return milist;
686 }
687
688 static void init_trib_mapinfo(trib_mapinfo *tmi)
689 {
690         if (!tmi)
691                 return;
692         tmi->shared_clean = 0;
693         tmi->shared_dirty = 0;
694         tmi->private_clean = 0;
695         tmi->private_dirty = 0;
696         tmi->swap = 0;
697         tmi->shared_clean_pss = 0;
698         tmi->shared_dirty_pss = 0;
699         tmi->rss = 0;
700         tmi->pss = 0;
701         tmi->size = 0;
702         tmi->graphic_3d = 0;
703         tmi->gem_rss = 0;
704         tmi->gem_pss = 0;
705         tmi->peak_rss = 0;
706         tmi->other_devices = 0;
707         tmi->gem_mmap = 0;
708 }
709
710 static int
711 get_trib_mapinfo(unsigned int tgid, mapinfo *milist,
712                  geminfo *gilist, trib_mapinfo *result)
713
714 {
715         mapinfo *mi;
716         mapinfo *temp = NULL;
717         geminfo *gi;
718
719         if (!result)
720                 return -EINVAL;
721
722         init_trib_mapinfo(result);
723         for (mi = milist; mi;) {
724                 if (strstr(mi->name, STR_SGX_PATH)) {
725                         result->graphic_3d += mi->pss;
726                 } else if (strstr(mi->name, STR_3D_PATH1) ||
727                         strstr(mi->name, STR_3D_PATH2)) {
728                         result->graphic_3d += mi->size;
729                 } else if (mi->rss != 0 && mi->pss == 0
730                            && mi->shared_clean == 0
731                            && mi->shared_dirty == 0
732                            && mi->private_clean == 0
733                            && mi->private_dirty == 0
734                            && mi->swap == 0) {
735                         result->other_devices += mi->size;
736                 } else if (!strncmp(mi->name, STR_DRM_PATH1,
737                                 sizeof(STR_DRM_PATH1)) ||
738                                 !strncmp(mi->name, STR_DRM_PATH2,
739                                 sizeof(STR_DRM_PATH2))) {
740                         result->gem_mmap += mi->rss;
741                 } else {
742                         result->shared_clean += mi->shared_clean;
743                         result->shared_dirty += mi->shared_dirty;
744                         result->private_clean += mi->private_clean;
745                         result->private_dirty += mi->private_dirty;
746                         result->swap += mi->swap;
747                         result->rss += mi->rss;
748                         result->pss += mi->pss;
749                         result->size += mi->size;
750
751                         if(mi->shared_clean != 0)
752                                 result->shared_clean_pss += mi->pss;
753                         else if (mi->shared_dirty != 0)
754                                 result->shared_dirty_pss += mi->pss;
755                 }
756
757                 temp = mi;
758                 mi = mi->next;
759                 free(temp->perm);
760                 free(temp->name);
761                 free(temp);
762                 temp = NULL;
763         }
764
765         result->peak_rss = get_peak_rss(tgid);
766         if (result->peak_rss < result->rss)
767                 result->peak_rss = result->rss;
768         if (result->gem_mmap > 0)
769                 result->peak_rss -= result->gem_mmap;
770
771         gi = find_geminfo(tgid, gilist);
772         if (gi != NULL) {
773                 result->gem_rss = gi->rss_size;
774                 result->gem_pss = gi->pss_size;
775         }
776
777         return 0;
778 }
779
780 static int get_cmdline(unsigned int pid, char *cmdline)
781 {
782         FILE *fp;
783         char buf[NAME_MAX] = {0, };
784         int ret = -1;
785
786         snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
787         fp = fopen(buf, "r");
788         if (fp == 0) {
789                 fprintf(stderr, "cannot file open %s\n", buf);
790                 return ret;
791         }
792         if ((ret = fscanf(fp, "%4095s", cmdline)) < 1) {
793                 fclose(fp);
794                 return ret;
795         }
796         fclose(fp);
797
798         return ret;
799 }
800
801 static int get_oomscoreadj(unsigned int pid)
802 {
803         FILE *fp;
804         char tmp[256];
805         int oomadj_val;
806
807         snprintf(tmp, sizeof(tmp), "/proc/%d/oom_score_adj", pid);
808         fp = fopen(tmp, "r");
809
810         if (fp == NULL) {
811                 oomadj_val = -50;
812                 return oomadj_val;
813         }
814         if (fgets(tmp, sizeof(tmp), fp) == NULL) {
815                 oomadj_val = -100;
816                 fclose(fp);
817                 return oomadj_val;
818         }
819
820         oomadj_val = atoi(tmp);
821
822         fclose(fp);
823         return oomadj_val;
824 }
825
826 static void get_rss(pid_t pid, unsigned long *result)
827 {
828         FILE *fp;
829         char proc_path[PATH_MAX];
830         unsigned long rss = 0;
831
832         *result = 0;
833
834         snprintf(proc_path, sizeof(proc_path), "/proc/%d/statm", pid);
835         fp = fopen(proc_path, "r");
836         if (fp == NULL)
837                 return;
838
839         if (fscanf(fp, "%*s %ld", &rss) < 1) {
840                 fclose(fp);
841                 return;
842         }
843
844         fclose(fp);
845
846         /* convert page to Kb */
847         *result = rss << 2;
848         return;
849 }
850
851 static void show_rss(int output_type, char *output_path)
852 {
853         DIR *pDir = NULL;
854         struct dirent curdir;
855         struct dirent *result;
856         pid_t pid;
857         char cmdline[PATH_MAX];
858         FILE *output_file = NULL;
859         int oom_score_adj;
860         unsigned long rss;
861         int ret;
862
863         pDir = opendir("/proc");
864         if (pDir == NULL) {
865                 fprintf(stderr, "cannot read directory /proc.\n");
866                 return;
867         }
868
869         if (output_type == OUTPUT_FILE && output_path) {
870                 output_file = fopen(output_path, "w+");
871                 if (!output_file) {
872                         fprintf(stderr, "cannot open output file(%s)\n",
873                                 output_path);
874                         closedir(pDir);
875                         exit(1);
876                 }
877         } else
878                 output_file = stdout;
879
880
881         fprintf(output_file,
882                         "     PID    RSS    OOM_SCORE    COMMAND\n");
883
884         while (!(ret = readdir_r(pDir, &curdir, &result)) && result != NULL) {
885                 pid = atoi(curdir.d_name);
886                 if (pid < 1 || pid > 32768 || pid == getpid())
887                         continue;
888
889                 if (get_cmdline(pid, cmdline) < 0)
890                         continue;
891                 get_rss(pid, &rss);
892                 oom_score_adj = get_oomscoreadj(pid);
893
894                 fprintf(output_file,
895                                 "%8d %8lu %8d          %s\n",
896                                 pid,
897                                 rss,
898                                 oom_score_adj,
899                                 cmdline);
900
901
902         } /* end of while */
903
904         get_tmpfs_info(output_file);
905         get_mem_info(output_file);
906
907         fclose(output_file);
908         closedir(pDir);
909
910         return;
911 }
912
913 static int show_map_all_new(int output_type, char *output_path)
914 {
915         DIR *pDir = NULL;
916         struct dirent curdir;
917         struct dirent *result;
918         unsigned int pid;
919         mapinfo *milist;
920         geminfo *glist;
921         unsigned total_pss = 0;
922         unsigned total_private = 0;
923         unsigned total_private_code = 0;
924         unsigned total_private_data = 0;
925         unsigned total_shared_code = 0;
926         unsigned total_shared_data = 0;
927         unsigned total_shared_code_pss = 0;
928         unsigned total_shared_data_pss = 0;
929         unsigned total_swap = 0;
930         unsigned total_rss = 0;
931         unsigned total_graphic_3d = 0;
932         unsigned total_gem_rss = 0;
933         unsigned total_gem_pss = 0;
934         unsigned total_peak_rss = 0;
935         unsigned total_allocated_gem = 0;
936         trib_mapinfo tmi;
937         char cmdline[PATH_MAX];
938         FILE *output_file = NULL;
939         int oom_score_adj;
940
941         int r;
942
943         pDir = opendir("/proc");
944         if (pDir == NULL) {
945                 fprintf(stderr, "cannot read directory /proc.\n");
946                 return 0;
947         }
948
949         if (output_type == OUTPUT_FILE && output_path) {
950                 output_file = fopen(output_path, "w+");
951                 if (!output_file) {
952                         fprintf(stderr, "cannot open output file(%s)\n",
953                                 output_path);
954                         closedir(pDir);
955                         exit(1);
956                 }
957         } else
958                 output_file = stdout;
959
960         glist = load_geminfo();
961
962         if (!sum) {
963                 if (verbos)
964                         fprintf(output_file,
965                                         "     PID  S(CODE)  S(DATA)  P(CODE)  P(DATA)"
966                                         "     PEAK      PSS       3D"
967                                         "     GEM(PSS)  GEM(RSS)    SWAP"
968                                         "     OOM_SCORE_ADJ    COMMAND\n");
969                 else
970                         fprintf(output_file,
971                                         "     PID     CODE     DATA     PEAK     PSS"
972                                         "     3D      GEM(PSS)      SWAP      COMMAND\n");
973         }
974
975         while (!(r = readdir_r(pDir, &curdir, &result)) && result != NULL) {
976                 pid = atoi(curdir.d_name);
977                 if (pid < 1 || pid > 32768 || pid == getpid())
978                         continue;
979
980                 if (get_cmdline(pid, cmdline) < 0)
981                         continue;
982
983                 milist = load_maps(pid);
984                 if (milist == 0)
985                         continue;
986
987                 /* get classified map info */
988                 get_trib_mapinfo(pid, milist, glist, &tmi);
989                 oom_score_adj = get_oomscoreadj(pid);
990
991                 if (!sum) {
992                         if (verbos)
993                                 fprintf(output_file,
994                                         "%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d %8d"
995                                         " %8d \t\t%s\n",
996                                         pid,
997                                         tmi.shared_clean, tmi.shared_dirty,
998                                         tmi.private_clean, tmi.private_dirty,
999                                         tmi.peak_rss, tmi.pss, tmi.graphic_3d,
1000                                         tmi.gem_pss, tmi.gem_rss, tmi.swap,
1001                                         oom_score_adj, cmdline);
1002                         else
1003                                 fprintf(output_file,
1004                                         "%8d %8d %8d %8d %8d %8d %8d %8d      %s\n",
1005                                         pid,
1006                                         tmi.shared_clean +
1007                                         tmi.private_clean,
1008                                         tmi.shared_dirty + tmi.private_dirty,
1009                                         tmi.peak_rss,
1010                                         tmi.pss,
1011                                         tmi.graphic_3d,
1012                                         tmi.gem_pss,
1013                                         tmi.swap, cmdline);
1014
1015                         if (tmi.other_devices != 0)
1016                                 fprintf(output_file,
1017                                         "%s(%d) %d KB may mapped by device(s).\n",
1018                                         cmdline, pid, tmi.other_devices);
1019                 }
1020
1021                 total_private += (tmi.private_clean + tmi.private_dirty);
1022                 total_pss += tmi.pss;
1023                 total_rss += tmi.rss;
1024                 total_graphic_3d += tmi.graphic_3d;
1025                 total_gem_rss += tmi.gem_rss;
1026                 total_gem_pss += tmi.gem_pss;
1027                 total_private_code += tmi.private_clean;
1028                 total_private_data += tmi.private_dirty;
1029                 total_swap += tmi.swap;
1030                 total_shared_code += tmi.shared_clean;
1031                 total_shared_data += tmi.shared_dirty;
1032                 total_peak_rss += tmi.peak_rss;
1033
1034                 total_shared_code_pss += tmi.shared_clean_pss;
1035                 total_shared_data_pss += tmi.shared_dirty_pss;
1036
1037         } /* end of while */
1038
1039         total_allocated_gem = KB(total_gem_memory());
1040         fprintf(output_file,
1041                         "==============================================="
1042                         "===============================================\n");
1043         if (verbos) {
1044                 fprintf(output_file,
1045                                 "TOTAL:      S(CODE) S(DATA) P(CODE)  P(DATA)"
1046                                 "    PEAK     PSS       3D    "
1047                                 "GEM(PSS) GEM(RSS) GEM(ALLOC) SWAP TOTAL(KB)\n");
1048                 fprintf(output_file,
1049                         "         %8d %8d %8d %8d %8d %8d %8d"
1050                         " %8d %8d %8d %8d %8d\n",
1051                         total_shared_code, total_shared_data,
1052                         total_private_code, total_private_data,
1053                         total_peak_rss, total_pss, total_graphic_3d,
1054                         total_gem_pss, total_gem_rss,
1055                         total_allocated_gem, total_swap,
1056                         total_pss + total_graphic_3d +
1057                         total_allocated_gem);
1058         } else {
1059                 fprintf(output_file,
1060                         "TOTAL:        CODE     DATA    PEAK     PSS     "
1061                         "3D    GEM(PSS) GEM(ALLOC)     TOTAL(KB)\n");
1062                 fprintf(output_file, "         %8d %8d %8d %8d %8d %8d %7d %8d %8d\n",
1063                         total_shared_code + total_private_code,
1064                         total_shared_data + total_private_data,
1065                         total_peak_rss, total_pss,
1066                         total_graphic_3d, total_gem_pss,
1067                         total_allocated_gem, total_swap,
1068                         total_pss + total_graphic_3d +
1069                         total_allocated_gem);
1070
1071         }
1072
1073         if (verbos)
1074                 fprintf(output_file,
1075                         "* S(CODE): shared clean memory, it includes"
1076                         " duplicated memory\n"
1077                         "* S(DATA): shared dirty memory, it includes"
1078                         " duplicated memory\n"
1079                         "* P(CODE): private clean memory\n"
1080                         "* P(DATA): private dirty memory\n"
1081                         "* PEAK: peak memory usage of S(CODE) + S(DATA)"
1082                         " + P(CODE) + P(DATA)\n"
1083                         "* PSS: Proportional Set Size\n"
1084                         "* 3D: memory allocated by GPU driver\n"
1085                         "* GEM(PSS): GEM memory devided by # of sharers\n"
1086                         "* GEM(RSS): GEM memory including duplicated memory\n"
1087                         "* GEM(ALLOC): sum of unique gem memory in the system\n"
1088                         "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1089         else
1090                 fprintf(output_file,
1091                         "* CODE: shared and private clean memory\n"
1092                         "* DATA: shared and private dirty memory\n"
1093                         "* PEAK: peak memory usage of CODE + DATA\n"
1094                         "* PSS: Proportional Set Size\n"
1095                         "* 3D: memory allocated by GPU driver\n"
1096                         "* GEM(PSS): GEM memory deviced by # of sharers\n"
1097                         "* GEM(ALLOC): sum of unique GEM memory in the system\n"
1098                         "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
1099
1100         get_tmpfs_info(output_file);
1101         get_memcg_info(output_file);
1102         get_mem_info(output_file);
1103
1104         fclose(output_file);
1105         free(glist);
1106         closedir(pDir);
1107         return 1;
1108 }
1109
1110 static int show_map_new(int pid)
1111 {
1112         mapinfo *milist;
1113         mapinfo *mi;
1114         unsigned shared_dirty = 0;
1115         unsigned shared_clean = 0;
1116         unsigned private_dirty = 0;
1117         unsigned private_clean = 0;
1118         unsigned pss = 0;
1119         unsigned long start = 0;
1120         unsigned long end = 0;
1121         unsigned private_clean_total = 0;
1122         unsigned private_dirty_total = 0;
1123         unsigned shared_clean_total = 0;
1124         unsigned shared_dirty_total = 0;
1125         int duplication = 0;
1126
1127         milist = load_maps(pid);
1128
1129         if (milist == 0) {
1130                 fprintf(stderr, "cannot get /proc/smaps for pid %d\n", pid);
1131                 return 1;
1132         }
1133
1134         if (!sum) {
1135                 printf(" S(CODE)  S(DATA)  P(CODE)  P(DATA)  ADDR(start-end)"
1136                         "  OBJECT NAME\n");
1137                 printf("-------- -------- -------- -------- -----------------"
1138                         "------------------------------\n");
1139         } else {
1140                 printf(" S(CODE)  S(DATA)  P(CODE)  P(DATA)  PSS\n");
1141                 printf("-------- -------- -------------------"
1142                         "------------------\n");
1143         }
1144         for (mi = milist; mi; mi = mi->next) {
1145                 shared_clean += mi->shared_clean;
1146                 shared_dirty += mi->shared_dirty;
1147                 private_clean += mi->private_clean;
1148                 private_dirty += mi->private_dirty;
1149                 pss += mi->pss;
1150
1151                 shared_clean_total += mi->shared_clean;
1152                 shared_dirty_total += mi->shared_dirty;
1153                 private_clean_total += mi->private_clean;
1154                 private_dirty_total += mi->private_dirty;
1155
1156                 if (!duplication)
1157                         start = mi->start;
1158
1159                 if ((mi->next && !strcmp(mi->next->name, mi->name)) &&
1160                     (mi->next->start == mi->end)) {
1161                         duplication = 1;
1162                         continue;
1163                 }
1164                 end = mi->end;
1165                 duplication = 0;
1166
1167                 if (!sum) {
1168                         printf("%8d %8d %8d %8d %08lx-%08lx %s\n",
1169                                 shared_clean, shared_dirty, private_clean,
1170                                 private_dirty, start, end, mi->name);
1171                 }
1172                 shared_clean = 0;
1173                 shared_dirty = 0;
1174                 private_clean = 0;
1175                 private_dirty = 0;
1176         }
1177         if (sum) {
1178                 printf("%8d %8d %8d %8d %18d\n",
1179                        shared_clean_total,
1180                        shared_dirty_total,
1181                        private_clean_total,
1182                        private_dirty_total,
1183                        pss);
1184         }
1185
1186         return 1;
1187 }
1188
1189 int get_fixed_smaps_lcnt(void)
1190 {
1191         struct utsname buf;
1192         int ret;
1193
1194         ret = uname(&buf);
1195
1196         if (!ret) {
1197                 char *pch;
1198                 char str[3];
1199                 int sub_version;
1200                 pch = strstr(buf.release, ".");
1201                 strncpy(str, pch+1, 2);
1202                 sub_version = atoi(str);
1203
1204                 if (buf.release[0] >= '4') {
1205                         if (sub_version >= 4)
1206                                 ret = 19;
1207                                 /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty,
1208                                  * Private_Clean, Private_Drity,
1209                                  * Referenced, Anonymous, AnonHugePages, Shared_Hugetlb,
1210                                  * Private_Hugetlb, Swap, SwapPss, KernelPageSize,
1211                                  * MMUPageSize, Locked, VmFlags */
1212                         else if (sub_version == 3)
1213                                 ret = 17;
1214                                 /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty,
1215                                  * Private_Clean, Private_Drity,
1216                                  * Referenced, Anonymous, AnonHugePages, Swap, SwapPss,
1217                                  * KernelPageSize, MMUPageSize, Locked, VmFlags */
1218                         else
1219                                 ret = 16;
1220                                 /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty,
1221                                  * Private_Clean, Private_Drity,
1222                                  * Referenced, Anonymous, AnonHugePages, Swap,
1223                                  * KernelPageSize, MMUPageSize, Locked, VmFlags */
1224                 } else if (buf.release[0] == '3') {
1225                         if (sub_version >= 10)
1226                                 ret = 16;
1227                                 /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty,
1228                                  * Private_Clean, Private_Drity,
1229                                  * Referenced, Anonymous, AnonHugePages, Swap,
1230                                  * KernelPageSize, MMUPageSize, Locked, VmFlags */
1231                         else
1232                                 ret = 15;
1233                                 /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty,
1234                                  * Private_Clean, Private_Drity,
1235                                  * Referenced, Anonymous, AnonHugePages, Swap,
1236                                  * KernelPageSize, MMUPageSize, Locked */
1237                 } else {
1238                         ret = 12;
1239                                 /* Vma, Size, Rss, Pss, Shared_Clean, Shard_Dirty,
1240                                  * Private_Clean, Private_Drity,
1241                                  * Referenced, Swap, KernelPageSize, MMUPageSize */
1242                 }
1243         }
1244         return ret;
1245 }
1246
1247 int get_smaps_lcnt(void)
1248 {
1249         char *buf, *buf_start, *line;
1250         char cmd[64] = "/proc/self/smaps";
1251         int line_count = 0;
1252         long start_addr, end_addr;
1253         unsigned long pg_off;
1254         int major, minor;
1255         char flags[4];
1256
1257         buf_start = cread(cmd);
1258         if (buf_start == NULL) {
1259                 goto error;
1260         }
1261
1262         buf = buf_start;
1263         if ((line = cgets(&buf)) == 0)
1264                 goto error;
1265
1266         if (sscanf(line, "%lx-%lx %s %lx %x:%x",
1267                         &start_addr, &end_addr, flags, &pg_off, &major, &minor) != 6) {
1268                 goto error;
1269         }
1270
1271         while ((line = cgets(&buf)) != 0 ) {
1272                 line_count++;
1273                 if (sscanf(line, "%lx-%lx %s %lx %x:%x",
1274                                 &start_addr, &end_addr, flags, &pg_off, &major, &minor) == 6)
1275                         break;
1276         }
1277
1278         return line_count;
1279
1280 error:
1281         return get_fixed_smaps_lcnt();
1282 }
1283
1284 int main(int argc, char *argv[])
1285 {
1286         int usage = 1;
1287         sum = 0;
1288
1289         if (argc > 1) {
1290                 smaps_lcnt = get_smaps_lcnt();
1291
1292                 if (!strncmp(argv[1], "-r", strlen("-r")+1)) {
1293                         if (argc >= 3)
1294                                 show_rss(OUTPUT_FILE, argv[2]);
1295                         else
1296                                 show_rss(OUTPUT_UART, NULL);
1297                         usage = 0;
1298                 } else if (!strncmp(argv[1], "-s", strlen("-s")+1)) {
1299                         sum = 1;
1300                         if (argc == 3 && atoi(argv[2]) > 0) {
1301                                 show_map_new(atoi(argv[2]));
1302                                 usage = 0;
1303                         }
1304                 } else if (!strncmp(argv[1], "-a", strlen("-a")+1)) {
1305                         verbos = 0;
1306                         show_map_all_new(OUTPUT_UART, NULL);
1307                         usage = 0;
1308                 } else if (!strncmp(argv[1], "-v", strlen("-v")+1)) {
1309                         verbos = 1;
1310                         show_map_all_new(OUTPUT_UART, NULL);
1311                         usage = 0;
1312                 } else if (!strncmp(argv[1], "-f", strlen("-f")+1)) {
1313                         if (argc >= 3) {
1314                                 verbos = 1;
1315                                 show_map_all_new(OUTPUT_FILE, argv[2]);
1316                                 usage = 0;
1317                         }
1318                 } else if (argc == 2 && atoi(argv[1]) > 0) {
1319                         show_map_new(atoi(argv[1]));
1320                         usage = 0;
1321                 }
1322         }
1323         if (usage) {
1324                 fprintf(stderr,
1325                         "memps [-a] | [-v] | [-s] <pid> | [-f] <output file full path>\n"
1326                         "        -s = sum (show only sum of each)\n"
1327                         "        -f = all (show all processes via output file)\n"
1328                         "        -a = all (show all processes)\n"
1329                         "        -v = verbos (show all processes in detail)\n");
1330         }
1331
1332         return 0;
1333 }