tizen 2.3 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         sprintf(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                 strcpy(mi->name, "[anon]");
309         else
310                 strcpy(mi->name, 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         sprintf(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         sprintf(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         sprintf(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         sprintf(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         pid_t pid;
721         char cmdline[PATH_MAX];
722         FILE *output_file = NULL;
723         int oom_score_adj;
724         unsigned int rss;
725
726         pDir = opendir("/proc");
727         if (pDir == NULL) {
728                 fprintf(stderr, "cannot read directory /proc.\n");
729                 return;
730         }
731
732         if (output_type == OUTPUT_FILE && output_path) {
733                 output_file = fopen(output_path, "w+");
734                 if (!output_file) {
735                         fprintf(stderr, "cannot open output file(%s)\n",
736                                 output_path);
737                         closedir(pDir);
738                         exit(1);
739                 }
740         } else
741                 output_file = stdout;
742
743
744         fprintf(output_file,
745                         "     PID    RSS    OOM_SCORE    COMMAND\n");
746
747         while ((curdir = readdir(pDir)) != NULL) {
748                 pid = atoi(curdir->d_name);
749                 if (pid < 1 || pid > 32768 || pid == getpid())
750                         continue;
751
752                 if (get_cmdline(pid, cmdline) < 0)
753                         continue;
754                 get_rss(pid, &rss);
755                 oom_score_adj = get_oomscoreadj(pid);
756
757                 fprintf(output_file,
758                                 "%8d %8u %8d          %s\n",
759                                 pid,
760                                 rss,
761                                 oom_score_adj,
762                                 cmdline);
763
764
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         unsigned int pid;
781         mapinfo *milist;
782         geminfo *glist;
783         unsigned total_pss = 0;
784         unsigned total_private = 0;
785         unsigned total_private_code = 0;
786         unsigned total_private_data = 0;
787         unsigned total_shared_code = 0;
788         unsigned total_shared_data = 0;
789         unsigned total_shared_code_pss = 0;
790         unsigned total_shared_data_pss = 0;
791         unsigned total_rss = 0;
792         unsigned total_graphic_3d = 0;
793         unsigned total_gem_rss = 0;
794         unsigned total_gem_pss = 0;
795         unsigned total_peak_rss = 0;
796         unsigned total_allocated_gem = 0;
797         trib_mapinfo tmi;
798         char cmdline[PATH_MAX];
799         FILE *output_file = NULL;
800         int oom_score_adj;
801
802         pDir = opendir("/proc");
803         if (pDir == NULL) {
804                 fprintf(stderr, "cannot read directory /proc.\n");
805                 return 0;
806         }
807
808         if (output_type == OUTPUT_FILE && output_path) {
809                 output_file = fopen(output_path, "w+");
810                 if (!output_file) {
811                         fprintf(stderr, "cannot open output file(%s)\n",
812                                 output_path);
813                         closedir(pDir);
814                         exit(1);
815                 }
816         } else
817                 output_file = stdout;
818
819         glist = load_geminfo();
820
821         if (!sum) {
822                 if (verbos)
823                         fprintf(output_file,
824                                         "     PID  S(CODE)  S(DATA)  P(CODE)  P(DATA)"
825                                         "     PEAK      PSS       3D"
826                                         "     GEM(PSS)  GEM(RSS)"
827                                         " OOM_SCORE_ADJ    COMMAND\n");
828                 else
829                         fprintf(output_file,
830                                         "     PID     CODE     DATA     PEAK     PSS"
831                                         "     3D      GEM(PSS)      COMMAND\n");
832         }
833
834         while ((curdir = readdir(pDir)) != NULL) {
835                 pid = atoi(curdir->d_name);
836                 if (pid < 1 || pid > 32768 || pid == getpid())
837                         continue;
838
839                 if (get_cmdline(pid, cmdline) < 0)
840                         continue;
841
842                 milist = load_maps(pid);
843                 if (milist == 0)
844                         continue;
845
846                 /* get classified map info */
847                 get_trib_mapinfo(pid, milist, glist, &tmi);
848                 oom_score_adj = get_oomscoreadj(pid);
849
850                 if (!sum) {
851                         if (verbos)
852                                 fprintf(output_file,
853                                         "%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d"
854                                         " %8d \t\t%s\n",
855                                         pid,
856                                         tmi.shared_clean, tmi.shared_dirty,
857                                         tmi.private_clean, tmi.private_dirty,
858                                         tmi.peak_rss, tmi.pss, tmi.graphic_3d,
859                                         tmi.gem_pss, tmi.gem_rss, oom_score_adj, cmdline);
860                         else
861                                 fprintf(output_file,
862                                         "%8d %8d %8d %8d %8d %8d %8d      %s\n",
863                                         pid,
864                                         tmi.shared_clean +
865                                         tmi.private_clean,
866                                         tmi.shared_dirty + tmi.private_dirty,
867                                         tmi.peak_rss,
868                                         tmi.pss,
869                                         tmi.graphic_3d,
870                                         tmi.gem_pss, cmdline);
871
872                         if (tmi.other_devices != 0)
873                                 fprintf(output_file,
874                                         "%s(%d) %d KB may mapped by device(s).\n",
875                                         cmdline, pid, tmi.other_devices);
876                 }
877
878                 total_private += (tmi.private_clean + tmi.private_dirty);
879                 total_pss += tmi.pss;
880                 total_rss += tmi.rss;
881                 total_graphic_3d += tmi.graphic_3d;
882                 total_gem_rss += tmi.gem_rss;
883                 total_gem_pss += tmi.gem_pss;
884                 total_private_code += tmi.private_clean;
885                 total_private_data += tmi.private_dirty;
886                 total_shared_code += tmi.shared_clean;
887                 total_shared_data += tmi.shared_dirty;
888                 total_peak_rss += tmi.peak_rss;
889
890                 total_shared_code_pss += tmi.shared_clean_pss;
891                 total_shared_data_pss += tmi.shared_dirty_pss;
892         } /* end of while */
893
894         total_allocated_gem = KB(total_gem_memory());
895         fprintf(output_file,
896                         "==============================================="
897                         "===============================================\n");
898         if (verbos) {
899                 fprintf(output_file,
900                                 "TOTAL:      S(CODE) S(DATA) P(CODE)  P(DATA)"
901                                 "    PEAK     PSS       3D    "
902                                 "GEM(PSS) GEM(RSS) GEM(ALLOC) TOTAL(KB)\n");
903                 fprintf(output_file,
904                         "         %8d %8d %8d %8d %8d %8d %8d"
905                         " %8d %8d %8d %8d\n",
906                         total_shared_code, total_shared_data,
907                         total_private_code, total_private_data,
908                         total_peak_rss, total_pss, total_graphic_3d,
909                         total_gem_pss, total_gem_rss,
910                         total_allocated_gem,
911                         total_pss + total_graphic_3d +
912                         total_allocated_gem);
913         } else {
914                 fprintf(output_file,
915                         "TOTAL:        CODE     DATA    PEAK     PSS     "
916                         "3D    GEM(PSS) GEM(ALLOC)     TOTAL(KB)\n");
917                 fprintf(output_file, "         %8d %8d %8d %8d %8d %8d %7d %8d\n",
918                         total_shared_code + total_private_code,
919                         total_shared_data + total_private_data,
920                         total_peak_rss, total_pss,
921                         total_graphic_3d, total_gem_pss,
922                         total_allocated_gem,
923                         total_pss + total_graphic_3d +
924                         total_allocated_gem);
925
926         }
927
928         if (verbos)
929                 fprintf(output_file,
930                         "* S(CODE): shared clean memory, it includes"
931                         " duplicated memory\n"
932                         "* S(DATA): shared dirty memory, it includes"
933                         " duplicated memory\n"
934                         "* P(CODE): private clean memory\n"
935                         "* P(DATA): private dirty memory\n"
936                         "* PEAK: peak memory usage of S(CODE) + S(DATA)"
937                         " + P(CODE) + P(DATA)\n"
938                         "* PSS: Proportional Set Size\n"
939                         "* 3D: memory allocated by GPU driver\n"
940                         "* GEM(PSS): GEM memory devided by # of sharers\n"
941                         "* GEM(RSS): GEM memory including duplicated memory\n"
942                         "* GEM(ALLOC): sum of unique gem memory in the system\n"
943                         "* TOTAL: PSS + 3D + GEM(ALLOC) \n");
944         else
945                 fprintf(output_file,
946                         "* CODE: shared and private clean memory\n"
947                         "* DATA: shared and private dirty memory\n"
948                         "* PEAK: peak memory usage of CODE + DATA\n"
949                         "* PSS: Proportional Set Size\n"
950                         "* 3D: memory allocated by GPU driver\n"
951                         "* GEM(PSS): GEM memory deviced by # of sharers\n"
952                         "* GEM(ALLOC): sum of unique GEM memory in the system\n"
953                         "* TOTAL: PSS + 3D + GEM(ALLOC)\n");
954
955         get_tmpfs_info(output_file);
956         get_mem_info(output_file);
957
958         fclose(output_file);
959         free(glist);
960         closedir(pDir);
961         return 1;
962 }
963
964 static int show_map_new(int pid)
965 {
966         mapinfo *milist;
967         mapinfo *mi;
968         unsigned shared_dirty = 0;
969         unsigned shared_clean = 0;
970         unsigned private_dirty = 0;
971         unsigned private_clean = 0;
972         unsigned pss = 0;
973         unsigned start = 0;
974         unsigned end = 0;
975         unsigned private_clean_total = 0;
976         unsigned private_dirty_total = 0;
977         unsigned shared_clean_total = 0;
978         unsigned shared_dirty_total = 0;
979         int duplication = 0;
980
981         milist = load_maps(pid);
982
983         if (milist == 0) {
984                 fprintf(stderr, "cannot get /proc/smaps for pid %d\n", pid);
985                 return 1;
986         }
987
988         if (!sum) {
989                 printf(" S(CODE)  S(DATA)  P(CODE)  P(DATA)  ADDR(start-end)"
990                         "OBJECT NAME\n");
991                 printf("-------- -------- -------- -------- -----------------"
992                         "------------------------------\n");
993         } else {
994                 printf(" S(CODE)  S(DATA)  P(CODE)  P(DATA)  PSS\n");
995                 printf("-------- -------- -------------------"
996                         "------------------\n");
997         }
998         for (mi = milist; mi; mi = mi->next) {
999                 shared_clean += mi->shared_clean;
1000                 shared_dirty += mi->shared_dirty;
1001                 private_clean += mi->private_clean;
1002                 private_dirty += mi->private_dirty;
1003                 pss += mi->pss;
1004
1005                 shared_clean_total += mi->shared_clean;
1006                 shared_dirty_total += mi->shared_dirty;
1007                 private_clean_total += mi->private_clean;
1008                 private_dirty_total += mi->private_dirty;
1009
1010                 if (!duplication)
1011                         start = mi->start;
1012
1013                 if ((mi->next && !strcmp(mi->next->name, mi->name)) &&
1014                     (mi->next->start == mi->end)) {
1015                         duplication = 1;
1016                         continue;
1017                 }
1018                 end = mi->end;
1019                 duplication = 0;
1020
1021                 if (!sum) {
1022                         printf("%8d %8d %8d %8d %08x-%08x %s\n",
1023                                shared_clean, shared_dirty, private_clean, private_dirty,
1024                                start, end, mi->name);
1025                 }
1026                 shared_clean = 0;
1027                 shared_dirty = 0;
1028                 private_clean = 0;
1029                 private_dirty = 0;
1030         }
1031         if (sum) {
1032                 printf("%8d %8d %8d %8d %18d\n",
1033                        shared_clean_total,
1034                        shared_dirty_total,
1035                        private_clean_total,
1036                        private_dirty_total,
1037                        pss);
1038         }
1039
1040         return 1;
1041 }
1042
1043 void check_kernel_version(void)
1044 {
1045         struct utsname buf;
1046         int ret;
1047
1048         ret = uname(&buf);
1049
1050         if (!ret) {
1051                 if (buf.release[0] == '3') {
1052                         char *pch;
1053                         char str[3];
1054                         int sub_version;
1055                         pch = strstr(buf.release, ".");
1056                         strncpy(str, pch+1, 2);
1057                         sub_version = atoi(str);
1058
1059                         if (sub_version >= 10)
1060                                 ignore_smaps_field = 8; /* Referenced, Anonymous, AnonHugePages,
1061                                                    Swap, KernelPageSize, MMUPageSize,
1062                                                    Locked, VmFlags */
1063
1064                         else
1065                                 ignore_smaps_field = 7; /* Referenced, Anonymous, AnonHugePages,
1066                                                    Swap, KernelPageSize, MMUPageSize,
1067                                                    Locked */
1068                 } else {
1069                         ignore_smaps_field = 4; /* Referenced, Swap, KernelPageSize,
1070                                                    MMUPageSize */
1071                 }
1072         }
1073 }
1074
1075 int main(int argc, char *argv[])
1076 {
1077         int usage = 1;
1078         sum = 0;
1079
1080         if (argc > 1) {
1081                 check_kernel_version();
1082
1083                 if (!strcmp(argv[1], "-r")) {
1084                         if (argc >= 3)
1085                                 show_rss(OUTPUT_FILE, argv[2]);
1086                         else
1087                                 show_rss(OUTPUT_UART, NULL);
1088                         usage = 0;
1089                 } else if (!strcmp(argv[1], "-s")) {
1090                         sum = 1;
1091                         if (argc == 3 && atoi(argv[2]) > 0) {
1092                                 show_map_new(atoi(argv[2]));
1093                                 usage = 0;
1094                         }
1095                 } else if (!strcmp(argv[1], "-a")) {
1096                         verbos = 0;
1097                         show_map_all_new(OUTPUT_UART, NULL);
1098                         usage = 0;
1099                 } else if (!strcmp(argv[1], "-v")) {
1100                         verbos = 1;
1101                         show_map_all_new(OUTPUT_UART, NULL);
1102                         usage = 0;
1103                 } else if (!strcmp(argv[1], "-f")) {
1104                         if (argc >= 3) {
1105                                 verbos = 1;
1106                                 show_map_all_new(OUTPUT_FILE, argv[2]);
1107                                 usage = 0;
1108                         }
1109                 } else if (argc == 2 && atoi(argv[1]) > 0) {
1110                         show_map_new(atoi(argv[1]));
1111                         usage = 0;
1112                 }
1113         }
1114         if (usage) {
1115                 fprintf(stderr,
1116                         "memps [-a] | [-v] | [-s] <pid> | [-f] <output file full path>\n"
1117                         "        -s = sum (show only sum of each)\n"
1118                         "        -f = all (show all processes via output file)\n"
1119                         "        -a = all (show all processes)\n"
1120                         "        -v = verbos (show all processes in detail)\n");
1121         }
1122
1123         return 0;
1124 }