Project import
diff --git a/librank/Android.mk b/librank/Android.mk
new file mode 100644
index 0000000..101b94a
--- /dev/null
+++ b/librank/Android.mk
@@ -0,0 +1,23 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := librank.c
+LOCAL_SHARED_LIBRARIES := libpagemap
+LOCAL_MODULE := librank
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_EXECUTABLE)
diff --git a/librank/MODULE_LICENSE_APACHE2 b/librank/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/librank/MODULE_LICENSE_APACHE2
diff --git a/librank/NOTICE b/librank/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/librank/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/librank/librank.c b/librank/librank.c
new file mode 100644
index 0000000..9d1c026
--- /dev/null
+++ b/librank/librank.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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>
+#include <unistd.h>
+
+#include <pagemap/pagemap.h>
+
+#define MAX_CMDLINE 256
+
+struct process_info {
+    pid_t pid;
+    char cmdline[MAX_CMDLINE];
+};
+
+struct mapping_info {
+    struct process_info *proc;
+    pm_memusage_t usage;
+};
+
+struct library_info {
+    struct library_info *next;
+    char *name;
+    struct mapping_info **mappings;
+    size_t mappings_count;
+    size_t mappings_size;
+    pm_memusage_t total_usage;
+};
+
+static void usage(char *myname);
+static int getprocname(pid_t pid, char *buf, size_t len);
+static int numcmp(long long a, long long b);
+static int licmp(const void *a, const void *b);
+
+char *library_name_blacklist[] = { "[heap]", "[stack]", "", NULL };
+
+#define declare_sort(field) \
+    static int sort_by_ ## field (const void *a, const void *b)
+
+declare_sort(vss);
+declare_sort(rss);
+declare_sort(pss);
+declare_sort(uss);
+declare_sort(swap);
+
+#define INIT_LIBRARIES 16
+#define INIT_MAPPINGS 4
+
+static int order;
+
+struct library_info **libraries;
+size_t libraries_count;
+size_t libraries_size;
+
+struct library_info *get_library(const char *name, bool all) {
+    size_t i;
+    struct library_info *library;
+
+    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 libraries[i];
+    }
+
+    if (libraries_count >= libraries_size) {
+        libraries = realloc(libraries, 2 * libraries_size * sizeof(struct library_info *));
+        if (!libraries) {
+            fprintf(stderr, "Couldn't resize libraries array: %s\n", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        libraries_size = 2 * libraries_size;
+    }
+
+    library = calloc(1, sizeof(*library));
+    if (!library) {
+        fprintf(stderr, "Couldn't allocate space for library struct: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    library->name = malloc(strlen(name) + 1);
+    if (!library->name) {
+        fprintf(stderr, "Couldn't allocate space for library name: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    strcpy(library->name, name);
+    library->mappings = malloc(INIT_MAPPINGS * sizeof(struct mapping_info *));
+    if (!library->mappings) {
+        fprintf(stderr, "Couldn't allocate space for library mappings array: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    library->mappings_count = 0; library->mappings_size = INIT_MAPPINGS;
+    pm_memusage_zero(&library->total_usage);
+
+    libraries[libraries_count++] = library;
+
+    return library;
+}
+
+struct mapping_info *get_mapping(struct library_info *library, struct process_info *proc) {
+    struct mapping_info *mapping;
+    size_t i;
+
+    for (i = 0; i < library->mappings_count; i++) {
+        if (library->mappings[i]->proc == proc)
+            return library->mappings[i];
+    }
+
+    if (library->mappings_count >= library->mappings_size) {
+        library->mappings = realloc(library->mappings,
+            2 * library->mappings_size * sizeof(struct mapping_info*));
+        if (!library->mappings) {
+            fprintf(stderr, "Couldn't resize mappings array: %s\n", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        library->mappings_size = 2 * library->mappings_size;
+    }
+
+    mapping = calloc(1, sizeof(*mapping));
+    if (!mapping) {
+        fprintf(stderr, "Couldn't allocate space for mapping struct: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    mapping->proc = proc;
+    pm_memusage_zero(&mapping->usage);
+
+    library->mappings[library->mappings_count++] = mapping;
+
+    return mapping;
+}
+
+struct process_info *get_process(pid_t pid) {
+    struct process_info *process;
+
+    process = calloc(1, sizeof(*process));
+    if (!process) {
+        fprintf(stderr, "Couldn't allocate space for process struct: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+
+    process->pid = pid;
+    getprocname(pid, process->cmdline, sizeof(process->cmdline));
+
+    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;
+    size_t prefix_len;
+    int (*compfn)(const void *a, const void *b);
+
+    pm_kernel_t *ker;
+    pm_process_t *proc;
+
+    pid_t *pids;
+    size_t num_procs;
+
+    pm_map_t **maps;
+    size_t num_maps;
+    pm_memusage_t map_usage;
+
+    struct library_info *li, **lis;
+    struct mapping_info *mi, **mis;
+    struct process_info *pi;
+
+    size_t i, j;
+    int 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;
+    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);
+            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();
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    libraries = malloc(INIT_LIBRARIES * sizeof(struct library_info *));
+    libraries_count = 0; libraries_size = INIT_LIBRARIES;
+    pm_memusage_zero(&map_usage);
+
+    error = pm_kernel_create(&ker);
+    if (error) {
+        fprintf(stderr, "Error initializing kernel interface -- "
+                        "does this kernel have pagemap?\n");
+        exit(EXIT_FAILURE);
+    }
+
+    error = pm_kernel_pids(ker, &pids, &num_procs);
+    if (error) {
+        fprintf(stderr, "Error listing processes.\n");
+        exit(EXIT_FAILURE);
+    }
+
+    for (i = 0; i < num_procs; i++) {
+        error = pm_process_create(ker, pids[i], &proc);
+        if (error) {
+            fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
+            continue;
+        }
+
+        pi = get_process(pids[i]);
+
+        error = pm_process_maps(proc, &maps, &num_maps);
+        if (error) {
+            fprintf(stderr, "Error listing maps for process %d.\n", proc->pid);
+            exit(EXIT_FAILURE);
+        }
+
+        for (j = 0; j < num_maps; j++) {
+            if (prefix && (strncmp(pm_map_name(maps[j]), prefix, prefix_len)))
+                continue;
+
+            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_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   %7s   %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("%6zdK   %7s   %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  %7zdK  %6zdK  %6zdK  %6zdK  ", "",
+                mi->usage.vss / 1024,
+                mi->usage.rss / 1024,
+                mi->usage.pss / 1024,
+                mi->usage.uss / 1024);
+            if (has_swap) {
+                printf("%6zdK  ", mi->usage.swap / 1024);
+            }
+            printf("  %s [%d]\n",
+                pi->cmdline,
+                pi->pid);
+        }
+        printf("\n");
+        fflush(stdout);
+    }
+
+    return 0;
+}
+
+static void usage(char *myname) {
+    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);
+}
+
+static int getprocname(pid_t pid, char *buf, size_t len) {
+    char filename[32];
+    FILE *f;
+
+    snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
+    f = fopen(filename, "r");
+    if (!f) {
+        *buf = '\0';
+        return 1;
+    }
+    if (!fgets(buf, len, f)) {
+        *buf = '\0';
+        fclose(f);
+        return 2;
+    }
+    fclose(f);
+    return 0;
+}
+
+static int numcmp(long long a, long long b) {
+    if (a < b) return -1;
+    if (a > b) return 1;
+    return 0;
+}
+
+static int licmp(const void *a, const void *b) {
+    return order * numcmp(
+        (*((struct library_info**)a))->total_usage.pss,
+        (*((struct library_info**)b))->total_usage.pss
+    );
+}
+
+#define create_sort(field, compfn) \
+    static int sort_by_ ## field (const void *a, const void *b) { \
+        return order * compfn( \
+            (*((struct mapping_info**)a))->usage.field, \
+            (*((struct mapping_info**)b))->usage.field \
+        ); \
+    }
+
+create_sort(vss, numcmp)
+create_sort(rss, numcmp)
+create_sort(pss, numcmp)
+create_sort(uss, numcmp)
+create_sort(swap, numcmp)