Commits

Anonymous committed 111659e Merge

Merge changes I707570ad,I900b673d,I62310b2c,I6b030d9d,I8f5a55de,I51a12fb8,I857143a5,I95025d52,I6367555d,I7d80e2b4,If4a4a2bb,I884eedea,Ibd0b26e4,Ia145018d,I2706e6fb,Ib1467329

* changes:
procrank: add support for only showing cached, noncached, or KSM pages
libpagemap: add pm_process_usage_flags
librank: add flag to show only KSM pages
libpagemap: fix new kernel flag names to start with PM_PAGE_
librank: show swapped pages per library
procrank: show swapped pages per process
libpagemap: count swapped pages
librank: add command line argument for cached or non-cached pages
libpagemap: add pm_map_usage_flags
librank: add command line argument to show all mappings
libpagemap: add page flags for new kernels
librank: add permissions command line argument
libpagemap: add definition for mask of permission flags
librank: convert argument parsing to getopt_long
libpagemap: fix vss calculation
libpagemap: fix reusing previous name for mappings with no name

  • Participants
  • Parent commits 69294b5, 8993c69

Comments (0)

Files changed (6)

libpagemap/include/pagemap/pagemap.h

     size_t rss;
     size_t pss;
     size_t uss;
+    size_t swap;
 };
 
 /* Clears a memusage. */
 #define PM_PAGE_RECLAIM    (1 <<  9)
 #define PM_PAGE_BUDDY      (1 << 10)
 
+/* for kernels >= 2.6.31 */
+#define PM_PAGE_MMAP          (1 << 11)
+#define PM_PAGE_ANON          (1 << 12)
+#define PM_PAGE_SWAPCACHE     (1 << 13)
+#define PM_PAGE_SWAPBACKED    (1 << 14)
+#define PM_PAGE_COMPOUND_HEAD (1 << 15)
+#define PM_PAGE_COMPOUND_TAIL (1 << 16)
+#define PM_PAGE_HUGE          (1 << 17)
+#define PM_PAGE_UNEVICTABLE   (1 << 18)
+#define PM_PAGE_HWPOISON      (1 << 19)
+#define PM_PAGE_NOPAGE        (1 << 20)
+
+/* for kernels >= 2.6.32 */
+#define PM_PAGE_KSM           (1 << 21)
+
+/* for kernels >= 3.4 */
+#define PM_PAGE_THP           (1 << 22)
+
 /* Destroy a pm_kernel_t. */
 int pm_kernel_destroy(pm_kernel_t *ker);
 
 /* Get the total memory usage of a process and store in *usage_out. */
 int pm_process_usage(pm_process_t *proc, pm_memusage_t *usage_out);
 
+/* Get the total memory usage of a process and store in *usage_out, only
+ * counting pages with specified flags. */
+int pm_process_usage_flags(pm_process_t *proc, pm_memusage_t *usage_out,
+                        uint64_t flags_mask, uint64_t required_flags);
+
 /* Get the working set of a process (if ws_out != NULL), and reset it
  * (if reset != 0). */
 int pm_process_workingset(pm_process_t *proc, pm_memusage_t *ws_out, int reset);
 #define PM_MAP_READ  1
 #define PM_MAP_WRITE 2
 #define PM_MAP_EXEC  4
+#define PM_MAP_PERMISSIONS (PM_MAP_READ | PM_MAP_WRITE | PM_MAP_EXEC)
 #define pm_map_start(map)  ((map)->start)
 #define pm_map_end(map)    ((map)->end)
 #define pm_map_offset(map) ((map)->offset)
 /* Get the memory usage of this map alone. */
 int pm_map_usage(pm_map_t *map, pm_memusage_t *usage_out);
 
+/* Get the memory usage of this map alone, only counting pages with specified
+ * flags. */
+int pm_map_usage_flags(pm_map_t *map, pm_memusage_t *usage_out,
+                        uint64_t flags_mask, uint64_t required_flags);
+
 /* Get the working set of this map alone. */
 int pm_map_workingset(pm_map_t *map, pm_memusage_t *ws_out);
 

libpagemap/pm_map.c

                                     pagemap_out, len);
 }
 
