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