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