libmeminfo: Add libmeminfo to gather global and per-process memory stats

The library is expected to be a unified place for all components to read
both global and per-process memory accounting form kernel including
getting the working set. This change adds the PageInfo, MemInfo and
ProcMemInfo classes and verifies the implementation against libpagemap
for correctness.

Adds a procmem2 tool show the usage.

TODO: Plumbing in os_debug, add vmastats, zoneinfo etc parsing.

Test: libmeminfo_test 1
Test: procmem2 1
Test: procmem2 -h -W 1
Test: procmem2 -h -w 1
Test: libmeminfo_benchmark
Bug: 111694435
Bug: 114325007

Change-Id: I280440b1dc26a498170686d10fcf63f953a0dcbd
Signed-off-by: Sandeep Patil <sspatil@google.com>
diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp
new file mode 100644
index 0000000..3571e41
--- /dev/null
+++ b/libmeminfo/tools/procmem.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2018 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 <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <meminfo/procmeminfo.h>
+
+using ProcMemInfo = ::android::meminfo::ProcMemInfo;
+using MemUsage = ::android::meminfo::MemUsage;
+
+static void usage(const char* cmd) {
+    fprintf(stderr,
+            "Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
+            "    -i  Uses idle page tracking for working set statistics.\n"
+            "    -w  Displays statistics for the working set only.\n"
+            "    -W  Resets the working set of the process.\n"
+            "    -p  Sort by PSS.\n"
+            "    -u  Sort by USS.\n"
+            "    -m  Sort by mapping order (as read from /proc).\n"
+            "    -h  Hide maps with no RSS.\n",
+            cmd);
+}
+
+static void show_footer(uint32_t nelem, const std::string& padding) {
+    std::string elem(7, '-');
+
+    for (uint32_t i = 0; i < nelem; ++i) {
+        std::cout << std::setw(7) << elem << padding;
+    }
+    std::cout << std::endl;
+}
+
+static void show_header(const std::vector<std::string>& header, const std::string& padding) {
+    if (header.empty()) return;
+
+    for (size_t i = 0; i < header.size() - 1; ++i) {
+        std::cout << std::setw(7) << header[i] << padding;
+    }
+    std::cout << header.back() << std::endl;
+    show_footer(header.size() - 1, padding);
+}
+
+static void scan_usage(std::stringstream& ss, const MemUsage& usage, const std::string& padding,
+                       bool show_wss) {
+    // clear string stream first.
+    ss.str("");
+    // TODO: use ::android::base::StringPrintf instead of <iomanip> here.
+    if (!show_wss)
+        ss << std::setw(6) << usage.vss/1024 << padding;
+    ss << std::setw(6) << usage.rss/1024 << padding << std::setw(6)
+       << usage.pss/1024 << padding << std::setw(6) << usage.uss/1024 << padding
+       << std::setw(6) << usage.shared_clean/1024 << padding << std::setw(6)
+       << usage.shared_dirty/1024 << padding << std::setw(6)
+       << usage.private_clean/1024 << padding << std::setw(6)
+       << usage.private_dirty/1024 << padding;
+}
+
+static int show(ProcMemInfo& proc, bool hide_zeroes, bool show_wss) {
+    const std::vector<std::string> main_header = {"Vss",  "Rss",  "Pss",  "Uss", "ShCl",
+                                                  "ShDi", "PrCl", "PrDi", "Name"};
+    const std::vector<std::string> wss_header = {"WRss",  "WPss",  "WUss",  "WShCl",
+                                                 "WShDi", "WPrCl", "WPrDi", "Name"};
+    const std::vector<std::string>& header = show_wss ? wss_header : main_header;
+
+    // Read process memory stats
+    const MemUsage& stats = show_wss ? proc.Wss() : proc.Usage();
+    const std::vector<::android::meminfo::Vma>& maps = proc.Maps();
+
+    // following retains 'procmem' output so as to not break any scripts
+    // that rely on it.
+    std::string spaces = "  ";
+    show_header(header, spaces);
+    const std::string padding = "K  ";
+    std::stringstream ss;
+    for (auto& vma : maps) {
+        const MemUsage& vma_stats = show_wss ? vma.wss : vma.usage;
+        if (hide_zeroes && vma_stats.rss == 0) {
+            continue;
+        }
+        scan_usage(ss, vma_stats, padding, show_wss);
+        ss << vma.name << std::endl;
+        std::cout << ss.str();
+    }
+    show_footer(header.size() - 1, spaces);
+    scan_usage(ss, stats, padding, show_wss);
+    ss << "TOTAL" << std::endl;
+    std::cout << ss.str();
+
+    return 0;
+}
+
+int main(int argc, char* argv[]) {
+    bool use_pageidle = false;
+    bool hide_zeroes = false;
+    bool wss_reset = false;
+    bool show_wss = false;
+    int opt;
+
+    while ((opt = getopt(argc, argv, "himpuWw")) != -1) {
+        switch (opt) {
+            case 'h':
+                hide_zeroes = true;
+                break;
+            case 'i':
+                use_pageidle = true;
+                break;
+            case 'm':
+                break;
+            case 'p':
+                break;
+            case 'u':
+                break;
+            case 'W':
+                wss_reset = true;
+                break;
+            case 'w':
+                show_wss = true;
+                break;
+            case '?':
+                usage(argv[0]);
+                return 0;
+            default:
+                abort();
+        }
+    }
+
+    if (optind != (argc - 1)) {
+        fprintf(stderr, "Need exactly one pid at the end\n");
+        usage(argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    pid_t pid = atoi(argv[optind]);
+    if (pid == 0) {
+        std::cerr << "Invalid process id" << std::endl;
+        exit(EXIT_FAILURE);
+    }
+
+    bool need_wss = wss_reset || show_wss;
+    ProcMemInfo proc(pid, need_wss);
+    if (wss_reset) {
+        if (!proc.WssReset()) {
+            std::cerr << "Failed to reset working set of pid : " << pid << std::endl;
+            exit(EXIT_FAILURE);
+        }
+        return 0;
+    }
+
+    return show(proc, hide_zeroes, show_wss);
+}