tizen 2.3.1 release
[kernel/api/system-resource.git] / src / memps / memps.c
1 /*
2    Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
3
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6
7    Unless required by applicable law or agreed to in writing, software
8    distributed under the License is distributed on an "AS IS" BASIS,
9    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10    See the License for the specific language governing permissions and
11    limitations under the License
12 */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <math.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <sys/vfs.h>
24 #include <linux/limits.h>
25
26 #include <ctype.h>
27 #include <stddef.h>
28
29 #include <dirent.h>
30 #include <sys/utsname.h>
31
32 #define STR_SGX_PATH    "/dev/pvrsrvkm"
33 #define STR_3D_PATH1    "/dev/mali"
34 #define STR_3D_PATH2    "/dev/kgsl-3d0"
35 #define STR_DRM_PATH1   "/drm mm object (deleted)"
36 #define STR_DRM_PATH2   "/dev/dri/card0"
37
38 #define BUF_MAX         (BUFSIZ)            /* most optimal for libc::stdio */
39 #define BUF_INC_SIZE    (512 * 1024)        /* maximal SMAPS I saw 2 MB     */
40 #define KB(bytes)       ((bytes)/1024)
41
42 typedef struct geminfo geminfo;
43 typedef struct mapinfo mapinfo;
44 typedef struct trib_mapinfo trib_mapinfo;
45
46 enum {
47         OUTPUT_UART,
48         OUTPUT_FILE,
49         NUM_OUTPUT_TYPE
50 };
51
52 struct mapinfo {
53         mapinfo *next;
54         unsigned start;
55         unsigned end;
56         unsigned size;
57         unsigned rss;
58         unsigned pss;
59         unsigned shared_clean;
60         unsigned shared_dirty;
61         unsigned private_clean;
62         unsigned private_dirty;
63         char perm[4];
64         char name[1];
65 };
66
67 /* classify normal, graphic and other devices memory */
68 struct trib_mapinfo {
69         unsigned shared_clean;
70         unsigned shared_dirty;
71         unsigned private_clean;
72         unsigned private_dirty;
73         unsigned shared_clean_pss;
74         unsigned shared_dirty_pss;
75         unsigned rss;
76         unsigned pss;
77         unsigned size;
78         unsigned graphic_3d;
79         unsigned gem_rss;
80         unsigned gem_pss;
81         unsigned peak_rss;
82         unsigned other_devices;
83         unsigned gem_mmap;
84 };
85
86 struct geminfo {
87         geminfo *next;
88         unsigned int tgid;
89         unsigned rss_size;
90         unsigned pss_size;
91         unsigned hcount;
92 };
93
94 static int ignore_smaps_field;
95 static int sum;
96 static int verbos;
97
98 /* reads file contents into memory */
99 static char* cread(const char* path)
100 {
101         /* once allocated area for reads */
102         static char*    text = NULL;
103         static size_t   size = 0;
104
105         ssize_t ret;
106         char*   ptr = text;
107         size_t  cap = size;
108         int     fd  = open(path, O_RDONLY);
109
110         if (fd < 0) {
111                 return NULL;
112         }
113
114         do {
115                 /* ensure we have enough space */
116                 if (cap == 0) {
117                         ptr = (char*)realloc(text, size + BUF_INC_SIZE);
118                         if (ptr == NULL) {
119                                 ret = -1;
120                                 break;
121                         }
122
123                         text  = ptr;
124                         ptr   = text + size;
125                         cap   = BUF_INC_SIZE;
126                         size += BUF_INC_SIZE;
127                 }
128                 ret = read(fd, ptr, cap);
129                 if (ret == 0) {
130                         *ptr = 0;
131                 } else if (ret > 0) {
132                         cap -= ret;
133                         ptr += ret;
134                 }
135         } while (ret > 0);
136         close(fd);
137
138         return (ret < 0 ? NULL : text);
139 } /* cread */
140
141 /* like fgets/gets but adjusting contents pointer */
142 static inline char* cgets(char** contents)
143 {
144         if (contents && *contents && **contents) {
145                 char* bos = *contents;          /* begin of string */
146                 char* eos = strchr(bos, '\n');  /* end of string   */
147
148                 if (eos) {
149                         *contents = eos + 1;
150                         *eos      = 0;
151                 } else {
152                         *contents = NULL;
153                 }
154                 return bos;
155         }
156
157         return NULL;
158 } /* cgets */
159
160
161 static unsigned get_peak_rss(unsigned int pid)
162 {
163         static const char field[] = "VmHWM:";
164         char tmp[128];
165         char* line;
166         char* value;
167
168         snprintf(tmp, sizeof(tmp), "/proc/%d/status", pid);
169         line = cread(tmp);
170         if (line == NULL) {
171                 fprintf(stderr, "cannot open %s\n", tmp);
172                 return 0;
173         }
174
175         value = strstr(line, field);
176         if (value) {
177                 value += sizeof(field);
178                 return strtoul(value, NULL, 10);
179         }
180
181         return 0;
182 }
183 #define NUM_GEM_FIELD 6
184
185 static geminfo *read_geminfo(FILE *fp)
186 {
187         geminfo *tgeminfo;
188         char line[BUF_MAX];
189         unsigned int pid, tgid, handle, refcount, hcount;
190         unsigned gem_size;
191
192         if (fgets(line, BUF_MAX, fp) != NULL) {
193                 if (sscanf(line, "%d %d %d %d %d 0x%x",
194                         &pid, &tgid, &handle, &refcount,
195                         &hcount, &gem_size) != NUM_GEM_FIELD)
196                         return NULL;
197
198                 tgeminfo = malloc(sizeof(geminfo));
199                 if (tgeminfo == NULL)
200                         return NULL;
201                 tgeminfo->tgid = tgid;
202                 tgeminfo->hcount = hcount;
203                 tgeminfo->rss_size = KB(gem_size);
204                 tgeminfo->pss_size = KB(gem_size/tgeminfo->hcount);
205         } else
206                 return NULL;
207
208         return tgeminfo;
209 }
210
211
212 static geminfo *load_geminfo(void)
213 {
214         geminfo *ginfo;
215         geminfo *gilist = NULL;
216         FILE *drm_fp;
217         char line[BUF_MAX];
218
219         drm_fp = fopen("/sys/kernel/debug/dri/0/gem_info", "r");
220
221         if (drm_fp == NULL) {
222                 fprintf(stderr,
223                 "cannot open /sys/kernel/debug/dri/0/gem_info\n");
224                 return NULL;
225         }
226
227         if (fgets(line, BUF_MAX, drm_fp) == NULL) {
228                 fclose(drm_fp);
229                 return NULL;
230         } else {
231                 /* we should count a number of whitespace separated fields */
232                 int      in_field = (line[0] && !isblank(line[0]));
233                 unsigned int size = (unsigned)in_field;
234                 const char*  ptr  = &line[1];
235
236                 /* sscanf() was used in original code, so number of fields */
237                 /* in string is expected to be at least NUM_GEM_FIELD      */
238                 while (*ptr && size < NUM_GEM_FIELD) {
239                         if (isblank(*ptr++)) {
240                                 if (in_field) {
241                                         /* end of field */
242                                         in_field = 0;
243                                 }
244                         } else {
245                                 if (!in_field) {
246                                         /* next field started */
247                                         in_field = 1;
248                                         size++;
249                                 }
250                         }
251                 } /* while */
252
253                 if (size != NUM_GEM_FIELD) {
254                         fclose(drm_fp);
255                         return NULL;
256                 }
257         }
258
259         while ((ginfo = read_geminfo(drm_fp)) != NULL) {
260                 if (gilist && ginfo->tgid == gilist->tgid) {
261                         gilist->pss_size += ginfo->pss_size;
262                         gilist->rss_size += ginfo->rss_size;
263                         free(ginfo);
264                         continue;
265                 }
266                 ginfo->next = gilist;
267                 gilist = ginfo;
268         }
269
270         fclose(drm_fp);
271
272         return gilist;
273 }
274
275
276 /* 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /android/lib/libcomposer.so
277  * 012345678901234567890123456789012345678901234567890123456789
278  * 0         1         2         3         4         5
279  */
280
281 mapinfo *read_mapinfo(char** smaps, int rest_line)
282 {
283         char* line;
284         mapinfo *mi;
285         int len;
286         int tmp;
287
288         if ((line = cgets(smaps)) == 0)
289                 return 0;
290
291         len    = strlen(line);
292         if (len < 1)
293                 return 0;
294
295         mi = malloc(sizeof(mapinfo) + len + 16);
296         if (mi == 0)
297                 return 0;
298
299         mi->start = strtoul(line, 0, 16);
300         mi->end = strtoul(line + 9, 0, 16);
301
302         mi->perm[0] = line[18]; /* read */
303         mi->perm[1] = line[19]; /* write */
304         mi->perm[2] = line[20]; /* execute */
305         mi->perm[3] = line[21]; /* may share or private */
306
307         if (len < 50)
308                 snprintf(mi->name, len+17, "%s", "[anon]");
309         else
310                 snprintf(mi->name, len+17, "%s", line + 49);
311
312         if ((line = cgets(smaps)) == 0)
313                 goto oops;
314         if (sscanf(line, "Size: %d kB", &mi->size) != 1)
315                 goto oops;
316         if ((line = cgets(smaps)) == 0)
317                 goto oops;
318         if (sscanf(line, "Rss: %d kB", &mi->rss) != 1)
319                 goto oops;
320         if ((line = cgets(smaps)) == 0)
321                 goto oops;
322         if (sscanf(line, "Pss: %d kB", &mi->pss) == 1)
323                 if ((line = cgets(smaps)) == 0)
324                         goto oops;
325         if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1)
326                 goto oops;
327         if ((line = cgets(smaps)) == 0)
328                 goto oops;
329         if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1)
330                 goto oops;
331         if ((line = cgets(smaps)) == 0)
332                 goto oops;
333         if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1)
334                 goto oops;
335         if ((line = cgets(smaps)) == 0)
336                 goto oops;
337         if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1)
338                 goto oops;
339
340         while (rest_line-- && (line = cgets(smaps))) {
341                 if (sscanf(line, "PSwap: %d kB", &tmp) == 1)
342                         rest_line++;
343         }
344
345         return mi;
346  oops:
347         printf("mi get error\n");
348         free(mi);
349         return 0;
350 }
351
352 static unsigned total_gem_memory(void)
353 {
354         FILE *gem_fp;
355         unsigned total_gem_mem = 0;
356         unsigned name, size, handles, refcount;
357         char line[BUF_MAX];
358
359         gem_fp = fopen("/proc/dri/0/gem_names", "r");
360         if(gem_fp == NULL) {
361                 fprintf(stderr,
362                 "cannot open /proc/dir/0/gem_names\n");
363                 return 0;
364         }
365
366         if (fgets(line, BUF_MAX, gem_fp) == NULL) {
367                 fclose(gem_fp);
368                 return 0;
369         }
370
371         while (fgets(line, BUF_MAX, gem_fp) != NULL)
372                 if (sscanf(line, "%d %d %d %d\n",
373                     &name, &size, &handles, &refcount) == 4)
374                         total_gem_mem += size;
375         fclose(gem_fp);
376
377         return total_gem_mem;
378 }
379
380 static void get_mem_info(FILE *output_fp)
381 {
382         char buf[PATH_MAX];
383         FILE *fp;
384         char *idx;
385         unsigned int free = 0, cached = 0;
386         unsigned int total_mem = 0, available = 0, used;
387         unsigned int swap_total = 0, swap_free = 0, swap_used;
388         unsigned int used_ratio;
389
390         if (output_fp == NULL)
391                 return;
392
393         fp = fopen("/proc/meminfo", "r");
394
395         if (!fp) {
396                 fprintf(stderr, "%s open failed, %p", buf, fp);
397                 return;
398         }
399
400         while (fgets(buf, PATH_MAX, fp) != NULL) {
401                 if ((idx = strstr(buf, "MemTotal:"))) {
402                         idx += strlen("Memtotal:");
403                         while (*idx < '0' || *idx > '9')
404                                 idx++;
405                         total_mem = atoi(idx);
406                 } else if ((idx = strstr(buf, "MemFree:"))) {
407                         idx += strlen("MemFree:");
408                         while (*idx < '0' || *idx > '9')
409                                 idx++;
410                         free = atoi(idx);
411                 } else if ((idx = strstr(buf, "MemAvailable:"))) {
412                         idx += strlen("MemAvailable:");
413                         while (*idx < '0' || *idx > '9')
414                                 idx++;
415                         available = atoi(idx);
416                 } else if((idx = strstr(buf, "Cached:")) && !strstr(buf, "Swap")) {
417                         idx += strlen("Cached:");
418                         while (*idx < '0' || *idx > '9')
419                                 idx++;
420                         cached = atoi(idx);
421                 } else if((idx = strstr(buf, "SwapTotal:"))) {
422                         idx += strlen("SwapTotal:");
423                         while (*idx < '0' || *idx > '9')
424                                 idx++;
425                         swap_total = atoi(idx);
426                 } else if((idx = strstr(buf, "SwapFree:"))) {
427                         idx += strlen("SwapFree");
428                         while (*idx < '0' || *idx > '9')
429                                 idx++;
430                         swap_free = atoi(idx);
431                         break;
432                 }
433         }
434
435         if (available == 0)
436                 available = free + cached;
437         used = total_mem - available;
438         used_ratio = used * 100 / total_mem;
439         swap_used = swap_total - swap_free;
440
441         fprintf(output_fp,
442                 "====================================================================\n");
443
444
445         fprintf(output_fp, "Total RAM size: \t%15d MB( %6d kB)\n",
446                         total_mem >> 10, total_mem);
447
448         fprintf(output_fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n",
449                         (total_mem - free) >> 10, total_mem - free);
450
451         fprintf(output_fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n",
452                         used >> 10, used);
453
454         fprintf(output_fp, "Used (Mem):  \t\t%15d MB( %6d kB)\n",
455                         used >> 10, used);
456
457         fprintf(output_fp, "Used (Swap): \t\t%15d MB( %6d kB)\n",
458                         swap_used >> 10, swap_used);
459
460         fprintf(output_fp, "Used Ratio: \t\t%15d  %%\n", used_ratio);
461
462         fprintf(output_fp, "Mem Free:\t\t%15d MB( %6d kB)\n",
463                         free >> 10, free);
464
465         fprintf(output_fp, "Available (Free+Reclaimable):%10d MB( %6d kB)\n",
466                         available >> 10,
467                         available);
468         fclose(fp);
469 }
470
471 static int get_tmpfs_info(FILE *output_fp)
472 {
473         FILE *fp;
474         char line[BUF_MAX];
475         char tmpfs_mp[NAME_MAX];        /* tmpfs mount point */
476         struct statfs tmpfs_info;
477
478         if (output_fp == NULL)
479                 return -1;
480
481         fp = fopen("/etc/mtab", "r");
482         if (fp == NULL)
483                 return -1;
484
485         fprintf(output_fp,
486                 "====================================================================\n");
487         fprintf(output_fp, "TMPFS INFO\n");
488
489         while (fgets(line, BUF_MAX, fp) != NULL) {
490                 if (sscanf(line, "tmpfs %s tmpfs", tmpfs_mp) == 1) {
491                         statfs(tmpfs_mp, &tmpfs_info);
492                         fprintf(output_fp,
493                                 "tmpfs %16s  Total %8ld KB, Used %8ld, Avail %8ld\n",
494                                 tmpfs_mp,
495                                 /* 1 block is 4 KB */
496                                 tmpfs_info.f_blocks * 4,
497                                 (tmpfs_info.f_blocks - tmpfs_info.f_bfree) * 4,
498                                 tmpfs_info.f_bfree * 4);
499                 }
500         }
501         fclose(fp);
502         return 0;
503 }
504
505 mapinfo *load_maps(int pid)
506 {
507         char* smaps;
508         char tmp[128];
509         mapinfo *milist = 0;
510         mapinfo *mi;
511
512         snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
513         smaps = cread(tmp);
514         if (smaps == NULL)
515                 return 0;
516
517         while ((mi = read_mapinfo(&smaps, ignore_smaps_field)) != 0) {
518                 if (milist) {
519                         if ((!strcmp(mi->name, milist->name)
520                              && (mi->name[0] != '['))) {
521                                 milist->size += mi->size;
522                                 milist->rss += mi->rss;
523                                 milist->pss += mi->pss;
524                                 milist->shared_clean += mi->shared_clean;
525                                 milist->shared_dirty += mi->shared_dirty;
526                                 milist->private_clean += mi->private_clean;
527                                 milist->private_dirty += mi->private_dirty;
528
529                                 milist->perm[0] = mi->perm[0];
530                                 milist->perm[1] = mi->perm[1];
531                                 milist->perm[2] = mi->perm[2];
532                                 milist->perm[3] = mi->perm[3];
533                                 milist->end = mi->end;
534                                 strncpy(milist->perm, mi->perm, 4);
535                                 free(mi);
536                                 continue;
537                         }
538                 }
539                 mi->next = milist;
540                 milist = mi;
541         }
542
543         return milist;
544 }
545
546 static geminfo *find_geminfo(unsigned int tgid, geminfo *gilist)
547 {
548         geminfo *gi;
549         for (gi = gilist; gi; ) {
550                 if (gi->tgid == tgid)
551                         return gi;
552
553                 gi = gi->next;
554         }
555         return NULL;
556 }
557
558 static void init_trib_mapinfo(trib_mapinfo *tmi)
559 {
560         if (!tmi)
561                 return;
562         tmi->shared_clean = 0;
563         tmi->shared_dirty = 0;
564         tmi->private_clean = 0;
565         tmi->private_dirty = 0;
566         tmi->shared_clean_pss = 0;
567         tmi->shared_dirty_pss = 0;
568         tmi->rss = 0;
569         tmi->pss = 0;
570         tmi->size = 0;
571         tmi->graphic_3d = 0;
572         tmi->gem_rss = 0;
573         tmi->gem_pss = 0;
574         tmi->peak_rss = 0;
575         tmi->other_devices = 0;
576         tmi->gem_mmap = 0;
577 }
578
579 static int
580 get_trib_mapinfo(unsigned int tgid, mapinfo *milist,
581                  geminfo *gilist, trib_mapinfo *result)
582
583 {
584         mapinfo *mi;
585         mapinfo *temp = NULL;
586         geminfo *gi;
587
588         if (!result)
589                 return -EINVAL;
590
591         init_trib_mapinfo(result);
592         for (mi = milist; mi;) {
593                 if (strstr(mi->name, STR_SGX_PATH)) {
594                         result->graphic_3d += mi->pss;
595                 } else if (strstr(mi->name, STR_3D_PATH1) ||
596                         strstr(mi->name, STR_3D_PATH2)) {
597                         result->graphic_3d += mi->size;
598                 } else if (mi->rss != 0 && mi->pss == 0
599                            && mi->shared_clean == 0
600                            && mi->shared_dirty == 0
601                            && mi->private_clean == 0
602                            && mi->private_dirty == 0) {
603                         result->other_devices += mi->size;
604                 } else if (!strncmp(mi->name, STR_DRM_PATH1,
605                                 sizeof(STR_DRM_PATH1)) ||
606                                 !strncmp(mi->name, STR_DRM_PATH2,
607                                 sizeof(STR_DRM_PATH2))) {
608                         result->gem_mmap += mi->rss;
609                 } else {
610                         result->shared_clean += mi->shared_clean;
611                         result->shared_dirty += mi->shared_dirty;
612                         result->private_clean += mi->private_clean;
613                         result->private_dirty += mi->private_dirty;
614                         result->rss += mi->rss;
615                         result->pss += mi->pss;
616                         result->size += mi->size;
617
618                         if(mi->shared_clean != 0)
619                                 result->shared_clean_pss += mi->pss;
620                         else if (mi->shared_dirty != 0)
621                                 result->shared_dirty_pss += mi->pss;
622                 }
623
624                 temp = mi;
625                 mi = mi->next;
626                 free(temp);
627                 temp = NULL;
628         }
629
630         result->peak_rss = get_peak_rss(tgid);
631         if (result->peak_rss < result->rss)
632                 result->peak_rss = result->rss;
633         if (result->gem_mmap > 0)
634                 result->peak_rss -= result->gem_mmap;
635
636         gi = find_geminfo(tgid, gilist);
637         if (gi != NULL) {
638                 result->gem_rss = gi->rss_size;
639                 result->gem_pss = gi->pss_size;
640         }
641
642         return 0;
643 }
644
645 static int get_cmdline(unsigned int pid, char *cmdline)
646 {
647         FILE *fp;
648         char buf[NAME_MAX] = {0, };
649         int ret = -1;
650
651         snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
652         fp = fopen(buf, "r");
653         if (fp == 0) {
654                 fprintf(stderr, "cannot file open %s\n", buf);
655                 return ret;
656         }
657         if ((ret = fscanf(fp, "%s", cmdline)) < 1) {
658                 fclose(fp);
659                 return ret;
660         }
661         fclose(fp);
662
663         return ret;
664 }
665
666 static int get_oomscoreadj(unsigned int pid)
667 {
668         FILE *fp;
669         char tmp[256];
670         int oomadj_val;
671
672         snprintf(tmp, sizeof(tmp), "/proc/%d/oom_score_adj", pid);
673         fp = fopen(tmp, "r");
674
675         if (fp == NULL) {
676                 oomadj_val = -50;
677                 return oomadj_val;
678         }
679         if (fgets(tmp, sizeof(tmp), fp) == NULL) {
680                 oomadj_val = -100;
681                 fclose(fp);
682                 return oomadj_val;
683         }
684
685         oomadj_val = atoi(tmp);
686
687         fclose(fp);
688         return oomadj_val;
689 }
690
691 static void get_rss(pid_t pid, unsigned int *result)
692 {
693         FILE *fp;
694         char proc_path[PATH_MAX];
695         int rss = 0;
696
697         *result = 0;
698
699         snprintf(proc_path, sizeof(proc_path), "/proc/%d/statm", pid);
700         fp = fopen(proc_path, "r");
701         if (fp == NULL)
702                 return;
703
704         if (fscanf(fp, "%*s %d", &rss) < 1) {
705                 fclose(fp);
706                 return;
707         }
708
709         fclose(fp);
710
711         /* convert page to Kb */
712         *result = rss * 4;
713         return;
714 }
715
716 static void show_rss(int output_type, char *output_path)
717 {
718         DIR *pDir = NULL;
719         struct dirent curdir;
720         struct dirent *result;
721         pid_t pid;
722         char cmdline[PATH_MAX];
723         FILE *output_file = NULL;
724         int oom_score_adj;
725         unsigned int rss;
726         int ret;
727
728         pDir = opendir("/proc");
729         if (pDir == NULL) {
730                 fprintf(stderr, "cannot read directory /proc.\n");
731                 return;
732         }
733
734         if (output_type == OUTPUT_FILE && output_path) {
735                 output_file = fopen(output_path, "w+");
736                 if (!output_file) {
737                         fprintf(stderr, "cannot open output file(%s)\n",
738                                 output_path);
739                         closedir(pDir);
740                         exit(1);
741                 }
742         } else
743                 output_file = stdout;
744
745
746         fprintf(output_file,
747                         "     PID    RSS    OOM_SCORE    COMMAND\n");
748
749         while (!(ret = readdir_r(pDir, &curdir, &result)) && result != NULL) {
750                 pid = atoi(curdir.d_name);
751                 if (pid < 1 || pid > 32768 || pid == getpid())
752                         continue;
753
754                 if (get_cmdline(pid, cmdline) < 0)
755                         continue;
756                 get_rss(pid, &rss);
757                 oom_score_adj = get_oomscoreadj(pid);
758
759                 fprintf(output_file,
760                                 "%8d %8u %8d          %s\n",
761                                 pid,
762                                 rss,
763                                 oom_score_adj,
764                                 cmdline);
765         } /* end of while */
766
767         get_tmpfs_info(output_file);
768         get_mem_info(output_file);
769
770         fclose(output_file);
771         closedir(pDir);
772
773         return;
774 }
775
776 static int show_map_all_new(int output_type, char *output_path)
777 {
778         DIR *pDir = NULL;
779         struct dirent curdir;
780         struct dirent *result;
781         unsigned int pid;
782         mapinfo *milist;
783         geminfo *glist;
784         unsigned total_pss = 0;
785         unsigned total_private = 0;
786         unsigned total_private_code = 0;
787         unsigned total_private_data = 0;
788         unsigned total_shared_code = 0;
789         unsigned total_shared_data = 0;
790         unsigned total_shared_code_pss = 0;
791         unsigned total_shared_data_pss = 0;
792         unsigned total_rss = 0;
793         unsigned total_graphic_3d = 0;
794         unsigned total_gem_rss = 0;
795         unsigned total_gem_pss = 0;
796         unsigned total_peak_rss = 0;
797         unsigned total_allocated_gem = 0;
798         trib_mapinfo tmi;
799         char cmdline[PATH_MAX];
800         FILE *output_file = NULL;
801         int oom_score_adj;
802         int ret;
803
804         pDir = opendir("/proc");
805         if (pDir == NULL) {
806                 fprintf(stderr, "cannot read directory /proc.\n");
807                 return 0;
808         }
809
810         if (output_type == OUTPUT_FILE && output_path) {
811                 output_file = fopen(output_path, "w+");
812                 if (!output_file) {
813                         fprintf(stderr, "cannot open output file(%s)\n",
814                                 output_path);
815                         closedir(pDir);
816                         exit(1);
817                 }
818         } else
819                 output_file = stdout;
820
821         glist = load_geminfo();
822
823         if (!sum) {
824                 if (verbos)
825                         fprintf(output_file,
826                                         "     PID  S(CODE)  S(DATA)  P(CODE)  P(DATA)"
827                                         "     PEAK      PSS       3D"
828                                         "     GEM(PSS)  GEM(RSS)"
829                                         " OOM_SCORE_ADJ    COMMAND\n");
830                 else
831                         fprintf(output_file,
832                                         "     PID     CODE     DATA     PEAK     PSS"
833                                         "     3D      GEM(PSS)      COMMAND\n");
834         }
835
836         while (!(ret = readdir_r(pDir, &curdir, &result)) && result != NULL) {
837                 pid = atoi(curdir.d_name);
838                 if (pid < 1 || pid > 32768 || pid == getpid())
839                         continue;
840
841                 if (get_cmdline(pid, cmdline) < 0)
842                         continue;
843
844                 milist = load_maps(pid);
845                 if (milist == 0)
846                         continue;
847
848                 /* get classified map info */
849                 get_trib_mapinfo(pid, milist, glist, &tmi);
850                 oom_score_adj = get_oomscoreadj(pid);
851
852                 if (!sum) {
853                         if (verbos)
854                                 fprintf(output_file,
855                                         "%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d"
856                                         " %8d \t\t%s\n",
857                                         pid,
858                                         tmi.shared_clean, tmi.shared_dirty,
859                                         tmi.private_clean, tmi.private_dirty,
860                                         tmi.peak_rss, tmi.pss, tmi.graphic_3d,
861                                         tmi.gem_pss, tmi.gem_rss, oom_score_adj, cmdline);
862                         else
863                                 fprintf(output_file,
864                                         "%8d %8d %8d %8d %8d %8d %8d      %s\n",
865                                         pid,
866                                         tmi.shared_clean +
867                                         tmi.private_clean,
868                                         tmi.shared_dirty + tmi.private_dirty,
869                                         tmi.peak_rss,
870                                         tmi.pss,
871                                         tmi.graphic_3d,
872                                         tmi.gem_pss, cmdline);
873
874                         if (tmi.other_devices != 0)
875                                 fprintf(output_file,
876                                         "%s(%d) %d KB may mapped by device(s).\n",
877                                         cmdline, pid, tmi.other_devices);
878                 }
879
880                 total_private += (tmi.private_clean + tmi.private_dirty);
881                 total_pss += tmi.pss;
882                 total_rss += tmi.rss;
883                 total_graphic_3d += tmi.graphic_3d;
884                 total_gem_rss += tmi.gem_rss;
885                 total_gem_pss += tmi.gem_pss;
886                 total_private_code += tmi.private_clean;
887                 total_private_data += tmi.private_dirty;
888                 total_shared_code += tmi.shared_clean;
889                 total_shared_data += tmi.shared_dirty;
890                 total_peak_rss += tmi.peak_rss;
891
892                 total_shared_code_pss += tmi.shared_clean_pss;
893                 total_shared_data_pss += tmi.shared_dirty_pss;
894
895         } /* end of while */
896
897         total_allocated_gem = KB(total_gem_memory());
898         fprintf(output_file,
899                         "==============================================="
900                         "===============================================\n");
901         if (verbos) {
902                 fprintf(output_file,
903                                 "TOTAL:      S(CODE) S(DATA) P(CODE)  P(DATA)"
904                                 "    PEAK     PSS       3D    "
905                                 "GEM(PSS) GEM(RSS) GEM(ALLOC) TOTAL(KB)\n");
906                 fprintf(output_file,
907                         "         %8d %8d %8d %8d %8d %8d %8d"
908                         " %8d %8d %8d %8d\n",
909                         total_shared_code, total_shared_data,
910                         total_private_code, total_private_data,
911                         total_peak_rss, total_pss, total_graphic_3d,
912                         total_gem_pss, total_gem_rss,
913                         total_allocated_gem,
914                         total_pss + total_graphic_3d +
915                         total_allocated_gem);
916         } else {
917                 fprintf(output_file,
918                         "TOTAL:        CODE     DATA    PEAK     PSS     "
919                         "3D    GEM(PSS) GEM(ALLOC)     TOTAL(KB)\n");
920                 fprintf(output_file, "         %8d %8d %8d %8d %8d %8d %7d %8d\n",
921                         total_shared_code + total_private_code,
922                         total_shared_data + total_private_data,
923                         total_peak_rss, total_pss,
924                         total_graphic_3d, total_gem_pss,
925                         total_allocated_gem,
926                         total_pss + total_graphic_3d +
927                         total_allocated_gem);
928
929         }
930
931         if (verbos)
932                 fprintf(output_file,
933                         "* S(CODE): shared clean memory, it includes"
934                         " duplicated memory\n"
935                         "* S(DATA): shared dirty memory, it includes"
936                         " duplicated memory\n"
937                         "* P(CODE): private clean memory\n"
938                         "* P(DATA): private dirty memory\n"
939                         "* PEAK: peak memory usage of S(CODE) + S(DATA)"
940                         " + P(CODE) + P(DATA)\n"
941                         "* PSS: Proportional Set Size\n"
942                         "* 3D: memory allocated by GPU driver\n"
943                         "* GEM(PSS): GEM memory devided by # of sharers\n"
944                         "* GEM(RSS): GEM memory including duplicated memory\n"
945                         "* GEM(ALLOC): sum of unique gem memory in the system\n"
946                         "* TOTAL: PSS + 3D + GEM(ALLOC) \n");
947         else
948                 fprintf(output_file,
949                         "* CODE: shared and private clean memory\n"
950                         "* DATA: shared and private dirty memory\n"
951                         "* PEAK: peak memory usage of CODE + DATA\n"
952                         "* PSS: Proportional Set Size\n"
953                         "* 3D: memory allocated by GPU driver\n"
954                         "* GEM(PSS): GEM memory deviced by # of sharers\n"
955                         "* GEM(ALLOC): sum of unique GEM memory in the system\n"
956                         "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
957
958         get_tmpfs_info(output_file);
959         get_mem_info(output_file);
960
961         fclose(output_file);
962         free(glist);
963         closedir(pDir);
964         return 1;
965 }
966
967 static int show_map_new(int pid)
968 {
969         mapinfo *milist;
970         mapinfo *mi;
971         unsigned shared_dirty = 0;
972         unsigned shared_clean = 0;
973         unsigned private_dirty = 0;
974         unsigned private_clean = 0;
975         unsigned pss = 0;
976         unsigned start = 0;
977         unsigned end = 0;
978         unsigned private_clean_total = 0;
979         unsigned private_dirty_total = 0;
980         unsigned shared_clean_total = 0;
981         unsigned shared_dirty_total = 0;
982         int duplication = 0;
983
984         milist = load_maps(pid);
985
986         if (milist == 0) {
987                 fprintf(stderr, "cannot get /proc/smaps for pid %d\n", pid);
988                 return 1;
989         }
990
991         if (!sum) {
992                 printf(" S(CODE)  S(DATA)  P(CODE)  P(DATA)  ADDR(start-end)"
993                         "OBJECT NAME\n");
994                 printf("-------- -------- -------- -------- -----------------"
995                         "------------------------------\n");
996         } else {
997                 printf(" S(CODE)  S(DATA)  P(CODE)  P(DATA)  PSS\n");
998                 printf("-------- -------- -------------------"
999                         "------------------\n");
1000         }
1001         for (mi = milist; mi; mi = mi->next) {
1002                 shared_clean += mi->shared_clean;
1003                 shared_dirty += mi->shared_dirty;
1004                 private_clean += mi->private_clean;
1005                 private_dirty += mi->private_dirty;
1006                 pss += mi->pss;
1007
1008                 shared_clean_total += mi->shared_clean;
1009                 shared_dirty_total += mi->shared_dirty;
1010                 private_clean_total += mi->private_clean;
1011                 private_dirty_total += mi->private_dirty;
1012
1013                 if (!duplication)
1014                         start = mi->start;
1015
1016                 if ((mi->next && !strcmp(mi->next->name, mi->name)) &&
1017                     (mi->next->start == mi->end)) {
1018                         duplication = 1;
1019                         continue;
1020                 }
1021                 end = mi->end;
1022                 duplication = 0;
1023
1024                 if (!sum) {
1025                         printf("%8d %8d %8d %8d %08x-%08x %s\n",
1026                                shared_clean, shared_dirty, private_clean, private_dirty,
1027                                start, end, mi->name);
1028                 }
1029                 shared_clean = 0;
1030                 shared_dirty = 0;
1031                 private_clean = 0;
1032                 private_dirty = 0;
1033         }
1034         if (sum) {
1035                 printf("%8d %8d %8d %8d %18d\n",
1036                        shared_clean_total,
1037                        shared_dirty_total,
1038                        private_clean_total,
1039                        private_dirty_total,
1040                        pss);
1041         }
1042
1043         return 1;
1044 }
1045
1046 void check_kernel_version(void)
1047 {
1048         struct utsname buf;
1049         int ret;
1050
1051         ret = uname(&buf);
1052
1053         if (!ret) {
1054                 if (buf.release[0] == '3') {
1055                         char *pch;
1056                         char str[3];
1057                         int sub_version;
1058                         pch = strstr(buf.release, ".");
1059                         strncpy(str, pch+1, 2);
1060                         sub_version = atoi(str);
1061
1062                         if (sub_version >= 10)
1063                                 ignore_smaps_field = 8; /* Referenced, Anonymous, AnonHugePages,
1064                                                    Swap, KernelPageSize, MMUPageSize,
1065                                                    Locked, VmFlags */
1066
1067                         else
1068                                 ignore_smaps_field = 7; /* Referenced, Anonymous, AnonHugePages,
1069                                                    Swap, KernelPageSize, MMUPageSize,
1070                                                    Locked */
1071                 } else {
1072                         ignore_smaps_field = 4; /* Referenced, Swap, KernelPageSize,
1073                                                    MMUPageSize */
1074                 }
1075         }
1076 }
1077
1078 int main(int argc, char *argv[])
1079 {
1080         int usage = 1;
1081         sum = 0;
1082
1083         if (argc > 1) {
1084                 check_kernel_version();
1085
1086                 if (!strcmp(argv[1], "-r")) {
1087                         if (argc >= 3)
1088                                 show_rss(OUTPUT_FILE, argv[2]);
1089                         else
1090                                 show_rss(OUTPUT_UART, NULL);
1091                         usage = 0;
1092                 } else if (!strcmp(argv[1], "-s")) {
1093                         sum = 1;
1094                         if (argc == 3 && atoi(argv[2]) > 0) {
1095                                 show_map_new(atoi(argv[2]));
1096                                 usage = 0;
1097                         }
1098                 } else if (!strcmp(argv[1], "-a")) {
1099                         verbos = 0;
1100                         show_map_all_new(OUTPUT_UART, NULL);
1101                         usage = 0;
1102                 } else if (!strcmp(argv[1], "-v")) {
1103                         verbos = 1;
1104                         show_map_all_new(OUTPUT_UART, NULL);
1105                         usage = 0;
1106                 } else if (!strcmp(argv[1], "-f")) {
1107                         if (argc >= 3) {
1108                                 verbos = 1;
1109                                 show_map_all_new(OUTPUT_FILE, argv[2]);
1110                                 usage = 0;
1111                         }
1112                 } else if (argc == 2 && atoi(argv[1]) > 0) {
1113                         show_map_new(atoi(argv[1]));
1114                         usage = 0;
1115                 }
1116         }
1117         if (usage) {
1118                 fprintf(stderr,
1119                         "memps [-a] | [-v] | [-s] <pid> | [-f] <output file full path>\n"
1120                         "        -s = sum (show only sum of each)\n"
1121                         "        -f = all (show all processes via output file)\n"
1122                         "        -a = all (show all processes)\n"
1123                         "        -v = verbos (show all processes in detail)\n");
1124         }
1125
1126         return 0;
1127 }