-int pm_map_usage(pm_map_t *map, pm_memusage_t *usage_out) {
+int pm_map_usage_flags(pm_map_t *map, pm_memusage_t *usage_out,
+                        uint64_t flags_mask, uint64_t required_flags) {
     uint64_t *pagemap;
     size_t len, i;
     uint64_t count;
     pm_memusage_zero(&usage);
 
     for (i = 0; i < len; i++) {
-        if (!PM_PAGEMAP_PRESENT(pagemap[i]) ||
-            PM_PAGEMAP_SWAPPED(pagemap[i]))
-            continue;
+        usage.vss += map->proc->ker->pagesize;
 
-        error = pm_kernel_count(map->proc->ker, PM_PAGEMAP_PFN(pagemap[i]),
-                                &count);
-        if (error) goto out;
+        if (!PM_PAGEMAP_PRESENT(pagemap[i]))
+            continue;
 
-        usage.vss += map->proc->ker->pagesize;
-        usage.rss += (count >= 1) ? (map->proc->ker->pagesize) : (0);
-        usage.pss += (count >= 1) ? (map->proc->ker->pagesize / count) : (0);
-        usage.uss += (count == 1) ? (map->proc->ker->pagesize) : (0);
+        if (!PM_PAGEMAP_SWAPPED(pagemap[i])) {
+            if (flags_mask) {
+                uint64_t flags;
+                error = pm_kernel_flags(map->proc->ker, PM_PAGEMAP_PFN(pagemap[i]),
+                                        &flags);
+                if (error) goto out;
+
+                if ((flags & flags_mask) != required_flags)
+                    continue;
+            }
+
+            error = pm_kernel_count(map->proc->ker, PM_PAGEMAP_PFN(pagemap[i]),
+                                    &count);
+            if (error) goto out;
+
+            usage.rss += (count >= 1) ? map->proc->ker->pagesize : (0);
+            usage.pss += (count >= 1) ? (map->proc->ker->pagesize / count) : (0);
+            usage.uss += (count == 1) ? (map->proc->ker->pagesize) : (0);
+        } else {
+            usage.swap += map->proc->ker->pagesize;
+        }
     }
 
     memcpy(usage_out, &usage, sizeof(usage));
     return error;
 }
 
+int pm_map_usage(pm_map_t *map, pm_memusage_t *usage_out) {
+    return pm_map_usage_flags(map, usage_out, 0, 0);
+}
+
 int pm_map_workingset(pm_map_t *map, pm_memusage_t *ws_out) {
     uint64_t *pagemap;
     size_t len, i;

libpagemap/pm_memusage.c

 #include <pagemap/pagemap.h>
 
 void pm_memusage_zero(pm_memusage_t *mu) {
-    mu->vss = mu->rss = mu->pss = mu->uss = 0;
+    mu->vss = mu->rss = mu->pss = mu->uss = mu->swap = 0;
 }
 
 void pm_memusage_add(pm_memusage_t *a, pm_memusage_t *b) {
     a->rss += b->rss;
     a->pss += b->pss;
     a->uss += b->uss;
+    a->swap += b->swap;
 }

libpagemap/pm_process.c

     return 0;
 }
 
-int pm_process_usage(pm_process_t *proc, pm_memusage_t *usage_out) {
+int pm_process_usage_flags(pm_process_t *proc, pm_memusage_t *usage_out,
+                        uint64_t flags_mask, uint64_t required_flags)
+{
     pm_memusage_t usage, map_usage;
     int error;
     int i;
     pm_memusage_zero(&usage);
 
     for (i = 0; i < proc->num_maps; i++) {
-        error = pm_map_usage(proc->maps[i], &map_usage);
+        error = pm_map_usage_flags(proc->maps[i], &map_usage, flags_mask,
+                                   required_flags);
         if (error) return error;
 
         pm_memusage_add(&usage, &map_usage);
     memcpy(usage_out, &usage, sizeof(pm_memusage_t));
 
     return 0;
+
+}
+
+int pm_process_usage(pm_process_t *proc, pm_memusage_t *usage_out) {
+    return pm_process_usage_flags(proc, usage_out, 0, 0);
 }
 
 int pm_process_pagemap_range(pm_process_t *proc,
 
         map->proc = proc;
 
+        name[0] = '\0';
         sscanf(line, "%lx-%lx %s %lx %*s %*d %" S(MAX_LINE) "s",
                &map->start, &map->end, perms, &map->offset, name);
 

librank/librank.c

 #include <assert.h>
 #include <dirent.h>
 #include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 declare_sort(rss);
 declare_sort(pss);
 declare_sort(uss);
+declare_sort(swap);
 
 #define INIT_LIBRARIES 16
 #define INIT_MAPPINGS 4
 int libraries_count;
 int libraries_size;
 
-struct library_info *get_library(char *name) {
+struct library_info *get_library(const char *name, bool all) {
     int i;
     struct library_info *library;
 
-    for (i = 0; library_name_blacklist[i]; i++)
-        if (!strcmp(name, library_name_blacklist[i]))
-            return NULL;
+    if (!all) {
+        for (i = 0; library_name_blacklist[i]; i++)
+            if (!strcmp(name, library_name_blacklist[i]))
+                return NULL;
+    } else {
+        if (name[0] == 0) {
+            name = "[anon]";
+        }
+    }
 
     for (i = 0; i < libraries_count; i++) {
         if (!strcmp(libraries[i]->name, name))
     return process;
 }
 
+static int parse_perm(const char *perm)
+{
+    int ret = 0;
+
+    while (*perm) {
+        switch(*perm) {
+        case 'r':
+            ret |= PM_MAP_READ;
+            break;
+        case 'w':
+            ret |= PM_MAP_WRITE;
+            break;
+        case 'x':
+            ret |= PM_MAP_EXEC;
+            break;
+        default:
+            fprintf(stderr, "Unknown permission '%c'\n", *perm);
+            exit(EXIT_FAILURE);
+        }
+        perm++;
+    }
+    return ret;
+}
+
 int main(int argc, char *argv[]) {
     char cmdline[256];
     char *prefix;
     struct process_info *pi;
 
     int i, j, error;
+    int perm;
+    bool all;
+    uint64_t required_flags;
+    uint64_t flags_mask;
+
+    bool has_swap = false;
 
     signal(SIGPIPE, SIG_IGN);
     compfn = &sort_by_pss;
     order = -1;
     prefix = NULL;
     prefix_len = 0;
-
-    for (i = 1; i < argc; i++) {
-        if (!strcmp(argv[i], "-P")) {
-            if (i + 1 >= argc) {
-                fprintf(stderr, "Option -P requires an argument.\n");
-                usage(argv[0]);
-                exit(EXIT_FAILURE);
-            }
-            prefix = argv[++i];
+    opterr = 0;
+    perm = 0;
+    all = false;
+    required_flags = 0;
+    flags_mask = 0;
+
+    while (1) {
+        int c;
+        const struct option longopts[] = {
+            {"all", 0, 0, 'a'},
+            {"cached", 0, 0, 'c'},
+            {"nocached", 0, 0, 'C'},
+            {"ksm", 0, 0, 'k'},
+            {"help", 0, 0, 'h'},
+            {"pss", 0, 0, 'p'},
+            {"uss", 0, 0, 'u'},
+            {"vss", 0, 0, 'v'},
+            {"rss", 0, 0, 'r'},
+            {"swap", 0, 0, 's'},
+            {"reverse", 0, 0, 'R'},
+            {"path", required_argument, 0, 'P'},
+            {"perm", required_argument, 0, 'm'},
+            {0, 0, 0, 0}
+        };
+        c = getopt_long(argc, argv, "acChkm:pP:uvrsR", longopts, NULL);
+        if (c < 0) {
+            break;
+        }
+        /* Alphabetical cases */
+        switch (c) {
+        case 'a':
+            all = true;
+            break;
+        case 'c':
+            required_flags = 0;
+            flags_mask = PM_PAGE_SWAPBACKED;
+            break;
+        case 'C':
+            required_flags = PM_PAGE_SWAPBACKED;
+            flags_mask = PM_PAGE_SWAPBACKED;
+            break;
+        case 'k':
+            required_flags = PM_PAGE_KSM;
+            flags_mask = PM_PAGE_KSM;
+            break;
+        case 'h':
+            usage(argv[0]);
+            exit(EXIT_SUCCESS);
+        case 'm':
+            perm = parse_perm(optarg);
+            break;
+        case 'p':
+            compfn = &sort_by_pss;
+            break;
+        case 'P':
+            prefix = optarg;
             prefix_len = strlen(prefix);
-            continue;
+            break;
+        case 'u':
+            compfn = &sort_by_uss;
+            break;
+        case 'v':
+            compfn = &sort_by_vss;
+            break;
+        case 'r':
+            compfn = &sort_by_rss;
+            break;
+        case 's':
+            compfn = &sort_by_swap;
+            break;
+        case 'R':
+            order *= -1;
+            break;
+        case '?':
+            fprintf(stderr, "Invalid argument \"%s\".\n", argv[optind - 1]);
+            usage(argv[0]);
+            exit(EXIT_FAILURE);
+        default:
+            abort();
         }
-        if (!strcmp(argv[i], "-v")) { compfn = &sort_by_vss; continue; }
-        if (!strcmp(argv[i], "-r")) { compfn = &sort_by_rss; continue; }
-        if (!strcmp(argv[i], "-p")) { compfn = &sort_by_pss; continue; }
-        if (!strcmp(argv[i], "-u")) { compfn = &sort_by_uss; continue; }
-        if (!strcmp(argv[i], "-R")) { order *= -1; continue; }
-        if (!strcmp(argv[i], "-h")) { usage(argv[0]); exit(0); }
-        fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
-        usage(argv[0]);
-        exit(EXIT_FAILURE);
     }
 
+    argc -= optind;
+    argv += optind;
+
     libraries = malloc(INIT_LIBRARIES * sizeof(struct library_info *));
     libraries_count = 0; libraries_size = INIT_LIBRARIES;
 
             if (prefix && (strncmp(pm_map_name(maps[j]), prefix, prefix_len)))
                 continue;
 
-            li = get_library(pm_map_name(maps[j]));
+            if (perm && (pm_map_flags(maps[j]) & PM_MAP_PERMISSIONS) != perm)
+                continue;
+
+            li = get_library(pm_map_name(maps[j]), all);
             if (!li)
                 continue;
 
             mi = get_mapping(li, pi);
             
-            error = pm_map_usage(maps[j], &map_usage);
+            error = pm_map_usage_flags(maps[j], &map_usage, flags_mask,
+                                       required_flags);
             if (error) {
                 fprintf(stderr, "Error getting map memory usage of "
                                 "map %s in process %d.\n",
                         pm_map_name(maps[j]), proc->pid);
                 exit(EXIT_FAILURE);
             }
+
+            if (map_usage.swap) {
+                has_swap = true;
+            }
+
             pm_memusage_add(&mi->usage, &map_usage);
             pm_memusage_add(&li->total_usage, &map_usage);
         }
     }
 
-    printf(          " %6s   %6s   %6s   %6s   %6s  %s\n", "RSStot", "VSS", "RSS", "PSS", "USS", "Name/PID");
+    printf(" %6s   %6s   %6s   %6s   %6s  ", "RSStot", "VSS", "RSS", "PSS", "USS");
+
+    if (has_swap) {
+        printf(" %6s  ", "Swap");
+    }
+
+    printf("Name/PID\n");
     fflush(stdout);
 
     qsort(libraries, libraries_count, sizeof(libraries[0]), &licmp);
     for (i = 0; i < libraries_count; i++) {
         li = libraries[i];
 
-        printf("%6dK   %6s   %6s   %6s   %6s  %s\n", li->total_usage.pss / 1024, "", "", "", "", li->name);
+        printf("%6dK   %6s   %6s   %6s   %6s  ", li->total_usage.pss / 1024, "", "", "", "");
+        if (has_swap) {
+            printf(" %6s  ", "");
+        }
+        printf("%s\n", li->name);
         fflush(stdout);
 
         qsort(li->mappings, li->mappings_count, sizeof(li->mappings[0]), compfn);
         for (j = 0; j < li->mappings_count; j++) {
             mi = li->mappings[j];
             pi = mi->proc;
-            printf(   " %6s  %6dK  %6dK  %6dK  %6dK    %s [%d]\n", "",
+            printf(   " %6s  %6dK  %6dK  %6dK  %6dK  ", "",
                 mi->usage.vss / 1024,
                 mi->usage.rss / 1024,
                 mi->usage.pss / 1024,
-                mi->usage.uss / 1024,
+                mi->usage.uss / 1024);
+            if (has_swap) {
+                printf("%6dK  ", mi->usage.swap / 1024);
+            }
+            printf("  %s [%d]\n",
                 pi->cmdline,
                 pi->pid);
         }
 }
 
 static void usage(char *myname) {
-    fprintf(stderr, "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -h ]\n"
+    fprintf(stderr, "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n"
                     "\n"
                     "Sort options:\n"
                     "    -v  Sort processes by VSS.\n"
                     "    -r  Sort processes by RSS.\n"
                     "    -p  Sort processes by PSS.\n"
                     "    -u  Sort processes by USS.\n"
+                    "    -s  Sort processes by swap.\n"
                     "        (Default sort order is PSS.)\n"
+                    "    -a  Show all mappings, including stack, heap and anon.\n"
                     "    -P /path  Limit libraries displayed to those in path.\n"
                     "    -R  Reverse sort order (default is descending).\n"
+                    "    -m [r][w][x] Only list pages that exactly match permissions\n"
+                    "    -c  Only show cached (storage backed) pages\n"
+                    "    -C  Only show non-cached (ram/swap backed) pages\n"
+                    "    -k  Only show pages collapsed by KSM\n"
                     "    -h  Display this help screen.\n",
     myname);
 }
 create_sort(rss, numcmp)
 create_sort(pss, numcmp)
 create_sort(uss, numcmp)
+create_sort(swap, numcmp)

procrank/procrank.c

 
 #include <dirent.h>
 #include <errno.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <unistd.h>
 declare_sort(rss);
 declare_sort(pss);
 declare_sort(uss);
+declare_sort(swap);
 
 int (*compfn)(const void *a, const void *b);
 static int order;
     size_t num_procs;
     unsigned long total_pss;
     unsigned long total_uss;
+    unsigned long total_swap;
     char cmdline[256]; // this must be within the range of int
     int error;
+    bool has_swap = false;
+    uint64_t required_flags = 0;
+    uint64_t flags_mask = 0;
 
     #define WS_OFF   0
     #define WS_ONLY  1
         if (!strcmp(argv[arg], "-r")) { compfn = &sort_by_rss; continue; }
         if (!strcmp(argv[arg], "-p")) { compfn = &sort_by_pss; continue; }
         if (!strcmp(argv[arg], "-u")) { compfn = &sort_by_uss; continue; }
+        if (!strcmp(argv[arg], "-s")) { compfn = &sort_by_swap; continue; }
+        if (!strcmp(argv[arg], "-c")) { required_flags = 0; flags_mask = PM_PAGE_SWAPBACKED; continue; }
+        if (!strcmp(argv[arg], "-C")) { required_flags = flags_mask = PM_PAGE_SWAPBACKED; continue; }
+        if (!strcmp(argv[arg], "-k")) { required_flags = flags_mask = PM_PAGE_KSM; continue; }
         if (!strcmp(argv[arg], "-w")) { ws = WS_ONLY; continue; }
         if (!strcmp(argv[arg], "-W")) { ws = WS_RESET; continue; }
         if (!strcmp(argv[arg], "-R")) { order *= -1; continue; }
 
         switch (ws) {
         case WS_OFF:
-            error = pm_process_usage(proc, &procs[i]->usage);
+            error = pm_process_usage_flags(proc, &procs[i]->usage, flags_mask,
+                                           required_flags);
             break;
         case WS_ONLY:
             error = pm_process_workingset(proc, &procs[i]->usage, 0);
             fprintf(stderr, "warning: could not read usage for %d\n", pids[i]);
         }
 
+        if (ws != WS_RESET && procs[i]->usage.swap) {
+            has_swap = true;
+        }
+
         pm_process_destroy(proc);
     }
 
 
     qsort(procs, num_procs, sizeof(procs[0]), compfn);
 
-    if (ws)
-        printf("%5s  %7s  %7s  %7s  %s\n", "PID", "WRss", "WPss", "WUss", "cmdline");
-    else
-        printf("%5s  %7s  %7s  %7s  %7s  %s\n", "PID", "Vss", "Rss", "Pss", "Uss", "cmdline");
+    printf("%5s  ", "PID");
+    if (ws) {
+        printf("%s  %7s  %7s  ", "WRss", "WPss", "WUss");
+        if (has_swap) {
+            printf("%7s  ", "WSwap");
+        }
+    } else {
+        printf("%8s  %7s  %7s  %7s  ", "Vss", "Rss", "Pss", "Uss");
+        if (has_swap) {
+            printf("%7s  ", "Swap");
+        }
+    }
+
+    printf("%s\n", "cmdline");
 
     total_pss = 0;
     total_uss = 0;
+    total_swap = 0;
 
     for (i = 0; i < num_procs; i++) {
         if (getprocname(procs[i]->pid, cmdline, (int)sizeof(cmdline)) < 0) {
 
         total_pss += procs[i]->usage.pss;
         total_uss += procs[i]->usage.uss;
+        total_swap += procs[i]->usage.swap;
+
+        printf("%5d  ", procs[i]->pid);
 
-        if (ws)
-            printf("%5d  %6dK  %6dK  %6dK  %s\n",
-                procs[i]->pid,
+        if (ws) {
+            printf("%6dK  %6dK  %6dK  ",
                 procs[i]->usage.rss / 1024,
                 procs[i]->usage.pss / 1024,
-                procs[i]->usage.uss / 1024,
-                cmdline
+                procs[i]->usage.uss / 1024
             );
-        else
-            printf("%5d  %6dK  %6dK  %6dK  %6dK  %s\n",
-                procs[i]->pid,
+        } else {
+            printf("%7dK  %6dK  %6dK  %6dK  ",
                 procs[i]->usage.vss / 1024,
                 procs[i]->usage.rss / 1024,
                 procs[i]->usage.pss / 1024,
-                procs[i]->usage.uss / 1024,
-                cmdline
+                procs[i]->usage.uss / 1024
             );
+        }
+
+        if (has_swap) {
+            printf("%6dK  ", procs[i]->usage.swap / 1024);
+        }
+
+        printf("%s\n", cmdline);
 
         free(procs[i]);
     }
 
     free(procs);
 
+    /* Print the separator line */
+    printf("%5s  ", "");
+
+    if (ws) {
+        printf("%7s  %7s  %7s  ", "", "------", "------");
+    } else {
+        printf("%8s  %7s  %7s  %7s  ", "", "", "------", "------");
+    }
+
+    if (has_swap) {
+        printf("%7s  ", "------");
+    }
+
+    printf("%s\n", "------");
+
+    /* Print the total line */
+    printf("%5s  ", "");
     if (ws) {
-        printf("%5s  %7s  %7s  %7s  %s\n",
-            "", "", "------", "------", "------");
-        printf("%5s  %7s  %6ldK  %6ldK  %s\n",
-            "", "", total_pss / 1024, total_uss / 1024, "TOTAL");
+        printf("%7s  %6ldK  %6ldK  ",
+            "", total_pss / 1024, total_uss / 1024);
     } else {
-        printf("%5s  %7s  %7s  %7s  %7s  %s\n",
-            "", "", "", "------", "------", "------");
-        printf("%5s  %7s  %7s  %6ldK  %6ldK  %s\n",
-            "", "", "", total_pss / 1024, total_uss / 1024, "TOTAL");
+        printf("%8s  %7s  %6ldK  %6ldK  ",
+            "", "", total_pss / 1024, total_uss / 1024);
     }
 
+    if (has_swap) {
+        printf("%6ldK  ", total_swap);
+    }
+
+    printf("TOTAL\n");
+
     printf("\n");
     print_mem_info();
 
 }
 
 static void usage(char *myname) {
-    fprintf(stderr, "Usage: %s [ -W ] [ -v | -r | -p | -u | -h ]\n"
+    fprintf(stderr, "Usage: %s [ -W ] [ -v | -r | -p | -u | -s | -h ]\n"
                     "    -v  Sort by VSS.\n"
                     "    -r  Sort by RSS.\n"
                     "    -p  Sort by PSS.\n"
                     "    -u  Sort by USS.\n"
+                    "    -s  Sort by swap.\n"
                     "        (Default sort order is PSS.)\n"
                     "    -R  Reverse sort order (default is descending).\n"
+                    "    -c  Only show cached (storage backed) pages\n"
+                    "    -C  Only show non-cached (ram/swap backed) pages\n"
+                    "    -k  Only show pages collapsed by KSM\n"
                     "    -w  Display statistics for working set only.\n"
                     "    -W  Reset working set of all processes.\n"
                     "    -h  Display this help screen.\n",
 create_sort(rss, numcmp)
 create_sort(pss, numcmp)
 create_sort(uss, numcmp)
+create_sort(swap, numcmp)