Snap for 10447354 from 9351dcecd2a462c9ff908dc915994b746c0697ee to mainline-wifi-release

Change-Id: I0c7ae9ae2ec784e35025473b8eb80c658994c761
diff --git a/OWNERS b/OWNERS
index 5a7a0bd..dd9c4c8 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1,6 @@
-# Bug component: 391836
+# Bug component: 1293033
 surenb@google.com
+tjmercier@google.com
+kaleshsingh@google.com
+jyescas@google.com
+carlosgalo@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 6b7fd71..c8dbf77 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,2 +1,5 @@
 [Builtin Hooks]
 clang_format = true
+
+[Builtin Hooks Options]
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 8dae108..50532e2 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "libmeminfo_test"
     }
   ],
-  "hwasan-postsubmit": [
+  "hwasan-presubmit": [
     {
       "name": "libmeminfo_test"
     }
diff --git a/include/meminfo/meminfo.h b/include/meminfo/meminfo.h
index 545f27d..199f7c8 100644
--- a/include/meminfo/meminfo.h
+++ b/include/meminfo/meminfo.h
@@ -45,6 +45,7 @@
     uint64_t file_pmd_mapped;
     uint64_t shared_hugetlb;
     uint64_t private_hugetlb;
+    uint64_t locked;
 
     uint64_t thp;
 
@@ -64,6 +65,7 @@
           file_pmd_mapped(0),
           shared_hugetlb(0),
           private_hugetlb(0),
+          locked(0),
           thp(0) {}
 
     ~MemUsage() = default;
diff --git a/include/meminfo/procmeminfo.h b/include/meminfo/procmeminfo.h
index 8e55b88..b53faa6 100644
--- a/include/meminfo/procmeminfo.h
+++ b/include/meminfo/procmeminfo.h
@@ -19,6 +19,7 @@
 #include <sys/types.h>
 
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "meminfo.h"
@@ -49,14 +50,20 @@
     const std::vector<Vma>& MapsWithoutUsageStats();
 
     // If MapsWithoutUsageStats was called, this function will fill in
-    // usage stats for this single vma.
-    bool FillInVmaStats(Vma& vma);
+    // usage stats for this single vma. If 'use_kb' is true, the vma's
+    // usage will be populated in kilobytes instead of bytes.
+    bool FillInVmaStats(Vma& vma, bool use_kb = false);
 
-    // Collect all 'vma' or 'maps' from /proc/<pid>/smaps and store them in 'maps_'. Returns a
-    // constant reference to the vma vector after the collection is done.
+    // If ReadMaps (with get_usage_stats == false) or MapsWithoutUsageStats was
+    // called, this function will fill in usage stats for all vmas in 'maps_'.
+    bool GetUsageStats(bool get_wss, bool use_pageidle = false, bool swap_only = false);
+
+    // Collect all 'vma' or 'maps' from /proc/<pid>/smaps and store them in 'maps_'. If
+    // 'collect_usage' is 'true', this method will populate 'usage_' as vmas are being
+    // collected. Returns a constant reference to the vma vector after the collection is done.
     //
     // Each 'struct Vma' is *fully* populated by this method (unlike SmapsOrRollup).
-    const std::vector<Vma>& Smaps(const std::string& path = "");
+    const std::vector<Vma>& Smaps(const std::string& path = "", bool collect_usage = false);
 
     // If 'use_smaps' is 'true' this method reads /proc/<pid>/smaps and calls the callback()
     // for each vma or map that it finds, else if 'use_smaps' is false /proc/<pid>/maps is
@@ -69,6 +76,16 @@
     // Returns false in case of failure during parsing.
     bool ForEachVmaFromMaps(const VmaCallback& callback);
 
+    // Similar to other VMA reading methods, except this one allows passing a reusable buffer
+    // to store the /proc/<pid>/maps content
+    bool ForEachVmaFromMaps(const VmaCallback& callback, std::string& mapsBuffer);
+
+    // Takes the existing VMAs in 'maps_' and calls the callback() for each one
+    // of them. This is intended to avoid parsing /proc/<pid>/maps or
+    // /proc/<pid>/smaps twice.
+    // Returns false if 'maps_' is empty.
+    bool ForEachExistingVma(const VmaCallback& callback);
+
     // Used to parse either of /proc/<pid>/{smaps, smaps_rollup} and record the process's
     // Pss and Private memory usage in 'stats'.  In particular, the method only populates the fields
     // of the MemUsage structure that are intended to be used by Android's periodic Pss collection.
@@ -138,5 +155,14 @@
 // as /proc/<pid>/smaps or /proc/<pid>/smaps_rollup
 bool SmapsOrRollupPssFromFile(const std::string& path, uint64_t* pss);
 
+// The output format that can be specified by user.
+enum class Format { INVALID = 0, RAW, JSON, CSV };
+
+Format GetFormat(std::string_view arg);
+
+std::string EscapeCsvString(const std::string& raw);
+
+std::string EscapeJsonString(const std::string& raw);
+
 }  // namespace meminfo
 }  // namespace android
diff --git a/include/meminfo/sysmeminfo.h b/include/meminfo/sysmeminfo.h
index a17f8ef..a695584 100644
--- a/include/meminfo/sysmeminfo.h
+++ b/include/meminfo/sysmeminfo.h
@@ -52,15 +52,24 @@
     static constexpr const char kMemActive[] = "Active:";
     static constexpr const char kMemInactive[] = "Inactive:";
     static constexpr const char kMemUnevictable[] = "Unevictable:";
-
+    static constexpr const char kMemAvailable[] = "MemAvailable:";
+    static constexpr const char kMemActiveAnon[] = "Active(anon):";
+    static constexpr const char kMemInactiveAnon[] = "Inactive(anon):";
+    static constexpr const char kMemActiveFile[] = "Active(file):";
+    static constexpr const char kMemInactiveFile[] = "Inactive(file):";
+    static constexpr const char kMemCmaTotal[] = "CmaTotal:";
+    static constexpr const char kMemCmaFree[] = "CmaFree:";
 
     static constexpr std::initializer_list<std::string_view> kDefaultSysMemInfoTags = {
-            SysMemInfo::kMemTotal,      SysMemInfo::kMemFree,        SysMemInfo::kMemBuffers,
-            SysMemInfo::kMemCached,     SysMemInfo::kMemShmem,       SysMemInfo::kMemSlab,
-            SysMemInfo::kMemSReclaim,   SysMemInfo::kMemSUnreclaim,  SysMemInfo::kMemSwapTotal,
-            SysMemInfo::kMemSwapFree,   SysMemInfo::kMemMapped,      SysMemInfo::kMemVmallocUsed,
-            SysMemInfo::kMemPageTables, SysMemInfo::kMemKernelStack, SysMemInfo::kMemKReclaimable,
-            SysMemInfo::kMemActive,     SysMemInfo::kMemInactive,    SysMemInfo::kMemUnevictable,
+            SysMemInfo::kMemTotal,      SysMemInfo::kMemFree,         SysMemInfo::kMemBuffers,
+            SysMemInfo::kMemCached,     SysMemInfo::kMemShmem,        SysMemInfo::kMemSlab,
+            SysMemInfo::kMemSReclaim,   SysMemInfo::kMemSUnreclaim,   SysMemInfo::kMemSwapTotal,
+            SysMemInfo::kMemSwapFree,   SysMemInfo::kMemMapped,       SysMemInfo::kMemVmallocUsed,
+            SysMemInfo::kMemPageTables, SysMemInfo::kMemKernelStack,  SysMemInfo::kMemKReclaimable,
+            SysMemInfo::kMemActive,     SysMemInfo::kMemInactive,     SysMemInfo::kMemUnevictable,
+            SysMemInfo::kMemAvailable,  SysMemInfo::kMemActiveAnon,   SysMemInfo::kMemInactiveAnon,
+            SysMemInfo::kMemActiveFile, SysMemInfo::kMemInactiveFile, SysMemInfo::kMemCmaTotal,
+            SysMemInfo::kMemCmaFree,
     };
 
     SysMemInfo() = default;
@@ -78,31 +87,48 @@
     uint64_t ReadVmallocInfo();
 
     // getters
-    uint64_t mem_total_kb() { return mem_in_kb_[kMemTotal]; }
-    uint64_t mem_free_kb() { return mem_in_kb_[kMemFree]; }
-    uint64_t mem_buffers_kb() { return mem_in_kb_[kMemBuffers]; }
-    uint64_t mem_cached_kb() { return mem_in_kb_[kMemCached]; }
-    uint64_t mem_shmem_kb() { return mem_in_kb_[kMemShmem]; }
-    uint64_t mem_slab_kb() { return mem_in_kb_[kMemSlab]; }
-    uint64_t mem_slab_reclaimable_kb() { return mem_in_kb_[kMemSReclaim]; }
-    uint64_t mem_slab_unreclaimable_kb() { return mem_in_kb_[kMemSUnreclaim]; }
-    uint64_t mem_swap_kb() { return mem_in_kb_[kMemSwapTotal]; }
-    uint64_t mem_swap_free_kb() { return mem_in_kb_[kMemSwapFree]; }
-    uint64_t mem_mapped_kb() { return mem_in_kb_[kMemMapped]; }
-    uint64_t mem_vmalloc_used_kb() { return mem_in_kb_[kMemVmallocUsed]; }
-    uint64_t mem_page_tables_kb() { return mem_in_kb_[kMemPageTables]; }
-    uint64_t mem_kernel_stack_kb() { return mem_in_kb_[kMemKernelStack]; }
-    uint64_t mem_kreclaimable_kb() { return mem_in_kb_[kMemKReclaimable]; }
-    uint64_t mem_active_kb() { return mem_in_kb_[kMemActive]; }
-    uint64_t mem_inactive_kb() { return mem_in_kb_[kMemInactive]; }
-    uint64_t mem_unevictable_kb() { return mem_in_kb_[kMemUnevictable]; }
-    uint64_t mem_zram_kb(const char* zram_dev = nullptr);
+    uint64_t mem_total_kb() const { return find_mem_by_tag(kMemTotal); }
+    uint64_t mem_free_kb() const { return find_mem_by_tag(kMemFree); }
+    uint64_t mem_buffers_kb() const { return find_mem_by_tag(kMemBuffers); }
+    uint64_t mem_cached_kb() const { return find_mem_by_tag(kMemCached); }
+    uint64_t mem_shmem_kb() const { return find_mem_by_tag(kMemShmem); }
+    uint64_t mem_slab_kb() const { return find_mem_by_tag(kMemSlab); }
+    uint64_t mem_slab_reclaimable_kb() const { return find_mem_by_tag(kMemSReclaim); }
+    uint64_t mem_slab_unreclaimable_kb() const { return find_mem_by_tag(kMemSUnreclaim); }
+    uint64_t mem_swap_kb() const { return find_mem_by_tag(kMemSwapTotal); }
+    uint64_t mem_swap_free_kb() const { return find_mem_by_tag(kMemSwapFree); }
+    uint64_t mem_mapped_kb() const { return find_mem_by_tag(kMemMapped); }
+    uint64_t mem_vmalloc_used_kb() const { return find_mem_by_tag(kMemVmallocUsed); }
+    uint64_t mem_page_tables_kb() const { return find_mem_by_tag(kMemPageTables); }
+    uint64_t mem_kernel_stack_kb() const { return find_mem_by_tag(kMemKernelStack); }
+    uint64_t mem_kreclaimable_kb() const { return find_mem_by_tag(kMemKReclaimable); }
+    uint64_t mem_active_kb() const { return find_mem_by_tag(kMemActive); }
+    uint64_t mem_inactive_kb() const { return find_mem_by_tag(kMemInactive); }
+    uint64_t mem_unevictable_kb() const { return find_mem_by_tag(kMemUnevictable); }
+    uint64_t mem_available_kb() const { return find_mem_by_tag(kMemAvailable); }
+    uint64_t mem_active_anon_kb() const { return find_mem_by_tag(kMemActiveAnon); }
+    uint64_t mem_inactive_anon_kb() const { return find_mem_by_tag(kMemInactiveAnon); }
+    uint64_t mem_active_file_kb() const { return find_mem_by_tag(kMemActiveFile); }
+    uint64_t mem_inactive_file_kb() const { return find_mem_by_tag(kMemInactiveFile); }
+    uint64_t mem_cma_total_kb() const { return find_mem_by_tag(kMemCmaTotal); }
+    uint64_t mem_cma_free_kb() const { return find_mem_by_tag(kMemCmaFree); }
+    uint64_t mem_zram_kb(const char* zram_dev = nullptr) const;
+    uint64_t mem_compacted_kb(const char* zram_dev = nullptr);
 
   private:
     std::map<std::string_view, uint64_t> mem_in_kb_;
-    bool MemZramDevice(const char* zram_dev, uint64_t* mem_zram_dev);
+    bool MemZramDevice(const char* zram_dev, uint64_t* mem_zram_dev) const;
+    bool GetTotalMemCompacted(const char* zram_dev, uint64_t* out_mem_compacted);
     bool ReadMemInfo(const char* path, size_t ntags, const std::string_view* tags,
                      std::function<void(std::string_view, uint64_t)> store_val);
+    // Convenience function to avoid duplicating code for each memory category.
+    uint64_t find_mem_by_tag(const char kTag[]) const {
+        auto it = mem_in_kb_.find(kTag);
+        if (it != mem_in_kb_.end()) {
+            return it->second;
+        }
+        return 0;
+    }
 };
 
 // Parse /proc/vmallocinfo and return total physical memory mapped
diff --git a/libdmabufinfo/TEST_MAPPING b/libdmabufinfo/TEST_MAPPING
index 1a8a5c6..29b7dfa 100644
--- a/libdmabufinfo/TEST_MAPPING
+++ b/libdmabufinfo/TEST_MAPPING
@@ -1,15 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "dmabufinfo_test",
-      "options": [
-        {
-          "include-filter": "*DmaBufSysfsStatsParser.*"
-        },
-        {
-          "include-filter": "*DmaBufProcessStatsTest.*"
-        }
-      ]
+      "name": "dmabufinfo_test"
     }
   ]
 }
diff --git a/libdmabufinfo/dmabuf_sysfs_stats.cpp b/libdmabufinfo/dmabuf_sysfs_stats.cpp
index e5a6486..38eb5c2 100644
--- a/libdmabufinfo/dmabuf_sysfs_stats.cpp
+++ b/libdmabufinfo/dmabuf_sysfs_stats.cpp
@@ -30,7 +30,7 @@
 namespace android {
 namespace dmabufinfo {
 
-static bool ReadUintFromFile(const std::string& path, unsigned int* val) {
+static bool ReadUintFromFile(const std::string& path, uint64_t* val) {
     std::string temp;
 
     if (!android::base::ReadFileToString(path, &temp)) {
@@ -52,6 +52,12 @@
     return android::base::ReadFileToString(exporter_path, exporter);
 }
 
+bool ReadBufferSize(unsigned int inode, uint64_t* size, const std::string& dmabuf_sysfs_path) {
+    std::string size_path =
+            ::android::base::StringPrintf("%s/%u/size", dmabuf_sysfs_path.c_str(), inode);
+    return ReadUintFromFile(size_path, size);
+}
+
 bool GetDmabufSysfsStats(DmabufSysfsStats* stats, const std::string& dmabuf_sysfs_stats_path) {
     std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(dmabuf_sysfs_stats_path.c_str()), closedir);
 
@@ -130,7 +136,7 @@
                 "%s/%s", dmabuf_sysfs_stats_path.c_str(), dent->d_name);
 
         // Read size of the buffer
-        unsigned int buf_size = 0;
+        uint64_t buf_size = 0;
         std::string size_path = buf_entry_path + "/size";
         if (!ReadUintFromFile(size_path, &buf_size)) return false;
         *total_exported += buf_size;
diff --git a/libdmabufinfo/dmabufinfo.cpp b/libdmabufinfo/dmabufinfo.cpp
index cb639c1..ff532b0 100644
--- a/libdmabufinfo/dmabufinfo.cpp
+++ b/libdmabufinfo/dmabufinfo.cpp
@@ -23,6 +23,7 @@
 #include <unistd.h>
 
 #include <filesystem>
+#include <fstream>
 #include <memory>
 #include <string>
 #include <vector>
@@ -55,8 +56,8 @@
                              const std::string& procfs_path) {
     std::string fdinfo =
             ::android::base::StringPrintf("%s/%d/fdinfo/%d", procfs_path.c_str(), pid, fd);
-    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fdinfo.c_str(), "re"), fclose};
-    if (fp == nullptr) {
+    std::ifstream fp(fdinfo);
+    if (!fp) {
         if (errno == ENOENT) {
             return NOT_FOUND;
         }
@@ -64,45 +65,43 @@
         return ERROR;
     }
 
-    char* line = nullptr;
-    size_t len = 0;
-    while (getline(&line, &len, fp.get()) > 0) {
+    for (std::string file_line; getline(fp, file_line);) {
+        const char* line = file_line.c_str();
         switch (line[0]) {
             case 'c':
                 if (strncmp(line, "count:", 6) == 0) {
-                    char* c = line + 6;
+                    const char* c = line + 6;
                     *count = strtoull(c, nullptr, 10);
                 }
                 break;
             case 'e':
                 if (strncmp(line, "exp_name:", 9) == 0) {
-                    char* c = line + 9;
+                    const char* c = line + 9;
                     *exporter = ::android::base::Trim(c);
                     *is_dmabuf_file = true;
                 }
                 break;
             case 'n':
                 if (strncmp(line, "name:", 5) == 0) {
-                    char* c = line + 5;
-                    *name = ::android::base::Trim(std::string(c));
+                    const char* c = line + 5;
+                    *name = ::android::base::Trim(c);
                 }
                 break;
             case 's':
                 if (strncmp(line, "size:", 5) == 0) {
-                    char* c = line + 5;
+                    const char* c = line + 5;
                     *size = strtoull(c, nullptr, 10);
                 }
                 break;
             case 'i':
                 if (strncmp(line, "ino:", 4) == 0) {
-                    char* c = line + 4;
+                    const char* c = line + 4;
                     *inode = strtoull(c, nullptr, 10);
                 }
                 break;
         }
     }
 
-    free(line);
     return OK;
 }
 
@@ -203,15 +202,12 @@
                               const std::string& procfs_path,
                               const std::string& dmabuf_sysfs_path) {
     std::string mapspath = ::android::base::StringPrintf("%s/%d/maps", procfs_path.c_str(), pid);
-    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mapspath.c_str(), "re"), fclose};
-    if (fp == nullptr) {
-        LOG(ERROR) << "Failed to open maps for pid: " << pid;
+    std::ifstream fp(mapspath);
+    if (!fp) {
+        LOG(ERROR) << "Failed to open " << mapspath << " for pid: " << pid;
         return false;
     }
 
-    char* line = nullptr;
-    size_t len = 0;
-
     // Process the map if it is dmabuf. Add map reference to existing object in 'dmabufs'
     // if it was already found. If it wasn't create a new one and append it to 'dmabufs'
     auto account_dmabuf = [&](const android::procinfo::MapInfo& mapinfo) {
@@ -231,56 +227,32 @@
         // We have a new buffer, but unknown count and name and exporter name
         // Try to lookup exporter name in sysfs
         std::string exporter;
-        if (!ReadBufferExporter(mapinfo.inode, &exporter, dmabuf_sysfs_path)) {
+        bool sysfs_stats = ReadBufferExporter(mapinfo.inode, &exporter, dmabuf_sysfs_path);
+        if (!sysfs_stats) {
             exporter = "<unknown>";
         }
-        DmaBuffer& dbuf = dmabufs->emplace_back(mapinfo.inode, mapinfo.end - mapinfo.start, 0,
-                                                exporter, "<unknown>");
+
+        // Using the VMA range as the size of the buffer can be misleading,
+        // due to partially mapped buffers or VMAs that extend beyond the
+        // buffer size.
+        //
+        // Attempt to retrieve the real buffer size from sysfs.
+        uint64_t size = 0;
+        if (!sysfs_stats || !ReadBufferSize(mapinfo.inode, &size, dmabuf_sysfs_path)) {
+            size = mapinfo.end - mapinfo.start;
+        }
+
+        DmaBuffer& dbuf = dmabufs->emplace_back(mapinfo.inode, size, 0, exporter, "<unknown>");
         dbuf.AddMapRef(pid);
     };
 
-    while (getline(&line, &len, fp.get()) > 0) {
-        if (!::android::procinfo::ReadMapFileContent(line, account_dmabuf)) {
-            LOG(ERROR) << "Failed to parse maps for pid: " << pid;
+    for (std::string line; getline(fp, line);) {
+        if (!::android::procinfo::ReadMapFileContent(line.data(), account_dmabuf)) {
+            LOG(ERROR) << "Failed to parse " << mapspath << " for pid: " << pid;
             return false;
         }
     }
 
-    free(line);
-    return true;
-}
-
-bool ReadDmaBufInfo(std::vector<DmaBuffer>* dmabufs, const std::string& path) {
-    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
-    if (fp == nullptr) {
-        LOG(ERROR) << "Failed to open dmabuf info from debugfs";
-        return false;
-    }
-
-    char* line = nullptr;
-    size_t len = 0;
-    dmabufs->clear();
-    while (getline(&line, &len, fp.get()) > 0) {
-        // The new dmabuf bufinfo format adds inode number and a name at the end
-        // We are looking for lines as follows:
-        // size     flags       mode        count  exp_name ino         name
-        // 01048576 00000002    00000007    00000001    ion 00018758    CAMERA
-        // 01048576 00000002    00000007    00000001    ion 00018758
-        uint64_t size, count, inode;
-        char* exporter_name = nullptr;
-        char* name = nullptr;
-        int matched = sscanf(line, "%" SCNu64 "%*x %*x %" SCNu64 " %ms %" SCNu64 " %ms", &size,
-                             &count, &exporter_name, &inode, &name);
-        if (matched < 4) {
-            continue;
-        }
-        dmabufs->emplace_back((ino_t)inode, size, count, exporter_name, matched > 4 ? name : "");
-        free(exporter_name);
-        free(name);
-    }
-
-    free(line);
-
     return true;
 }
 
@@ -302,7 +274,7 @@
     return true;
 }
 
-bool ReadDmaBufs(std::vector<DmaBuffer>* bufs) {
+bool ReadProcfsDmaBufs(std::vector<DmaBuffer>* bufs) {
     bufs->clear();
 
     std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir);
diff --git a/libdmabufinfo/dmabufinfo_test.cpp b/libdmabufinfo/dmabufinfo_test.cpp
index c7f6581..02a8b17 100644
--- a/libdmabufinfo/dmabufinfo_test.cpp
+++ b/libdmabufinfo/dmabufinfo_test.cpp
@@ -21,6 +21,7 @@
 #include <sys/mman.h>
 #include <sys/types.h>
 
+#include <filesystem>
 #include <fstream>
 #include <string>
 #include <unordered_map>
@@ -191,45 +192,6 @@
         EXPECT_EQ((_ref != _maprefs.end()), _expect);                        \
     } while (0)
 
-TEST(DmaBufInfoParser, TestReadDmaBufInfo) {
-    std::string bufinfo = R"bufinfo(00045056    00000002    00000007    00000002    ion 00022069    
-	Attached Devices:
-Total 0 devices attached
-01048576    00000002    00000007    00000001    ion 00019834    CAMERA
-	Attached Devices:
-	soc:qcom,cam_smmu:msm_cam_smmu_icp
-Total 1 devices attached)bufinfo";
-
-    TemporaryFile tf;
-    ASSERT_TRUE(tf.fd != -1);
-    ASSERT_TRUE(::android::base::WriteStringToFd(bufinfo, tf.fd));
-    std::string path = std::string(tf.path);
-
-    std::vector<DmaBuffer> dmabufs;
-    EXPECT_TRUE(ReadDmaBufInfo(&dmabufs, path));
-
-    EXPECT_EQ(dmabufs.size(), 2UL);
-
-    EXPECT_EQ(dmabufs[0].size(), 45056UL);
-    EXPECT_EQ(dmabufs[0].inode(), 22069UL);
-    EXPECT_EQ(dmabufs[0].count(), 2UL);
-    EXPECT_EQ(dmabufs[0].exporter(), "ion");
-    EXPECT_TRUE(dmabufs[0].name().empty());
-    EXPECT_EQ(dmabufs[0].total_refs(), 0ULL);
-    EXPECT_TRUE(dmabufs[0].fdrefs().empty());
-    EXPECT_TRUE(dmabufs[0].maprefs().empty());
-
-    EXPECT_EQ(dmabufs[1].size(), 1048576UL);
-    EXPECT_EQ(dmabufs[1].inode(), 19834UL);
-    EXPECT_EQ(dmabufs[1].count(), 1UL);
-    EXPECT_EQ(dmabufs[1].exporter(), "ion");
-    EXPECT_FALSE(dmabufs[1].name().empty());
-    EXPECT_EQ(dmabufs[1].name(), "CAMERA");
-    EXPECT_EQ(dmabufs[1].total_refs(), 0ULL);
-    EXPECT_TRUE(dmabufs[1].fdrefs().empty());
-    EXPECT_TRUE(dmabufs[1].maprefs().empty());
-}
-
 class DmaBufSysfsStatsParser : public ::testing::Test {
   public:
     virtual void SetUp() {
@@ -440,7 +402,7 @@
     AddMapEntries(map_entries);
 
     AddSysfsDmaBufStats(2, 1024, 2);  // Dmabuf 1
-    AddSysfsDmaBufStats(3, 1024, 1);  // Dmabuf 2
+    AddSysfsDmaBufStats(3, 2048, 1);  // Dmabuf 2
 
     std::vector<DmaBuffer> dmabufs;
     ASSERT_TRUE(ReadDmaBufMapRefs(pid, &dmabufs, procfs_path, dmabuf_sysfs_path));
@@ -490,6 +452,13 @@
 
     bool is_valid() { return (ion_fd >= 0 && ion_heap_mask > 0); }
 
+    bool is_using_dmabuf_heaps() {
+        // We can verify that a device is running on dmabuf-heaps by checking that
+        // the `dev/ion` is missing, while `dev/dma_heap` is present.
+        // https://source.android.com/docs/core/architecture/kernel/dma-buf-heaps
+        return !fs::is_directory("/dev/ion") && fs::is_directory("/dev/dma_heap");
+    }
+
     unique_fd allocate(uint64_t size, const std::string& name) {
         int fd;
         int err = ion_alloc_fd(ion_fd, size, 0, ion_heap_mask, 0, &fd);
@@ -570,6 +539,11 @@
 TEST_F(DmaBufTester, TestFdRef) {
     // Test if a dma buffer is found while the corresponding file descriptor
     // is open
+
+    if (is_using_dmabuf_heaps()) {
+        GTEST_SKIP();
+    }
+
     ASSERT_TRUE(is_valid());
     pid_t pid = getpid();
     std::vector<DmaBuffer> dmabufs;
@@ -594,6 +568,11 @@
 TEST_F(DmaBufTester, TestMapRef) {
     // Test to make sure we can find a buffer if the fd is closed but the buffer
     // is mapped
+
+    if (is_using_dmabuf_heaps()) {
+        GTEST_SKIP();
+    }
+
     ASSERT_TRUE(is_valid());
     pid_t pid = getpid();
     std::vector<DmaBuffer> dmabufs;
@@ -635,6 +614,10 @@
     // Each time a shared buffer is received over a socket, the remote process
     // will take an extra reference on it.
 
+    if (is_using_dmabuf_heaps()) {
+        GTEST_SKIP();
+    }
+
     ASSERT_TRUE(is_valid());
 
     pid_t pid = getpid();
@@ -678,6 +661,10 @@
     // dup()ing an fd will make this process take an extra reference on the
     // shared buffer.
 
+    if (is_using_dmabuf_heaps()) {
+        GTEST_SKIP();
+    }
+
     ASSERT_TRUE(is_valid());
 
     pid_t pid = getpid();
@@ -708,6 +695,11 @@
 TEST_F(DmaBufTester, ForkTest) {
     // fork()ing a child will cause the child to automatically take a reference
     // on any existing shared buffers.
+
+    if (is_using_dmabuf_heaps()) {
+        GTEST_SKIP();
+    }
+
     ASSERT_TRUE(is_valid());
 
     pid_t pid = getpid();
diff --git a/libdmabufinfo/include/dmabufinfo/dmabuf_sysfs_stats.h b/libdmabufinfo/include/dmabufinfo/dmabuf_sysfs_stats.h
index a7c0841..e09764d 100644
--- a/libdmabufinfo/include/dmabufinfo/dmabuf_sysfs_stats.h
+++ b/libdmabufinfo/include/dmabufinfo/dmabuf_sysfs_stats.h
@@ -30,9 +30,9 @@
  * @size: Size of the buffer.
  */
 struct DmabufInfo {
-    unsigned int inode;
+    unsigned long inode;
     std::string exp_name;
-    unsigned int size;
+    uint64_t size;
 };
 
 struct DmabufTotal {
@@ -84,5 +84,9 @@
 bool ReadBufferExporter(unsigned int inode, std::string* exporter,
                         const std::string& dmabuf_sysfs_path = "/sys/kernel/dmabuf/buffers");
 
+/* Reads the size of the DMA buffer with @inode */
+bool ReadBufferSize(unsigned int inode, uint64_t* size,
+                    const std::string& dmabuf_sysfs_path = "/sys/kernel/dmabuf/buffers");
+
 }  // namespace dmabufinfo
 }  // namespace android
diff --git a/libdmabufinfo/include/dmabufinfo/dmabufinfo.h b/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
index bb4aff1..9b5c945 100644
--- a/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
+++ b/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
@@ -62,7 +62,7 @@
     void SetName(const std::string& name) { name_ = name; }
     void SetExporter(const std::string& exporter) { exporter_ = exporter; }
     void SetCount(uint64_t count) { count_ = count; }
-    uint64_t Pss(pid_t pid) const { return maprefs_.count(pid) > 0 ? size_ / maprefs_.size() : 0; }
+    uint64_t Pss() const { return size_ / pids_.size(); }
 
     bool operator==(const DmaBuffer& rhs) {
         return (inode_ == rhs.inode()) && (size_ == rhs.size()) && (name_ == rhs.name()) &&
@@ -88,13 +88,6 @@
     }
 };
 
-// Read and return current dma buf objects from
-// DEBUGFS/dma_buf/bufinfo. The references to each dma buffer are not
-// populated here and will return an empty vector.
-// Returns false if something went wrong with the function, true otherwise.
-bool ReadDmaBufInfo(std::vector<DmaBuffer>* dmabufs,
-                    const std::string& path = "/sys/kernel/debug/dma_buf/bufinfo");
-
 // Read and return dmabuf objects for a given process without the help
 // of DEBUGFS
 // Returns false if something went wrong with the function, true otherwise.
@@ -130,7 +123,7 @@
 // Writes DmaBuffer info into an existing vector (which will be cleared first.)
 // Will include all DmaBuffers, whether thay are retained or mapped.
 // Returns true on success, otherwise false.
-bool ReadDmaBufs(std::vector<DmaBuffer>* bufs);
+bool ReadProcfsDmaBufs(std::vector<DmaBuffer>* bufs);
 
 }  // namespace dmabufinfo
 }  // namespace android
diff --git a/libdmabufinfo/tools/dmabuf_dump.cpp b/libdmabufinfo/tools/dmabuf_dump.cpp
index 65ebd08..8798539 100644
--- a/libdmabufinfo/tools/dmabuf_dump.cpp
+++ b/libdmabufinfo/tools/dmabuf_dump.cpp
@@ -135,16 +135,12 @@
 
     // Create a reverse map from pid to dmabufs
     std::unordered_map<pid_t, std::set<ino_t>> pid_to_inodes = {};
-    uint64_t total_size = 0;  // Total size of dmabufs in the system
-    uint64_t kernel_rss = 0;  // Total size of dmabufs NOT mapped or opened by a process
+    uint64_t userspace_size = 0;  // Size of userspace dmabufs in the system
     for (auto& buf : bufs) {
         for (auto pid : buf.pids()) {
             pid_to_inodes[pid].insert(buf.inode());
         }
-        total_size += buf.size();
-        if (buf.fdrefs().empty() && buf.maprefs().empty()) {
-            kernel_rss += buf.size();
-        }
+        userspace_size += buf.size();
     }
     // Create an inode to dmabuf map. We know inodes are unique..
     std::unordered_map<ino_t, DmaBuffer> inode_to_dmabuf;
@@ -161,12 +157,11 @@
         printf("%22s %16s %16s %16s %16s\n", "Name", "Rss", "Pss", "nr_procs", "Inode");
         for (auto& inode : inodes) {
             DmaBuffer& buf = inode_to_dmabuf[inode];
-            uint64_t proc_pss = buf.Pss(pid);
             printf("%22s %13" PRIu64 " kB %13" PRIu64 " kB %16zu %16" PRIuMAX "\n",
                    buf.name().empty() ? "<unknown>" : buf.name().c_str(), buf.size() / 1024,
-                   proc_pss / 1024, buf.pids().size(), static_cast<uintmax_t>(buf.inode()));
+                   buf.Pss() / 1024, buf.pids().size(), static_cast<uintmax_t>(buf.inode()));
             rss += buf.size();
-            pss += proc_pss;
+            pss += buf.Pss();
         }
         printf("%22s %13" PRIu64 " kB %13" PRIu64 " kB %16s\n", "PROCESS TOTAL", rss / 1024,
                pss / 1024, "");
@@ -174,9 +169,21 @@
         total_rss += rss;
         total_pss += pss;
     }
+
+    uint64_t kernel_rss = 0;  // Total size of dmabufs NOT mapped or opened by a process
+    if (android::dmabufinfo::GetDmabufTotalExportedKb(&kernel_rss)) {
+        kernel_rss *= 1024;  // KiB -> bytes
+        if (kernel_rss >= userspace_size)
+            kernel_rss -= userspace_size;
+        else
+            printf("Warning: Total dmabufs < userspace dmabufs\n");
+    } else {
+        printf("Warning: Could not get total exported dmabufs. Kernel size will be 0.\n");
+    }
     printf("dmabuf total: %" PRIu64 " kB kernel_rss: %" PRIu64 " kB userspace_rss: %" PRIu64
            " kB userspace_pss: %" PRIu64 " kB\n ",
-           total_size / 1024, kernel_rss / 1024, total_rss / 1024, total_pss / 1024);
+           (userspace_size + kernel_rss) / 1024, kernel_rss / 1024, total_rss / 1024,
+           total_pss / 1024);
 }
 
 static void DumpDmabufSysfsStats() {
@@ -193,7 +200,7 @@
     printf("\n\n----------------------- DMA-BUF per-buffer stats -----------------------\n");
     printf("    Dmabuf Inode |     Size(bytes) |             Exporter Name             |\n");
     for (const auto& buf : buffer_stats) {
-        printf("%16u |%16u | %16s \n", buf.inode, buf.size, buf.exp_name.c_str());
+        printf("%16lu |%" PRIu64 " | %16s \n", buf.inode, buf.size, buf.exp_name.c_str());
     }
 
     printf("\n\n----------------------- DMA-BUF exporter stats -----------------------\n");
@@ -261,8 +268,8 @@
             exit(EXIT_FAILURE);
         }
     } else {
-        if (!ReadDmaBufs(&bufs)) {
-            fprintf(stderr, "Failed to ReadDmaBufs, check logcat for info\n");
+        if (!ReadProcfsDmaBufs(&bufs)) {
+            fprintf(stderr, "Failed to ReadProcfsDmaBufs, check logcat for info\n");
             exit(EXIT_FAILURE);
         }
     }
diff --git a/libmeminfo_test.cpp b/libmeminfo_test.cpp
index 20159bf..8b1fe59 100644
--- a/libmeminfo_test.cpp
+++ b/libmeminfo_test.cpp
@@ -136,6 +136,31 @@
     }
 }
 
+TEST(ProcMemInfo, MapsUsageFillInAll) {
+    ProcMemInfo proc_mem(pid);
+    const std::vector<Vma>& maps = proc_mem.MapsWithoutUsageStats();
+    EXPECT_FALSE(maps.empty());
+    for (auto& map : maps) {
+        ASSERT_EQ(0, map.usage.vss);
+        ASSERT_EQ(0, map.usage.rss);
+        ASSERT_EQ(0, map.usage.pss);
+        ASSERT_EQ(0, map.usage.uss);
+        ASSERT_EQ(0, map.usage.swap);
+        ASSERT_EQ(0, map.usage.swap_pss);
+        ASSERT_EQ(0, map.usage.private_clean);
+        ASSERT_EQ(0, map.usage.private_dirty);
+        ASSERT_EQ(0, map.usage.shared_clean);
+        ASSERT_EQ(0, map.usage.shared_dirty);
+    }
+    // GetUsageStats' non-default parameter get_wss is false by default in
+    // ProcMemInfo's constructor.
+    ASSERT_TRUE(proc_mem.GetUsageStats(false));
+    for (auto& map : maps) {
+        // Check that at least one usage stat was updated.
+        ASSERT_NE(0, map.usage.vss);
+    }
+}
+
 TEST(ProcMemInfo, PageMapPresent) {
     static constexpr size_t kNumPages = 20;
     size_t pagesize = getpagesize();
@@ -339,6 +364,154 @@
     EXPECT_EQ(pss, 19119);
 }
 
+TEST(ProcMemInfo, ForEachExistingVmaTest) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
+    ProcMemInfo proc_mem(pid);
+    // Populate maps_.
+    proc_mem.Smaps(path);
+    std::vector<Vma> vmas;
+    auto collect_vmas = [&](const Vma& v) { vmas.push_back(v); };
+    EXPECT_TRUE(proc_mem.ForEachExistingVma(collect_vmas));
+
+    // The size of vmas is not checked because Smaps() will return 5 vmas on x86
+    // and 6 vmas otherwise, as [vsyscall] is not processed on x86.
+
+    // Expect values to be equal to what we have in testdata1/smaps_short
+    // Check for names
+    EXPECT_EQ(vmas[0].name, "[anon:dalvik-zygote-jit-code-cache]");
+    EXPECT_EQ(vmas[1].name, "/system/framework/x86_64/boot-framework.art");
+    EXPECT_TRUE(vmas[2].name == "[anon:libc_malloc]" ||
+                android::base::StartsWith(vmas[2].name, "[anon:scudo:"))
+            << "Unknown map name " << vmas[2].name;
+    EXPECT_EQ(vmas[3].name, "/system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex");
+    EXPECT_EQ(vmas[4].name, "/system/lib64/libhwui.so");
+
+    // Check start address
+    EXPECT_EQ(vmas[0].start, 0x54c00000);
+    EXPECT_EQ(vmas[1].start, 0x701ea000);
+    EXPECT_EQ(vmas[2].start, 0x70074dd8d000);
+    EXPECT_EQ(vmas[3].start, 0x700755a2d000);
+    EXPECT_EQ(vmas[4].start, 0x7007f85b0000);
+
+    // Check end address
+    EXPECT_EQ(vmas[0].end, 0x56c00000);
+    EXPECT_EQ(vmas[1].end, 0x70cdb000);
+    EXPECT_EQ(vmas[2].end, 0x70074ee0d000);
+    EXPECT_EQ(vmas[3].end, 0x700755a6e000);
+    EXPECT_EQ(vmas[4].end, 0x7007f8b9b000);
+
+    // Check Flags
+    EXPECT_EQ(vmas[0].flags, PROT_READ | PROT_EXEC);
+    EXPECT_EQ(vmas[1].flags, PROT_READ | PROT_WRITE);
+    EXPECT_EQ(vmas[2].flags, PROT_READ | PROT_WRITE);
+    EXPECT_EQ(vmas[3].flags, PROT_READ | PROT_EXEC);
+    EXPECT_EQ(vmas[4].flags, PROT_READ | PROT_EXEC);
+
+    // Check Shared
+    EXPECT_FALSE(vmas[0].is_shared);
+    EXPECT_FALSE(vmas[1].is_shared);
+    EXPECT_FALSE(vmas[2].is_shared);
+    EXPECT_FALSE(vmas[3].is_shared);
+    EXPECT_FALSE(vmas[4].is_shared);
+
+    // Check Offset
+    EXPECT_EQ(vmas[0].offset, 0x0);
+    EXPECT_EQ(vmas[1].offset, 0x0);
+    EXPECT_EQ(vmas[2].offset, 0x0);
+    EXPECT_EQ(vmas[3].offset, 0x00016000);
+    EXPECT_EQ(vmas[4].offset, 0x001ee000);
+
+    // Check Inode
+    EXPECT_EQ(vmas[0].inode, 0);
+    EXPECT_EQ(vmas[1].inode, 3165);
+    EXPECT_EQ(vmas[2].inode, 0);
+    EXPECT_EQ(vmas[3].inode, 1947);
+    EXPECT_EQ(vmas[4].inode, 1537);
+
+    // Check smaps specific fields
+    ASSERT_EQ(vmas[0].usage.vss, 32768);
+    EXPECT_EQ(vmas[1].usage.vss, 11204);
+    EXPECT_EQ(vmas[2].usage.vss, 16896);
+    EXPECT_EQ(vmas[3].usage.vss, 260);
+    EXPECT_EQ(vmas[4].usage.vss, 6060);
+
+    EXPECT_EQ(vmas[0].usage.rss, 2048);
+    EXPECT_EQ(vmas[1].usage.rss, 11188);
+    EXPECT_EQ(vmas[2].usage.rss, 15272);
+    EXPECT_EQ(vmas[3].usage.rss, 260);
+    EXPECT_EQ(vmas[4].usage.rss, 4132);
+
+    EXPECT_EQ(vmas[0].usage.pss, 113);
+    EXPECT_EQ(vmas[1].usage.pss, 2200);
+    EXPECT_EQ(vmas[2].usage.pss, 15272);
+    EXPECT_EQ(vmas[3].usage.pss, 260);
+    EXPECT_EQ(vmas[4].usage.pss, 1274);
+
+    EXPECT_EQ(vmas[0].usage.uss, 0);
+    EXPECT_EQ(vmas[1].usage.uss, 1660);
+    EXPECT_EQ(vmas[2].usage.uss, 15272);
+    EXPECT_EQ(vmas[3].usage.uss, 260);
+    EXPECT_EQ(vmas[4].usage.uss, 0);
+
+    EXPECT_EQ(vmas[0].usage.private_clean, 0);
+    EXPECT_EQ(vmas[1].usage.private_clean, 0);
+    EXPECT_EQ(vmas[2].usage.private_clean, 0);
+    EXPECT_EQ(vmas[3].usage.private_clean, 260);
+    EXPECT_EQ(vmas[4].usage.private_clean, 0);
+
+    EXPECT_EQ(vmas[0].usage.private_dirty, 0);
+    EXPECT_EQ(vmas[1].usage.private_dirty, 1660);
+    EXPECT_EQ(vmas[2].usage.private_dirty, 15272);
+    EXPECT_EQ(vmas[3].usage.private_dirty, 0);
+    EXPECT_EQ(vmas[4].usage.private_dirty, 0);
+
+    EXPECT_EQ(vmas[0].usage.shared_clean, 0);
+    EXPECT_EQ(vmas[1].usage.shared_clean, 80);
+    EXPECT_EQ(vmas[2].usage.shared_clean, 0);
+    EXPECT_EQ(vmas[3].usage.shared_clean, 0);
+    EXPECT_EQ(vmas[4].usage.shared_clean, 4132);
+
+    EXPECT_EQ(vmas[0].usage.shared_dirty, 2048);
+    EXPECT_EQ(vmas[1].usage.shared_dirty, 9448);
+    EXPECT_EQ(vmas[2].usage.shared_dirty, 0);
+    EXPECT_EQ(vmas[3].usage.shared_dirty, 0);
+    EXPECT_EQ(vmas[4].usage.shared_dirty, 0);
+
+    EXPECT_EQ(vmas[0].usage.swap, 0);
+    EXPECT_EQ(vmas[1].usage.swap, 0);
+    EXPECT_EQ(vmas[2].usage.swap, 0);
+    EXPECT_EQ(vmas[3].usage.swap, 0);
+    EXPECT_EQ(vmas[4].usage.swap, 0);
+
+    EXPECT_EQ(vmas[0].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[1].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[2].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[3].usage.swap_pss, 0);
+    EXPECT_EQ(vmas[4].usage.swap_pss, 0);
+
+#ifndef __x86_64__
+    // vmas[5] will not exist on x86, as [vsyscall] would not be processed.
+    EXPECT_EQ(vmas[5].name, "[vsyscall]");
+    EXPECT_EQ(vmas[5].start, 0xffffffffff600000);
+    EXPECT_EQ(vmas[5].end, 0xffffffffff601000);
+    EXPECT_EQ(vmas[5].flags, PROT_READ | PROT_EXEC);
+    EXPECT_FALSE(vmas[5].is_shared);
+    EXPECT_EQ(vmas[5].offset, 0x0);
+    EXPECT_EQ(vmas[5].inode, 0);
+    EXPECT_EQ(vmas[5].usage.vss, 4);
+    EXPECT_EQ(vmas[5].usage.rss, 0);
+    EXPECT_EQ(vmas[5].usage.pss, 0);
+    EXPECT_EQ(vmas[5].usage.uss, 0);
+    EXPECT_EQ(vmas[5].usage.private_clean, 0);
+    EXPECT_EQ(vmas[5].usage.private_dirty, 0);
+    EXPECT_EQ(vmas[5].usage.shared_clean, 0);
+    EXPECT_EQ(vmas[5].usage.shared_dirty, 0);
+    EXPECT_EQ(vmas[5].usage.swap, 0);
+    EXPECT_EQ(vmas[5].usage.swap_pss, 0);
+#endif
+}
+
 TEST(ProcMemInfo, ForEachVmaFromFile_SmapsTest) {
     // Parse smaps file correctly to make callbacks for each virtual memory area (vma)
     std::string exec_dir = ::android::base::GetExecutableDirectory();
@@ -683,6 +856,30 @@
 #endif
 }
 
+TEST(ProcMemInfo, SmapsPopulatesUsageTest) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
+    ProcMemInfo proc_mem(pid);
+    auto vmas = proc_mem.Smaps(path, true);
+
+    // Expect values to be equal to sums of usage in testdata1/smaps_short. For
+    // this data, only vss differs on x86.
+#ifndef __x86_64__
+    EXPECT_EQ(proc_mem.Usage().vss, 67192);
+#else
+    EXPECT_EQ(proc_mem.Usage().vss, 67188);
+#endif
+    EXPECT_EQ(proc_mem.Usage().rss, 32900);
+    EXPECT_EQ(proc_mem.Usage().pss, 19119);
+    EXPECT_EQ(proc_mem.Usage().uss, 17192);
+    EXPECT_EQ(proc_mem.Usage().private_clean, 260);
+    EXPECT_EQ(proc_mem.Usage().private_dirty, 16932);
+    EXPECT_EQ(proc_mem.Usage().shared_clean, 4212);
+    EXPECT_EQ(proc_mem.Usage().shared_dirty, 11496);
+    EXPECT_EQ(proc_mem.Usage().swap, 0);
+    EXPECT_EQ(proc_mem.Usage().swap_pss, 0);
+}
+
 TEST(SysMemInfo, TestSysMemInfoFile) {
     std::string meminfo = R"meminfo(MemTotal:        3019740 kB
 MemFree:         1809728 kB
@@ -754,6 +951,13 @@
     EXPECT_EQ(mi.mem_active_kb(), 445856);
     EXPECT_EQ(mi.mem_inactive_kb(), 459092);
     EXPECT_EQ(mi.mem_unevictable_kb(), 3096);
+    EXPECT_EQ(mi.mem_available_kb(), 2546560);
+    EXPECT_EQ(mi.mem_active_anon_kb(), 78492);
+    EXPECT_EQ(mi.mem_inactive_anon_kb(), 2240);
+    EXPECT_EQ(mi.mem_active_file_kb(), 367364);
+    EXPECT_EQ(mi.mem_inactive_file_kb(), 456852);
+    EXPECT_EQ(mi.mem_cma_total_kb(), 131072);
+    EXPECT_EQ(mi.mem_cma_free_kb(), 130380);
 }
 
 TEST(SysMemInfo, TestEmptyFile) {
@@ -798,6 +1002,13 @@
     MEMINFO_ACTIVE,
     MEMINFO_INACTIVE,
     MEMINFO_UNEVICTABLE,
+    MEMINFO_AVAILABLE,
+    MEMINFO_ACTIVE_ANON,
+    MEMINFO_INACTIVE_ANON,
+    MEMINFO_ACTIVE_FILE,
+    MEMINFO_INACTIVE_FILE,
+    MEMINFO_CMA_TOTAL,
+    MEMINFO_CMA_FREE,
     MEMINFO_COUNT
 };
 
@@ -880,6 +1091,13 @@
     EXPECT_EQ(mem[MEMINFO_ACTIVE], 445856);
     EXPECT_EQ(mem[MEMINFO_INACTIVE], 459092);
     EXPECT_EQ(mem[MEMINFO_UNEVICTABLE], 3096);
+    EXPECT_EQ(mem[MEMINFO_AVAILABLE], 2546560);
+    EXPECT_EQ(mem[MEMINFO_ACTIVE_ANON], 78492);
+    EXPECT_EQ(mem[MEMINFO_INACTIVE_ANON], 2240);
+    EXPECT_EQ(mem[MEMINFO_ACTIVE_FILE], 367364);
+    EXPECT_EQ(mem[MEMINFO_INACTIVE_FILE], 456852);
+    EXPECT_EQ(mem[MEMINFO_CMA_TOTAL], 131072);
+    EXPECT_EQ(mem[MEMINFO_CMA_FREE], 130380);
 }
 
 TEST(SysMemInfo, TestVmallocInfoNoMemory) {
diff --git a/libsmapinfo/Android.bp b/libsmapinfo/Android.bp
new file mode 100644
index 0000000..e421014
--- /dev/null
+++ b/libsmapinfo/Android.bp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2022 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+    name: "smapinfo_defaults",
+    vendor_available: true,
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "libmeminfo",
+        "libprocinfo",
+    ],
+}
+
+cc_library_shared {
+    name: "libsmapinfo",
+    host_supported: true,
+    defaults: ["smapinfo_defaults"],
+    export_include_dirs: ["include"],
+    srcs: ["processrecord.cpp",
+           "smapinfo.cpp"],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
diff --git a/libsmapinfo/include/processrecord.h b/libsmapinfo/include/processrecord.h
new file mode 100644
index 0000000..e40cd43
--- /dev/null
+++ b/libsmapinfo/include/processrecord.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <cstdint>
+#include <functional>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include <meminfo/meminfo.h>
+#include <meminfo/procmeminfo.h>
+
+namespace android {
+namespace smapinfo {
+
+class ProcessRecord final {
+  public:
+    ProcessRecord(pid_t pid, bool get_wss, uint64_t pgflags, uint64_t pgflags_mask,
+                  bool get_cmdline, bool get_oomadj, std::ostream& err);
+
+    bool valid() const;
+    void CalculateSwap(const std::vector<uint16_t>& swap_offset_array,
+                       float zram_compression_ratio);
+
+    // Getters
+    pid_t pid() const { return pid_; }
+    const std::string& cmdline() const { return cmdline_; }
+    int32_t oomadj() const { return oomadj_; }
+    uint64_t proportional_swap() const { return proportional_swap_; }
+    uint64_t unique_swap() const { return unique_swap_; }
+    uint64_t zswap() const { return zswap_; }
+
+    // Wrappers to ProcMemInfo
+    const std::vector<uint64_t>& SwapOffsets() const { return swap_offsets_; }
+    // show_wss may be used to return differentiated output in the future.
+    const ::android::meminfo::MemUsage& Usage([[maybe_unused]] bool show_wss) const {
+        return usage_or_wss_;
+    }
+    const std::vector<::android::meminfo::Vma>& Smaps() { return procmem_.Smaps(); }
+    bool ForEachExistingVma(const ::android::meminfo::VmaCallback& callback) {
+        return procmem_.ForEachExistingVma(callback);
+    }
+
+  private:
+    ::android::meminfo::ProcMemInfo procmem_;
+    pid_t pid_;
+    std::string cmdline_;
+    int32_t oomadj_;
+    uint64_t proportional_swap_;
+    uint64_t unique_swap_;
+    uint64_t zswap_;
+    ::android::meminfo::MemUsage usage_or_wss_;
+    std::vector<uint64_t> swap_offsets_;
+};
+
+}  // namespace smapinfo
+}  // namespace android
diff --git a/libsmapinfo/include/smapinfo.h b/libsmapinfo/include/smapinfo.h
new file mode 100644
index 0000000..59edd7f
--- /dev/null
+++ b/libsmapinfo/include/smapinfo.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <map>
+#include <ostream>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <meminfo/procmeminfo.h>
+#include <processrecord.h>
+
+namespace android {
+namespace smapinfo {
+
+// The user-specified order to sort processes.
+enum class SortOrder { BY_PSS = 0, BY_RSS, BY_USS, BY_VSS, BY_SWAP, BY_OOMADJ };
+
+// Populates the input set with all pids present in the /proc directory. Only
+// returns false if /proc could not be opened, returns true otherwise.
+bool get_all_pids(std::set<pid_t>* pids);
+
+// Sorts processes provided in 'pids' by memory usage (or oomadj score) and
+// prints them. Returns false in the following failure cases:
+// a) system memory information could not be read,
+// b) swap offsets could not be counted for some process,
+// c) reset_wss is true but the working set for some process could not be reset.
+bool run_procrank(uint64_t pgflags, uint64_t pgflags_mask, const std::set<pid_t>& pids,
+                  bool get_oomadj, bool get_wss, SortOrder sort_order, bool reverse_sort,
+                  std::map<pid_t, ProcessRecord>* processrecords_ptr, std::ostream& out,
+                  std::ostream& err);
+
+// Sorts libraries used by processes in 'pids' by memory usage and prints them.
+// Returns false if any process's usage info could not be read.
+bool run_librank(uint64_t pgflags, uint64_t pgflags_mask, const std::set<pid_t>& pids,
+                 const std::string& lib_prefix, bool all_libs,
+                 const std::vector<std::string>& excluded_libs, uint16_t mapflags_mask,
+                 android::meminfo::Format format, SortOrder sort_order, bool reverse_sort,
+                 std::map<pid_t, ProcessRecord>* processrecords_ptr, std::ostream& out,
+                 std::ostream& err);
+
+// Retrieves showmap information from the provided pid (or file) and prints it.
+// Returns false if there are no maps associated with 'pid' or if the file
+// denoted by 'filename' is malformed.
+bool run_showmap(pid_t pid, const std::string& filename, bool terse, bool verbose, bool show_addr,
+                 bool quiet, android::meminfo::Format format,
+                 std::map<pid_t, ProcessRecord>* processrecords_ptr, std::ostream& out,
+                 std::ostream& err);
+
+// Runs procrank, librank, and showmap with a single read of smaps. Default
+// arguments are used for all tools (except quiet output for showmap). This
+// prints output that is specifically meant to be included in bug reports.
+// Returns false only in the case that /proc could not be opened.
+bool run_bugreport_procdump(std::ostream& out, std::ostream& err);
+
+}  // namespace smapinfo
+}  // namespace android
diff --git a/libsmapinfo/processrecord.cpp b/libsmapinfo/processrecord.cpp
new file mode 100644
index 0000000..615cfb8
--- /dev/null
+++ b/libsmapinfo/processrecord.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 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 <inttypes.h>
+#include <linux/oom.h>
+#include <stdlib.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <meminfo/procmeminfo.h>
+
+#include <processrecord.h>
+
+namespace android {
+namespace smapinfo {
+
+using ::android::base::StringPrintf;
+using ::android::meminfo::MemUsage;
+using ::android::meminfo::ProcMemInfo;
+using ::android::meminfo::Vma;
+using ::android::meminfo::VmaCallback;
+
+ProcessRecord::ProcessRecord(pid_t pid, bool get_wss, uint64_t pgflags, uint64_t pgflags_mask,
+                             bool get_cmdline, bool get_oomadj, std::ostream& err)
+    : procmem_(pid, get_wss, pgflags, pgflags_mask),
+      pid_(-1),
+      oomadj_(OOM_SCORE_ADJ_MAX + 1),
+      proportional_swap_(0),
+      unique_swap_(0),
+      zswap_(0) {
+    // cmdline_ only needs to be populated if this record will be used by procrank/librank.
+    if (get_cmdline) {
+        std::string fname = StringPrintf("/proc/%d/cmdline", pid);
+        if (!::android::base::ReadFileToString(fname, &cmdline_)) {
+            err << "Failed to read cmdline from: " << fname << "\n";
+            cmdline_ = "<unknown>";
+        }
+        // We deliberately don't read the /proc/<pid>/cmdline file directly into 'cmdline_'
+        // because some processes have cmdlines that end with "0x00 0x0A 0x00",
+        // e.g. xtra-daemon, lowi-server.
+        // The .c_str() assignment takes care of trimming the cmdline at the first 0x00. This is
+        // how the original procrank worked (luckily).
+        cmdline_.resize(strlen(cmdline_.c_str()));
+
+        // If there is no cmdline (empty, not <unknown>), a kernel thread will have comm. This only
+        // matters for bug reports, which output 'SHOW MAP <pid>: <cmdline>' as section titles.
+        if (cmdline_.empty()) {
+            fname = StringPrintf("/proc/%d/comm", pid);
+            if (!::android::base::ReadFileToString(fname, &cmdline_)) {
+                err << "Failed to read comm from: " << fname << "\n";
+            }
+            // comm seems to contain a trailing '\n' that isn't present in cmdline. dumpstate
+            // surrounds kernel thread names with brackets; this behavior is maintained here.
+            if (auto pos = cmdline_.find_last_of('\n'); pos != std::string::npos) {
+                cmdline_.erase(pos);
+            }
+            cmdline_ = StringPrintf("[%s]", cmdline_.c_str());
+        }
+    }
+
+    // oomadj_ only needs to be populated if this record will be used by procrank/librank.
+    if (get_oomadj) {
+        std::string fname = StringPrintf("/proc/%d/oom_score_adj", pid);
+        std::string oom_score;
+        if (!::android::base::ReadFileToString(fname, &oom_score)) {
+            err << "Failed to read oom_score_adj file: " << fname << "\n";
+            return;
+        }
+        if (!::android::base::ParseInt(::android::base::Trim(oom_score), &oomadj_)) {
+            err << "Failed to parse oomadj from: " << fname << "\n";
+            return;
+        }
+    }
+
+    // We want to use Smaps() to populate procmem_'s maps before calling Wss() or Usage(), as
+    // these will fall back on the slower ReadMaps().
+    //
+    // This Smaps() call is temporarily disabled because it results in
+    // procmem_'s swap_offsets_ not being populated, causing procrank to not
+    // report PSwap/USwap/ZSwap. Wss() and Usage() both fall back to ReadMaps(),
+    // which will populate swap_offsets_.
+    //
+    // procmem_.Smaps("", true);
+    usage_or_wss_ = get_wss ? procmem_.Wss() : procmem_.Usage();
+    swap_offsets_ = procmem_.SwapOffsets();
+    pid_ = pid;
+}
+
+bool ProcessRecord::valid() const {
+    return pid_ != -1;
+}
+
+void ProcessRecord::CalculateSwap(const std::vector<uint16_t>& swap_offset_array,
+                                  float zram_compression_ratio) {
+    for (auto& off : swap_offsets_) {
+        proportional_swap_ += getpagesize() / swap_offset_array[off];
+        unique_swap_ += swap_offset_array[off] == 1 ? getpagesize() : 0;
+        zswap_ = proportional_swap_ * zram_compression_ratio;
+    }
+    // This is divided by 1024 to convert to KB.
+    proportional_swap_ /= 1024;
+    unique_swap_ /= 1024;
+    zswap_ /= 1024;
+}
+
+}  // namespace smapinfo
+}  // namespace android
diff --git a/libsmapinfo/smapinfo.cpp b/libsmapinfo/smapinfo.cpp
new file mode 100644
index 0000000..2c2c0f6
--- /dev/null
+++ b/libsmapinfo/smapinfo.cpp
@@ -0,0 +1,1274 @@
+/*
+ * Copyright (C) 2022 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 <inttypes.h>
+#include <linux/oom.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <iomanip>
+#include <iostream>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <meminfo/sysmeminfo.h>
+
+#include <processrecord.h>
+#include <smapinfo.h>
+
+namespace android {
+namespace smapinfo {
+
+using ::android::base::StringPrintf;
+using ::android::meminfo::EscapeCsvString;
+using ::android::meminfo::EscapeJsonString;
+using ::android::meminfo::Format;
+using ::android::meminfo::MemUsage;
+using ::android::meminfo::Vma;
+
+bool get_all_pids(std::set<pid_t>* pids) {
+    pids->clear();
+    std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
+    if (!procdir) return false;
+
+    struct dirent* dir;
+    pid_t pid;
+    while ((dir = readdir(procdir.get()))) {
+        if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
+        pids->insert(pid);
+    }
+    return true;
+}
+
+namespace procrank {
+
+static bool count_swap_offsets(const ProcessRecord& proc, std::vector<uint16_t>& swap_offset_array,
+                               std::ostream& err) {
+    const std::vector<uint64_t>& swp_offs = proc.SwapOffsets();
+    for (auto& off : swp_offs) {
+        if (off >= swap_offset_array.size()) {
+            err << "swap offset " << off << " is out of bounds for process: " << proc.pid() << "\n";
+            return false;
+        }
+        if (swap_offset_array[off] == USHRT_MAX) {
+            err << "swap offset " << off << " ref count overflow in process: " << proc.pid()
+                << "\n";
+            return false;
+        }
+        swap_offset_array[off]++;
+    }
+    return true;
+}
+
+struct params {
+    // Calculated total memory usage across all processes in the system.
+    uint64_t total_pss;
+    uint64_t total_uss;
+    uint64_t total_swap;
+    uint64_t total_pswap;
+    uint64_t total_uswap;
+    uint64_t total_zswap;
+
+    // Print options.
+    bool show_oomadj;
+    bool show_wss;
+    bool swap_enabled;
+    bool zram_enabled;
+
+    // If zram is enabled, the compression ratio is zram used / swap used.
+    float zram_compression_ratio;
+};
+
+static std::function<bool(ProcessRecord& a, ProcessRecord& b)> select_sort(struct params* params,
+                                                                           SortOrder sort_order) {
+    // Create sort function based on sort_order.
+    std::function<bool(ProcessRecord & a, ProcessRecord & b)> proc_sort;
+    switch (sort_order) {
+        case (SortOrder::BY_OOMADJ):
+            proc_sort = [](ProcessRecord& a, ProcessRecord& b) { return a.oomadj() > b.oomadj(); };
+            break;
+        case (SortOrder::BY_RSS):
+            proc_sort = [=](ProcessRecord& a, ProcessRecord& b) {
+                return a.Usage(params->show_wss).rss > b.Usage(params->show_wss).rss;
+            };
+            break;
+        case (SortOrder::BY_SWAP):
+            proc_sort = [=](ProcessRecord& a, ProcessRecord& b) {
+                return a.Usage(params->show_wss).swap > b.Usage(params->show_wss).swap;
+            };
+            break;
+        case (SortOrder::BY_USS):
+            proc_sort = [=](ProcessRecord& a, ProcessRecord& b) {
+                return a.Usage(params->show_wss).uss > b.Usage(params->show_wss).uss;
+            };
+            break;
+        case (SortOrder::BY_VSS):
+            proc_sort = [=](ProcessRecord& a, ProcessRecord& b) {
+                return a.Usage(params->show_wss).vss > b.Usage(params->show_wss).vss;
+            };
+            break;
+        case (SortOrder::BY_PSS):
+        default:
+            proc_sort = [=](ProcessRecord& a, ProcessRecord& b) {
+                return a.Usage(params->show_wss).pss > b.Usage(params->show_wss).pss;
+            };
+            break;
+    }
+    return proc_sort;
+}
+
+static bool populate_procs(struct params* params, uint64_t pgflags, uint64_t pgflags_mask,
+                           std::vector<uint16_t>& swap_offset_array, const std::set<pid_t>& pids,
+                           std::vector<ProcessRecord>* procs,
+                           std::map<pid_t, ProcessRecord>* processrecords_ptr, std::ostream& err) {
+    // Fall back to using an empty map of ProcessRecords if nullptr was passed in.
+    std::map<pid_t, ProcessRecord> processrecords;
+    if (!processrecords_ptr) {
+        processrecords_ptr = &processrecords;
+    }
+    // Mark each swap offset used by the process as we find them for calculating
+    // proportional swap usage later.
+    for (pid_t pid : pids) {
+        // Check if a ProcessRecord already exists for this pid, create one if one does not exist.
+        auto iter = processrecords_ptr->find(pid);
+        ProcessRecord& proc =
+                (iter != processrecords_ptr->end())
+                        ? iter->second
+                        : processrecords_ptr
+                                  ->emplace(pid, ProcessRecord(pid, params->show_wss, pgflags,
+                                                               pgflags_mask, true,
+                                                               params->show_oomadj, err))
+                                  .first->second;
+
+        if (!proc.valid()) {
+            // Check to see if the process is still around, skip the process if the proc
+            // directory is inaccessible. It was most likely killed while creating the process
+            // record.
+            std::string procdir = StringPrintf("/proc/%d", pid);
+            if (access(procdir.c_str(), F_OK | R_OK)) continue;
+
+            // Warn if we failed to gather process stats even while it is still alive.
+            // Return success here, so we continue to print stats for other processes.
+            err << "warning: failed to create process record for: " << pid << "\n";
+            continue;
+        }
+
+        // Skip processes with no memory mappings.
+        uint64_t vss = proc.Usage(params->show_wss).vss;
+        if (vss == 0) continue;
+
+        // Collect swap_offset counts from all processes in 1st pass.
+        if (!params->show_wss && params->swap_enabled &&
+            !count_swap_offsets(proc, swap_offset_array, err)) {
+            err << "Failed to count swap offsets for process: " << pid << "\n";
+            err << "Failed to read all pids from the system\n";
+            return false;
+        }
+
+        procs->push_back(proc);
+    }
+    return true;
+}
+
+static void print_header(struct params* params, std::ostream& out) {
+    out << StringPrintf("%5s  ", "PID");
+    if (params->show_oomadj) {
+        out << StringPrintf("%5s  ", "oom");
+    }
+
+    if (params->show_wss) {
+        out << StringPrintf("%7s  %7s  %7s  ", "WRss", "WPss", "WUss");
+    } else {
+        // Swap statistics here, as working set pages by definition shouldn't end up in swap.
+        out << StringPrintf("%8s  %7s  %7s  %7s  ", "Vss", "Rss", "Pss", "Uss");
+        if (params->swap_enabled) {
+            out << StringPrintf("%7s  %7s  %7s  ", "Swap", "PSwap", "USwap");
+            if (params->zram_enabled) {
+                out << StringPrintf("%7s  ", "ZSwap");
+            }
+        }
+    }
+
+    out << "cmdline\n";
+}
+
+static void print_divider(struct params* params, std::ostream& out) {
+    out << StringPrintf("%5s  ", "");
+    if (params->show_oomadj) {
+        out << StringPrintf("%5s  ", "");
+    }
+
+    if (params->show_wss) {
+        out << StringPrintf("%7s  %7s  %7s  ", "", "------", "------");
+    } else {
+        out << StringPrintf("%8s  %7s  %7s  %7s  ", "", "", "------", "------");
+        if (params->swap_enabled) {
+            out << StringPrintf("%7s  %7s  %7s  ", "------", "------", "------");
+            if (params->zram_enabled) {
+                out << StringPrintf("%7s  ", "------");
+            }
+        }
+    }
+
+    out << StringPrintf("%s\n", "------");
+}
+
+static void print_processrecord(struct params* params, ProcessRecord& proc, std::ostream& out) {
+    out << StringPrintf("%5d  ", proc.pid());
+    if (params->show_oomadj) {
+        out << StringPrintf("%5d  ", proc.oomadj());
+    }
+
+    if (params->show_wss) {
+        out << StringPrintf("%6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ",
+                            proc.Usage(params->show_wss).rss, proc.Usage(params->show_wss).pss,
+                            proc.Usage(params->show_wss).uss);
+    } else {
+        out << StringPrintf("%7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ",
+                            proc.Usage(params->show_wss).vss, proc.Usage(params->show_wss).rss,
+                            proc.Usage(params->show_wss).pss, proc.Usage(params->show_wss).uss);
+        if (params->swap_enabled) {
+            out << StringPrintf("%6" PRIu64 "K  ", proc.Usage(params->show_wss).swap);
+            out << StringPrintf("%6" PRIu64 "K  ", proc.proportional_swap());
+            out << StringPrintf("%6" PRIu64 "K  ", proc.unique_swap());
+            if (params->zram_enabled) {
+                out << StringPrintf("%6" PRIu64 "K  ", proc.zswap());
+            }
+        }
+    }
+    out << proc.cmdline() << "\n";
+}
+
+static void print_totals(struct params* params, std::ostream& out) {
+    out << StringPrintf("%5s  ", "");
+    if (params->show_oomadj) {
+        out << StringPrintf("%5s  ", "");
+    }
+
+    if (params->show_wss) {
+        out << StringPrintf("%7s  %6" PRIu64 "K  %6" PRIu64 "K  ", "", params->total_pss,
+                            params->total_uss);
+    } else {
+        out << StringPrintf("%8s  %7s  %6" PRIu64 "K  %6" PRIu64 "K  ", "", "", params->total_pss,
+                            params->total_uss);
+        if (params->swap_enabled) {
+            out << StringPrintf("%6" PRIu64 "K  ", params->total_swap);
+            out << StringPrintf("%6" PRIu64 "K  ", params->total_pswap);
+            out << StringPrintf("%6" PRIu64 "K  ", params->total_uswap);
+            if (params->zram_enabled) {
+                out << StringPrintf("%6" PRIu64 "K  ", params->total_zswap);
+            }
+        }
+    }
+    out << "TOTAL\n\n";
+}
+
+static void print_sysmeminfo(struct params* params, const ::android::meminfo::SysMemInfo& smi,
+                             std::ostream& out) {
+    if (params->swap_enabled) {
+        out << StringPrintf("ZRAM: %" PRIu64 "K physical used for %" PRIu64 "K in swap (%" PRIu64
+                            "K total swap)\n",
+                            smi.mem_zram_kb(), (smi.mem_swap_kb() - smi.mem_swap_free_kb()),
+                            smi.mem_swap_kb());
+    }
+
+    out << StringPrintf(" RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64
+                        "K buffers, %" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64 "K slab\n",
+                        smi.mem_total_kb(), smi.mem_free_kb(), smi.mem_buffers_kb(),
+                        smi.mem_cached_kb(), smi.mem_shmem_kb(), smi.mem_slab_kb());
+}
+
+static void add_to_totals(struct params* params, ProcessRecord& proc,
+                          const std::vector<uint16_t>& swap_offset_array) {
+    params->total_pss += proc.Usage(params->show_wss).pss;
+    params->total_uss += proc.Usage(params->show_wss).uss;
+    if (!params->show_wss && params->swap_enabled) {
+        proc.CalculateSwap(swap_offset_array, params->zram_compression_ratio);
+        params->total_swap += proc.Usage(params->show_wss).swap;
+        params->total_pswap += proc.proportional_swap();
+        params->total_uswap += proc.unique_swap();
+        if (params->zram_enabled) {
+            params->total_zswap += proc.zswap();
+        }
+    }
+}
+
+}  // namespace procrank
+
+bool run_procrank(uint64_t pgflags, uint64_t pgflags_mask, const std::set<pid_t>& pids,
+                  bool get_oomadj, bool get_wss, SortOrder sort_order, bool reverse_sort,
+                  std::map<pid_t, ProcessRecord>* processrecords_ptr, std::ostream& out,
+                  std::ostream& err) {
+    ::android::meminfo::SysMemInfo smi;
+    if (!smi.ReadMemInfo()) {
+        err << "Failed to get system memory info\n";
+        return false;
+    }
+
+    struct procrank::params params = {
+            .total_pss = 0,
+            .total_uss = 0,
+            .total_swap = 0,
+            .total_pswap = 0,
+            .total_uswap = 0,
+            .total_zswap = 0,
+            .show_oomadj = get_oomadj,
+            .show_wss = get_wss,
+            .swap_enabled = false,
+            .zram_enabled = false,
+            .zram_compression_ratio = 0.0,
+    };
+
+    // Figure out swap and zram.
+    uint64_t swap_total = smi.mem_swap_kb() * 1024;
+    params.swap_enabled = swap_total > 0;
+    // Allocate the swap array.
+    std::vector<uint16_t> swap_offset_array(swap_total / getpagesize() + 1, 0);
+    if (params.swap_enabled) {
+        params.zram_enabled = smi.mem_zram_kb() > 0;
+        if (params.zram_enabled) {
+            params.zram_compression_ratio = static_cast<float>(smi.mem_zram_kb()) /
+                                            (smi.mem_swap_kb() - smi.mem_swap_free_kb());
+        }
+    }
+
+    std::vector<ProcessRecord> procs;
+    if (!procrank::populate_procs(&params, pgflags, pgflags_mask, swap_offset_array, pids, &procs,
+                                  processrecords_ptr, err)) {
+        return false;
+    }
+
+    if (procs.empty()) {
+        // This would happen in corner cases where procrank is being run to find KSM usage on a
+        // system with no KSM and combined with working set determination as follows
+        //   procrank -w -u -k
+        //   procrank -w -s -k
+        //   procrank -w -o -k
+        out << "<empty>\n\n";
+        procrank::print_sysmeminfo(&params, smi, out);
+        return true;
+    }
+
+    // Create sort function based on sort_order, default is PSS descending.
+    std::function<bool(ProcessRecord & a, ProcessRecord & b)> proc_sort =
+            procrank::select_sort(&params, sort_order);
+
+    // Sort all process records, default is PSS descending.
+    if (reverse_sort) {
+        std::sort(procs.rbegin(), procs.rend(), proc_sort);
+    } else {
+        std::sort(procs.begin(), procs.end(), proc_sort);
+    }
+
+    procrank::print_header(&params, out);
+
+    for (auto& proc : procs) {
+        procrank::add_to_totals(&params, proc, swap_offset_array);
+        procrank::print_processrecord(&params, proc, out);
+    }
+
+    procrank::print_divider(&params, out);
+    procrank::print_totals(&params, out);
+    procrank::print_sysmeminfo(&params, smi, out);
+
+    return true;
+}
+
+namespace librank {
+
+static void add_mem_usage(MemUsage* to, const MemUsage& from) {
+    to->vss += from.vss;
+    to->rss += from.rss;
+    to->pss += from.pss;
+    to->uss += from.uss;
+
+    to->swap += from.swap;
+
+    to->private_clean += from.private_clean;
+    to->private_dirty += from.private_dirty;
+    to->shared_clean += from.shared_clean;
+    to->shared_dirty += from.shared_dirty;
+}
+
+// Represents a specific process's usage of a library.
+struct LibProcRecord {
+  public:
+    LibProcRecord(ProcessRecord& proc) : pid_(-1), oomadj_(OOM_SCORE_ADJ_MAX + 1) {
+        pid_ = proc.pid();
+        cmdline_ = proc.cmdline();
+        oomadj_ = proc.oomadj();
+        usage_.clear();
+    }
+
+    bool valid() const { return pid_ != -1; }
+    void AddUsage(const MemUsage& mem_usage) { add_mem_usage(&usage_, mem_usage); }
+
+    // Getters
+    pid_t pid() const { return pid_; }
+    const std::string& cmdline() const { return cmdline_; }
+    int32_t oomadj() const { return oomadj_; }
+    const MemUsage& usage() const { return usage_; }
+
+  private:
+    pid_t pid_;
+    std::string cmdline_;
+    int32_t oomadj_;
+    MemUsage usage_;
+};
+
+// Represents all processes' usage of a specific library.
+struct LibRecord {
+  public:
+    LibRecord(const std::string& name) : name_(name) {}
+
+    void AddUsage(const LibProcRecord& proc, const MemUsage& mem_usage) {
+        auto [it, inserted] = procs_.insert(std::pair<pid_t, LibProcRecord>(proc.pid(), proc));
+        // Adds to proc's PID's contribution to usage of this lib, as well as total lib usage.
+        it->second.AddUsage(mem_usage);
+        add_mem_usage(&usage_, mem_usage);
+    }
+    uint64_t pss() const { return usage_.pss; }
+
+    // Getters
+    const std::string& name() const { return name_; }
+    const std::map<pid_t, LibProcRecord>& processes() const { return procs_; }
+
+  private:
+    std::string name_;
+    MemUsage usage_;
+    std::map<pid_t, LibProcRecord> procs_;
+};
+
+static std::function<bool(LibProcRecord& a, LibProcRecord& b)> select_sort(SortOrder sort_order) {
+    // Create sort function based on sort_order.
+    std::function<bool(LibProcRecord & a, LibProcRecord & b)> proc_sort;
+    switch (sort_order) {
+        case (SortOrder::BY_RSS):
+            proc_sort = [](LibProcRecord& a, LibProcRecord& b) {
+                return a.usage().rss > b.usage().rss;
+            };
+            break;
+        case (SortOrder::BY_USS):
+            proc_sort = [](LibProcRecord& a, LibProcRecord& b) {
+                return a.usage().uss > b.usage().uss;
+            };
+            break;
+        case (SortOrder::BY_VSS):
+            proc_sort = [](LibProcRecord& a, LibProcRecord& b) {
+                return a.usage().vss > b.usage().vss;
+            };
+            break;
+        case (SortOrder::BY_OOMADJ):
+            proc_sort = [](LibProcRecord& a, LibProcRecord& b) { return a.oomadj() > b.oomadj(); };
+            break;
+        case (SortOrder::BY_PSS):
+        default:
+            proc_sort = [](LibProcRecord& a, LibProcRecord& b) {
+                return a.usage().pss > b.usage().pss;
+            };
+            break;
+    }
+    return proc_sort;
+}
+
+struct params {
+    // Filtering options.
+    std::string lib_prefix;
+    bool all_libs;
+    const std::vector<std::string>& excluded_libs;
+    uint16_t mapflags_mask;
+
+    // Print options.
+    Format format;
+    bool swap_enabled;
+    bool show_oomadj;
+};
+
+static bool populate_libs(struct params* params, uint64_t pgflags, uint64_t pgflags_mask,
+                          const std::set<pid_t>& pids,
+                          std::map<std::string, LibRecord>& lib_name_map,
+                          std::map<pid_t, ProcessRecord>* processrecords_ptr, std::ostream& err) {
+    // Fall back to using an empty map of ProcessRecords if nullptr was passed in.
+    std::map<pid_t, ProcessRecord> processrecords;
+    if (!processrecords_ptr) {
+        processrecords_ptr = &processrecords;
+    }
+    for (pid_t pid : pids) {
+        // Check if a ProcessRecord already exists for this pid, create one if one does not exist.
+        auto iter = processrecords_ptr->find(pid);
+        ProcessRecord& proc =
+                (iter != processrecords_ptr->end())
+                        ? iter->second
+                        : processrecords_ptr
+                                  ->emplace(pid, ProcessRecord(pid, false, pgflags, pgflags_mask,
+                                                               true, params->show_oomadj, err))
+                                  .first->second;
+
+        if (!proc.valid()) {
+            err << "error: failed to create process record for: " << pid << "\n";
+            return false;
+        }
+
+        const std::vector<Vma>& maps = proc.Smaps();
+        if (maps.size() == 0) {
+            continue;
+        }
+
+        LibProcRecord record(proc);
+        for (const Vma& map : maps) {
+            // Skip library/map if the prefix for the path doesn't match.
+            if (!params->lib_prefix.empty() &&
+                !::android::base::StartsWith(map.name, params->lib_prefix)) {
+                continue;
+            }
+            // Skip excluded library/map names.
+            if (!params->all_libs &&
+                (std::find(params->excluded_libs.begin(), params->excluded_libs.end(), map.name) !=
+                 params->excluded_libs.end())) {
+                continue;
+            }
+            // Skip maps based on map permissions.
+            if (params->mapflags_mask &&
+                ((map.flags & (PROT_READ | PROT_WRITE | PROT_EXEC)) != params->mapflags_mask)) {
+                continue;
+            }
+
+            // Add memory for lib usage.
+            auto [it, inserted] = lib_name_map.emplace(map.name, LibRecord(map.name));
+            it->second.AddUsage(record, map.usage);
+
+            if (!params->swap_enabled && map.usage.swap) {
+                params->swap_enabled = true;
+            }
+        }
+    }
+    return true;
+}
+
+static void print_header(struct params* params, std::ostream& out) {
+    switch (params->format) {
+        case Format::RAW:
+            // clang-format off
+            out << std::setw(7) << "RSStot"
+                << std::setw(10) << "VSS"
+                << std::setw(9) << "RSS"
+                << std::setw(9) << "PSS"
+                << std::setw(9) << "USS"
+                << "  ";
+            //clang-format on
+            if (params->swap_enabled) {
+                out << std::setw(7) << "Swap"
+                    << "  ";
+            }
+            if (params->show_oomadj) {
+                out << std::setw(7) << "Oom"
+                    << "  ";
+            }
+            out << "Name/PID\n";
+            break;
+        case Format::CSV:
+            out << "\"Library\",\"Total_RSS\",\"Process\",\"PID\",\"VSS\",\"RSS\",\"PSS\",\"USS\"";
+            if (params->swap_enabled) {
+                out << ",\"Swap\"";
+            }
+            if (params->show_oomadj) {
+                out << ",\"Oomadj\"";
+            }
+            out << "\n";
+            break;
+        case Format::JSON:
+        default:
+            break;
+    }
+}
+
+static void print_library(struct params* params, const LibRecord& lib,
+                          std::ostream& out) {
+    if (params->format == Format::RAW) {
+        // clang-format off
+        out << std::setw(6) << lib.pss() << "K"
+            << std::setw(10) << ""
+            << std::setw(9) << ""
+            << std::setw(9) << ""
+            << std::setw(9) << ""
+            << "  ";
+        // clang-format on
+        if (params->swap_enabled) {
+            out << std::setw(7) << ""
+                << "  ";
+        }
+        if (params->show_oomadj) {
+            out << std::setw(7) << ""
+                << "  ";
+        }
+        out << lib.name() << "\n";
+    }
+}
+
+static void print_proc_as_raw(struct params* params, const LibProcRecord& p, std::ostream& out) {
+    const MemUsage& usage = p.usage();
+    // clang-format off
+    out << std::setw(7) << ""
+        << std::setw(9) << usage.vss << "K  "
+        << std::setw(6) << usage.rss << "K  "
+        << std::setw(6) << usage.pss << "K  "
+        << std::setw(6) << usage.uss << "K  ";
+    // clang-format on
+    if (params->swap_enabled) {
+        out << std::setw(6) << usage.swap << "K  ";
+    }
+    if (params->show_oomadj) {
+        out << std::setw(7) << p.oomadj() << "  ";
+    }
+    out << "  " << p.cmdline() << " [" << p.pid() << "]\n";
+}
+
+static void print_proc_as_json(struct params* params, const LibRecord& l, const LibProcRecord& p,
+                               std::ostream& out) {
+    const MemUsage& usage = p.usage();
+    // clang-format off
+    out << "{\"Library\":" << EscapeJsonString(l.name())
+        << ",\"Total_RSS\":" << l.pss()
+        << ",\"Process\":" << EscapeJsonString(p.cmdline())
+        << ",\"PID\":\"" << p.pid() << "\""
+        << ",\"VSS\":" << usage.vss
+        << ",\"RSS\":" << usage.rss
+        << ",\"PSS\":" << usage.pss
+        << ",\"USS\":" << usage.uss;
+    // clang-format on
+    if (params->swap_enabled) {
+        out << ",\"Swap\":" << usage.swap;
+    }
+    if (params->show_oomadj) {
+        out << ",\"Oom\":" << p.oomadj();
+    }
+    out << "}\n";
+}
+
+static void print_proc_as_csv(struct params* params, const LibRecord& l, const LibProcRecord& p,
+                              std::ostream& out) {
+    const MemUsage& usage = p.usage();
+    // clang-format off
+    out << EscapeCsvString(l.name())
+        << "," << l.pss()
+        << "," << EscapeCsvString(p.cmdline())
+        << ",\"[" << p.pid() << "]\""
+        << "," << usage.vss
+        << "," << usage.rss
+        << "," << usage.pss
+        << "," << usage.uss;
+    // clang-format on
+    if (params->swap_enabled) {
+        out << "," << usage.swap;
+    }
+    if (params->show_oomadj) {
+        out << "," << p.oomadj();
+    }
+    out << "\n";
+}
+
+static void print_procs(struct params* params, const LibRecord& lib,
+                        const std::vector<LibProcRecord>& procs, std::ostream& out) {
+    for (const LibProcRecord& p : procs) {
+        switch (params->format) {
+            case Format::RAW:
+                print_proc_as_raw(params, p, out);
+                break;
+            case Format::JSON:
+                print_proc_as_json(params, lib, p, out);
+                break;
+            case Format::CSV:
+                print_proc_as_csv(params, lib, p, out);
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+}  // namespace librank
+
+bool run_librank(uint64_t pgflags, uint64_t pgflags_mask, const std::set<pid_t>& pids,
+                 const std::string& lib_prefix, bool all_libs,
+                 const std::vector<std::string>& excluded_libs, uint16_t mapflags_mask,
+                 Format format, SortOrder sort_order, bool reverse_sort,
+                 std::map<pid_t, ProcessRecord>* processrecords_ptr, std::ostream& out,
+                 std::ostream& err) {
+    struct librank::params params = {
+            .lib_prefix = lib_prefix,
+            .all_libs = all_libs,
+            .excluded_libs = excluded_libs,
+            .mapflags_mask = mapflags_mask,
+            .format = format,
+            .swap_enabled = false,
+            .show_oomadj = (sort_order == SortOrder::BY_OOMADJ),
+    };
+
+    // Fills in usage info for each LibRecord.
+    std::map<std::string, librank::LibRecord> lib_name_map;
+    if (!librank::populate_libs(&params, pgflags, pgflags_mask, pids, lib_name_map,
+                                processrecords_ptr, err)) {
+        return false;
+    }
+
+    librank::print_header(&params, out);
+
+    // Create vector of all LibRecords, sorted by descending PSS.
+    std::vector<librank::LibRecord> libs;
+    libs.reserve(lib_name_map.size());
+    for (const auto& [k, v] : lib_name_map) {
+        libs.push_back(v);
+    }
+    std::sort(libs.begin(), libs.end(),
+              [](const librank::LibRecord& l1, const librank::LibRecord& l2) {
+                  return l1.pss() > l2.pss();
+              });
+
+    std::function<bool(librank::LibProcRecord & a, librank::LibProcRecord & b)> libproc_sort =
+            librank::select_sort(sort_order);
+    for (librank::LibRecord& lib : libs) {
+        // Sort all processes for this library, default is PSS-descending.
+        std::vector<librank::LibProcRecord> procs;
+        procs.reserve(lib.processes().size());
+        for (const auto& [k, v] : lib.processes()) {
+            procs.push_back(v);
+        }
+        if (reverse_sort) {
+            std::sort(procs.rbegin(), procs.rend(), libproc_sort);
+        } else {
+            std::sort(procs.begin(), procs.end(), libproc_sort);
+        }
+
+        librank::print_library(&params, lib, out);
+        librank::print_procs(&params, lib, procs, out);
+    }
+
+    return true;
+}
+
+namespace showmap {
+
+// These are defined as static variables instead of a struct (as in procrank::params and
+// librank::params) because the collect_vma callback references them.
+static bool show_addr;
+static bool verbose;
+
+static std::string get_vma_name(const Vma& vma, bool total, bool is_bss) {
+    if (total) {
+        return "TOTAL";
+    }
+    std::string vma_name = vma.name;
+    if (is_bss) {
+        vma_name.append(" [bss]");
+    }
+    return vma_name;
+}
+
+static std::string get_flags(const Vma& vma, bool total) {
+    std::string flags_str("---");
+    if (verbose && !total) {
+        if (vma.flags & PROT_READ) flags_str[0] = 'r';
+        if (vma.flags & PROT_WRITE) flags_str[1] = 'w';
+        if (vma.flags & PROT_EXEC) flags_str[2] = 'x';
+    }
+    return flags_str;
+}
+
+struct VmaInfo {
+    Vma vma;
+    bool is_bss;
+    uint32_t count;
+
+    VmaInfo() = default;
+    VmaInfo(const Vma& v) : vma(v), is_bss(false), count(1) {}
+    VmaInfo(const Vma& v, bool bss) : vma(v), is_bss(bss), count(1) {}
+    VmaInfo(const Vma& v, const std::string& name, bool bss) : vma(v), is_bss(bss), count(1) {
+        vma.name = name;
+    }
+
+    void to_raw(bool total, std::ostream& out) const;
+    void to_csv(bool total, std::ostream& out) const;
+    void to_json(bool total, std::ostream& out) const;
+};
+
+void VmaInfo::to_raw(bool total, std::ostream& out) const {
+    if (show_addr) {
+        if (total) {
+            out << "                                  ";
+        } else {
+            out << std::hex << std::setw(16) << vma.start << " " << std::setw(16) << vma.end << " "
+                << std::dec;
+        }
+    }
+    // clang-format off
+    out << std::setw(8) << vma.usage.vss << " "
+        << std::setw(8) << vma.usage.rss << " "
+        << std::setw(8) << vma.usage.pss << " "
+        << std::setw(8) << vma.usage.shared_clean << " "
+        << std::setw(8) << vma.usage.shared_dirty << " "
+        << std::setw(8) << vma.usage.private_clean << " "
+        << std::setw(8) << vma.usage.private_dirty << " "
+        << std::setw(8) << vma.usage.swap << " "
+        << std::setw(8) << vma.usage.swap_pss << " "
+        << std::setw(9) << vma.usage.anon_huge_pages << " "
+        << std::setw(9) << vma.usage.shmem_pmd_mapped << " "
+        << std::setw(9) << vma.usage.file_pmd_mapped << " "
+        << std::setw(8) << vma.usage.shared_hugetlb << " "
+        << std::setw(8) << vma.usage.private_hugetlb << " "
+        << std::setw(8) << vma.usage.locked << " ";
+    // clang-format on
+    if (!verbose && !show_addr) {
+        out << std::setw(4) << count << " ";
+    }
+    if (verbose) {
+        if (total) {
+            out << "      ";
+        } else {
+            out << std::setw(5) << get_flags(vma, total) << " ";
+        }
+    }
+    out << get_vma_name(vma, total, is_bss) << "\n";
+}
+
+void VmaInfo::to_csv(bool total, std::ostream& out) const {
+    // clang-format off
+    out << vma.usage.vss
+        << "," << vma.usage.rss
+        << "," << vma.usage.pss
+        << "," << vma.usage.shared_clean
+        << "," << vma.usage.shared_dirty
+        << "," << vma.usage.private_clean
+        << "," << vma.usage.private_dirty
+        << "," << vma.usage.swap
+        << "," << vma.usage.swap_pss
+        << "," << vma.usage.anon_huge_pages
+        << "," << vma.usage.shmem_pmd_mapped
+        << "," << vma.usage.file_pmd_mapped
+        << "," << vma.usage.shared_hugetlb
+        << "," << vma.usage.private_hugetlb
+        << "," << vma.usage.locked;
+    // clang-format on
+    if (show_addr) {
+        out << ",";
+        if (total) {
+            out << ",";
+        } else {
+            out << std::hex << vma.start << "," << vma.end << std::dec;
+        }
+    }
+    if (!verbose && !show_addr) {
+        out << "," << count;
+    }
+    if (verbose) {
+        out << ",";
+        if (!total) {
+            out << EscapeCsvString(get_flags(vma, total));
+        }
+    }
+    out << "," << EscapeCsvString(get_vma_name(vma, total, is_bss)) << "\n";
+}
+
+void VmaInfo::to_json(bool total, std::ostream& out) const {
+    // clang-format off
+    out << "{\"virtual size\":" << vma.usage.vss
+        << ",\"RSS\":" << vma.usage.rss
+        << ",\"PSS\":" << vma.usage.pss
+        << ",\"shared clean\":" << vma.usage.shared_clean
+        << ",\"shared dirty\":" << vma.usage.shared_dirty
+        << ",\"private clean\":" << vma.usage.private_clean
+        << ",\"private dirty\":" << vma.usage.private_dirty
+        << ",\"swap\":" << vma.usage.swap
+        << ",\"swapPSS\":" << vma.usage.swap_pss
+        << ",\"Anon HugePages\":" << vma.usage.anon_huge_pages
+        << ",\"Shmem PmdMapped\":" << vma.usage.shmem_pmd_mapped
+        << ",\"File PmdMapped\":" << vma.usage.file_pmd_mapped
+        << ",\"Shared Hugetlb\":" << vma.usage.shared_hugetlb
+        << ",\"Private Hugetlb\":" << vma.usage.private_hugetlb
+        << ",\"Locked\":" << vma.usage.locked;
+    // clang-format on
+    if (show_addr) {
+        if (total) {
+            out << ",\"start addr\":\"\",\"end addr\":\"\"";
+        } else {
+            out << ",\"start addr\":\"" << std::hex << vma.start << "\",\"end addr\":\"" << vma.end
+                << "\"" << std::dec;
+        }
+    }
+    if (!verbose && !show_addr) {
+        out << ",\"#\":" << count;
+    }
+    if (verbose) {
+        out << ",\"flags\":" << EscapeJsonString(get_flags(vma, total));
+    }
+    out << ",\"object\":" << EscapeJsonString(get_vma_name(vma, total, is_bss)) << "}";
+}
+
+static bool is_library(const std::string& name) {
+    return (name.size() > 4) && (name[0] == '/') && ::android::base::EndsWith(name, ".so");
+}
+
+static void infer_vma_name(VmaInfo& current, const VmaInfo& recent) {
+    if (current.vma.name.empty()) {
+        if (recent.vma.end == current.vma.start && is_library(recent.vma.name)) {
+            current.vma.name = recent.vma.name;
+            current.is_bss = true;
+        } else {
+            current.vma.name = "[anon]";
+        }
+    }
+}
+
+static void add_mem_usage(MemUsage* to, const MemUsage& from) {
+    to->vss += from.vss;
+    to->rss += from.rss;
+    to->pss += from.pss;
+
+    to->swap += from.swap;
+    to->swap_pss += from.swap_pss;
+
+    to->private_clean += from.private_clean;
+    to->private_dirty += from.private_dirty;
+    to->shared_clean += from.shared_clean;
+    to->shared_dirty += from.shared_dirty;
+
+    to->anon_huge_pages += from.anon_huge_pages;
+    to->shmem_pmd_mapped += from.shmem_pmd_mapped;
+    to->file_pmd_mapped += from.file_pmd_mapped;
+    to->shared_hugetlb += from.shared_hugetlb;
+    to->private_hugetlb += from.private_hugetlb;
+}
+
+// A multimap is used instead of a map to allow for duplicate keys in case verbose output is used.
+static std::multimap<std::string, VmaInfo> vmas;
+
+static void collect_vma(const Vma& vma) {
+    static VmaInfo recent;
+    VmaInfo current(vma);
+
+    std::string key;
+    if (show_addr) {
+        // vma.end is included in case vma.start is identical for two VMAs.
+        key = StringPrintf("%16" PRIx64 "%16" PRIx64, vma.start, vma.end);
+    } else {
+        key = vma.name;
+    }
+
+    if (vmas.empty()) {
+        vmas.emplace(key, current);
+        recent = current;
+        return;
+    }
+
+    infer_vma_name(current, recent);
+    recent = current;
+
+    // If sorting by address, the VMA can be placed into the map as-is.
+    if (show_addr) {
+        vmas.emplace(key, current);
+        return;
+    }
+
+    // infer_vma_name() may have changed current.vma.name, so this key needs to be set again before
+    // using it to sort by name. For verbose output, the VMA can immediately be placed into the map.
+    key = current.vma.name;
+    if (verbose) {
+        vmas.emplace(key, current);
+        return;
+    }
+
+    // Coalesces VMAs' usage by name, if !show_addr && !verbose.
+    auto iter = vmas.find(key);
+    if (iter == vmas.end()) {
+        vmas.emplace(key, current);
+        return;
+    }
+
+    VmaInfo& match = iter->second;
+    add_mem_usage(&match.vma.usage, current.vma.usage);
+    match.is_bss &= current.is_bss;
+}
+
+static void print_text_header(std::ostream& out) {
+    if (show_addr) {
+        out << "           start              end ";
+    }
+    out << " virtual                     shared   shared  private  private                   "
+           "Anon      Shmem     File      Shared   Private\n";
+    if (show_addr) {
+        out << "            addr             addr ";
+    }
+    out << "    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS "
+           "HugePages PmdMapped PmdMapped Hugetlb  Hugetlb    Locked ";
+    if (!verbose && !show_addr) {
+        out << "   # ";
+    }
+    if (verbose) {
+        out << "flags ";
+    }
+    out << "object\n";
+}
+
+static void print_text_divider(std::ostream& out) {
+    if (show_addr) {
+        out << "---------------- ---------------- ";
+    }
+    out << "-------- -------- -------- -------- -------- -------- -------- -------- -------- "
+           "--------- --------- --------- -------- -------- -------- ";
+    if (!verbose && !show_addr) {
+        out << "---- ";
+    }
+    if (verbose) {
+        out << "----- ";
+    }
+    out << "------------------------------\n";
+}
+
+static void print_csv_header(std::ostream& out) {
+    out << "\"virtual size\",\"RSS\",\"PSS\",\"shared clean\",\"shared dirty\",\"private clean\","
+           "\"private dirty\",\"swap\",\"swapPSS\",\"Anon HugePages\",\"Shmem PmdMapped\","
+           "\"File PmdMapped\",\"Shared Hugetlb\",\"Private Hugetlb\",\"Locked\"";
+    if (show_addr) {
+        out << ",\"start addr\",\"end addr\"";
+    }
+    if (!verbose && !show_addr) {
+        out << ",\"#\"";
+    }
+    if (verbose) {
+        out << ",\"flags\"";
+    }
+    out << ",\"object\"\n";
+}
+
+static void print_header(Format format, std::ostream& out) {
+    switch (format) {
+        case Format::RAW:
+            print_text_header(out);
+            print_text_divider(out);
+            break;
+        case Format::CSV:
+            print_csv_header(out);
+            break;
+        case Format::JSON:
+            out << "[";
+            break;
+        default:
+            break;
+    }
+}
+
+static void print_vmainfo(const VmaInfo& v, Format format, std::ostream& out) {
+    switch (format) {
+        case Format::RAW:
+            v.to_raw(false, out);
+            break;
+        case Format::CSV:
+            v.to_csv(false, out);
+            break;
+        case Format::JSON:
+            v.to_json(false, out);
+            out << ",";
+            break;
+        default:
+            break;
+    }
+}
+
+static void print_vmainfo_totals(const VmaInfo& total_usage, Format format, std::ostream& out) {
+    switch (format) {
+        case Format::RAW:
+            print_text_divider(out);
+            print_text_header(out);
+            print_text_divider(out);
+            total_usage.to_raw(true, out);
+            break;
+        case Format::CSV:
+            total_usage.to_csv(true, out);
+            break;
+        case Format::JSON:
+            total_usage.to_json(true, out);
+            out << "]\n";
+            break;
+        default:
+            break;
+    }
+}
+
+}  // namespace showmap
+
+bool run_showmap(pid_t pid, const std::string& filename, bool terse, bool verbose, bool show_addr,
+                 bool quiet, Format format, std::map<pid_t, ProcessRecord>* processrecords_ptr,
+                 std::ostream& out, std::ostream& err) {
+    // Accumulated vmas are cleared to account for sequential showmap calls by bugreport_procdump.
+    showmap::vmas.clear();
+
+    showmap::show_addr = show_addr;
+    showmap::verbose = verbose;
+
+    bool success;
+    if (!filename.empty()) {
+        success = ::android::meminfo::ForEachVmaFromFile(filename, showmap::collect_vma);
+    } else if (!processrecords_ptr) {
+        ProcessRecord proc(pid, false, 0, 0, false, false, err);
+        success = proc.ForEachExistingVma(showmap::collect_vma);
+    } else {
+        // Check if a ProcessRecord already exists for this pid, create one if one does not exist.
+        auto iter = processrecords_ptr->find(pid);
+        ProcessRecord& proc =
+                (iter != processrecords_ptr->end())
+                        ? iter->second
+                        : processrecords_ptr
+                                  ->emplace(pid, ProcessRecord(pid, false, 0, 0, false, false, err))
+                                  .first->second;
+        success = proc.ForEachExistingVma(showmap::collect_vma);
+    }
+
+    if (!success) {
+        if (!quiet) {
+            if (!filename.empty()) {
+                err << "Failed to parse file " << filename << "\n";
+            } else {
+                err << "No maps for pid " << pid << "\n";
+            }
+        }
+        return false;
+    }
+
+    showmap::print_header(format, out);
+
+    showmap::VmaInfo total_usage;
+    for (const auto& entry : showmap::vmas) {
+        const showmap::VmaInfo& v = entry.second;
+        showmap::add_mem_usage(&total_usage.vma.usage, v.vma.usage);
+        total_usage.count += v.count;
+        if (terse && !(v.vma.usage.private_dirty || v.vma.usage.private_clean)) {
+            continue;
+        }
+        showmap::print_vmainfo(v, format, out);
+    }
+    showmap::print_vmainfo_totals(total_usage, format, out);
+
+    return true;
+}
+
+namespace bugreport_procdump {
+
+static void create_processrecords(const std::set<pid_t>& pids,
+                                  std::map<pid_t, ProcessRecord>& processrecords,
+                                  std::ostream& err) {
+    for (pid_t pid : pids) {
+        ProcessRecord proc(pid, false, 0, 0, true, false, err);
+        if (!proc.valid()) {
+            err << "Could not create a ProcessRecord for pid " << pid << "\n";
+            continue;
+        }
+        processrecords.emplace(pid, std::move(proc));
+    }
+}
+
+static void print_section_start(const std::string& name, std::ostream& out) {
+    out << "------ " << name << " ------\n";
+}
+
+static void print_section_end(const std::string& name,
+                              const std::chrono::time_point<std::chrono::steady_clock>& start,
+                              std::ostream& out) {
+    // std::ratio<1> represents the period for one second.
+    using floatsecs = std::chrono::duration<float, std::ratio<1>>;
+    auto end = std::chrono::steady_clock::now();
+    std::streamsize precision = out.precision();
+    out << "------ " << std::setprecision(3) << std::fixed << floatsecs(end - start).count()
+        << " was the duration of '" << name << "' ------\n";
+    out << std::setprecision(precision) << std::defaultfloat;
+}
+
+static void call_smaps_of_all_processes(const std::string& filename, bool terse, bool verbose,
+                                        bool show_addr, bool quiet, Format format,
+                                        std::map<pid_t, ProcessRecord>& processrecords,
+                                        std::ostream& out, std::ostream& err) {
+    for (const auto& [pid, record] : processrecords) {
+        std::string showmap_title = StringPrintf("SHOW MAP %d: %s", pid, record.cmdline().c_str());
+
+        auto showmap_start = std::chrono::steady_clock::now();
+        print_section_start(showmap_title, out);
+        run_showmap(pid, filename, terse, verbose, show_addr, quiet, format, &processrecords, out,
+                    err);
+        print_section_end(showmap_title, showmap_start, out);
+    }
+}
+
+static void call_librank(const std::set<pid_t>& pids,
+                         std::map<pid_t, ProcessRecord>& processrecords, std::ostream& out,
+                         std::ostream& err) {
+    auto librank_start = std::chrono::steady_clock::now();
+    print_section_start("LIBRANK", out);
+    run_librank(0, 0, pids, "", false, {"[heap]", "[stack]"}, 0, Format::RAW, SortOrder::BY_PSS,
+                false, &processrecords, out, err);
+    print_section_end("LIBRANK", librank_start, out);
+}
+
+static void call_procrank(const std::set<pid_t>& pids,
+                          std::map<pid_t, ProcessRecord>& processrecords, std::ostream& out,
+                          std::ostream& err) {
+    auto procrank_start = std::chrono::steady_clock::now();
+    print_section_start("PROCRANK", out);
+    run_procrank(0, 0, pids, false, false, SortOrder::BY_PSS, false, &processrecords, out, err);
+    print_section_end("PROCRANK", procrank_start, out);
+}
+
+}  // namespace bugreport_procdump
+
+bool run_bugreport_procdump(std::ostream& out, std::ostream& err) {
+    std::set<pid_t> pids;
+    if (!::android::smapinfo::get_all_pids(&pids)) {
+        err << "Failed to get all pids.\n";
+        return false;
+    }
+
+    // create_processrecords is the only expensive call in this function, as showmap, librank, and
+    // procrank will only print already-collected information. This duration is captured by
+    // dumpstate in the BUGREPORT PROCDUMP section.
+    std::map<pid_t, ProcessRecord> processrecords;
+    bugreport_procdump::create_processrecords(pids, processrecords, err);
+
+    // pids without associated ProcessRecords are removed so that librank/procrank do not fall back
+    // to creating new ProcessRecords for them.
+    for (pid_t pid : pids) {
+        if (processrecords.find(pid) == processrecords.end()) {
+            pids.erase(pid);
+        }
+    }
+
+    auto all_smaps_start = std::chrono::steady_clock::now();
+    bugreport_procdump::print_section_start("SMAPS OF ALL PROCESSES", out);
+    bugreport_procdump::call_smaps_of_all_processes("", false, false, false, true, Format::RAW,
+                                                    processrecords, out, err);
+    bugreport_procdump::print_section_end("SMAPS OF ALL PROCESSES", all_smaps_start, out);
+
+    bugreport_procdump::call_librank(pids, processrecords, out, err);
+    bugreport_procdump::call_procrank(pids, processrecords, out, err);
+
+    return true;
+}
+
+}  // namespace smapinfo
+}  // namespace android
diff --git a/procmeminfo.cpp b/procmeminfo.cpp
index 13a17da..b0a0491 100644
--- a/procmeminfo.cpp
+++ b/procmeminfo.cpp
@@ -66,6 +66,27 @@
     to->shared_dirty += from.shared_dirty;
 }
 
+// Converts MemUsage stats from KB to B in case usage is expected in bytes.
+static void convert_usage_kb_to_b(MemUsage& usage) {
+    // These stats are only populated if /proc/<pid>/smaps is read, so they are excluded:
+    // swap_pss, anon_huge_pages, shmem_pmdmapped, file_pmd_mapped, shared_hugetlb, private_hugetlb.
+    constexpr int conversion_factor = 1024;
+    usage.vss *= conversion_factor;
+    usage.rss *= conversion_factor;
+    usage.pss *= conversion_factor;
+    usage.uss *= conversion_factor;
+
+    usage.swap *= conversion_factor;
+
+    usage.private_clean *= conversion_factor;
+    usage.private_dirty *= conversion_factor;
+
+    usage.shared_clean *= conversion_factor;
+    usage.shared_dirty *= conversion_factor;
+
+    usage.thp *= conversion_factor;
+}
+
 // Returns true if the line was valid smaps stats line false otherwise.
 static bool parse_smaps_field(const char* line, MemUsage* stats) {
     const char *end = line;
@@ -123,6 +144,11 @@
                     stats->file_pmd_mapped = strtoull(c, nullptr, 10);
                 }
                 break;
+            case 'L':
+                if (strncmp(line, "Locked:", 7) == 0) {
+                    stats->locked = strtoull(c, nullptr, 10);
+                }
+                break;
         }
         return true;
     }
@@ -167,7 +193,7 @@
     return maps_;
 }
 
-const std::vector<Vma>& ProcMemInfo::Smaps(const std::string& path) {
+const std::vector<Vma>& ProcMemInfo::Smaps(const std::string& path, bool collect_usage) {
     if (!maps_.empty()) {
         return maps_;
     }
@@ -176,6 +202,9 @@
         if (std::find(g_excluded_vmas.begin(), g_excluded_vmas.end(), vma.name) ==
                 g_excluded_vmas.end()) {
             maps_.emplace_back(vma);
+            if (collect_usage) {
+                add_mem_usage(&usage_, vma.usage);
+            }
         }
     };
     if (path.empty() && !ForEachVma(collect_vmas)) {
@@ -225,6 +254,16 @@
     return ForEachVmaFromFile(path, callback, use_smaps);
 }
 
+bool ProcMemInfo::ForEachExistingVma(const VmaCallback& callback) {
+    if (maps_.empty()) {
+        return false;
+    }
+    for (auto& vma : maps_) {
+        callback(vma);
+    }
+    return true;
+}
+
 bool ProcMemInfo::ForEachVmaFromMaps(const VmaCallback& callback) {
     Vma vma;
     auto vmaCollect = [&callback,&vma](const uint64_t start, uint64_t end, uint16_t flags,
@@ -244,6 +283,26 @@
     return success;
 }
 
+bool ProcMemInfo::ForEachVmaFromMaps(const VmaCallback& callback, std::string& mapsBuffer) {
+    Vma vma;
+    vma.name.reserve(256);
+    auto vmaCollect = [&callback,&vma](const uint64_t start, uint64_t end, uint16_t flags,
+                            uint64_t pgoff, ino_t inode, const char* name, bool shared) {
+        vma.start = start;
+        vma.end = end;
+        vma.flags = flags;
+        vma.offset = pgoff;
+        vma.name = name;
+        vma.inode = inode;
+        vma.is_shared = shared;
+        callback(vma);
+    };
+
+    bool success = ::android::procinfo::ReadProcessMaps(pid_, vmaCollect, mapsBuffer);
+
+    return success;
+}
+
 bool ProcMemInfo::SmapsOrRollup(MemUsage* stats) const {
     std::string path = ::android::base::StringPrintf(
             "/proc/%d/%s", pid_, IsSmapsRollupSupported() ? "smaps_rollup" : "smaps");
@@ -337,6 +396,14 @@
         return true;
     }
 
+    if (!GetUsageStats(get_wss, use_pageidle, swap_only)) {
+        maps_.clear();
+        return false;
+    }
+    return true;
+}
+
+bool ProcMemInfo::GetUsageStats(bool get_wss, bool use_pageidle, bool swap_only) {
     ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
     if (pagemap_fd == -1) {
         return false;
@@ -346,7 +413,6 @@
         if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss, use_pageidle, swap_only)) {
             LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
                        << vma.end << "]";
-            maps_.clear();
             return false;
         }
         add_mem_usage(&usage_, vma.usage);
@@ -355,7 +421,7 @@
     return true;
 }
 
-bool ProcMemInfo::FillInVmaStats(Vma& vma) {
+bool ProcMemInfo::FillInVmaStats(Vma& vma, bool use_kb) {
     ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
     if (pagemap_fd == -1) {
         return false;
@@ -366,6 +432,9 @@
                    << vma.end << "]";
         return false;
     }
+    if (!use_kb) {
+        convert_usage_kb_to_b(vma.usage);
+    }
     return true;
 }
 
@@ -377,9 +446,9 @@
         return false;
     }
 
-    uint64_t pagesz = getpagesize();
-    size_t num_pages = (vma.end - vma.start) / pagesz;
-    size_t first_page = vma.start / pagesz;
+    uint64_t pagesz_kb = getpagesize() / 1024;
+    size_t num_pages = (vma.end - vma.start) / (pagesz_kb * 1024);
+    size_t first_page = vma.start / (pagesz_kb * 1024);
 
     std::vector<uint64_t> page_cache;
     size_t cur_page_cache_index = 0;
@@ -417,7 +486,7 @@
         if (!PAGE_PRESENT(page_info) && !PAGE_SWAPPED(page_info)) continue;
 
         if (PAGE_SWAPPED(page_info)) {
-            vma.usage.swap += pagesz;
+            vma.usage.swap += pagesz_kb;
             swap_offsets_.emplace_back(PAGE_SWAP_OFFSET(page_info));
             continue;
         }
@@ -434,7 +503,7 @@
         }
 
         if (KPAGEFLAG_THP(cur_page_flags)) {
-            vma.usage.thp += pagesz;
+            vma.usage.thp += pagesz_kb;
         }
 
         // skip unwanted pages from the count
@@ -464,22 +533,22 @@
             // This effectively makes vss = rss for the working set is requested.
             // The libpagemap implementation returns vss > rss for
             // working set, which doesn't make sense.
-            vma.usage.vss += pagesz;
+            vma.usage.vss += pagesz_kb;
         }
 
-        vma.usage.rss += pagesz;
-        vma.usage.uss += is_private ? pagesz : 0;
-        vma.usage.pss += pagesz / cur_page_counts;
+        vma.usage.rss += pagesz_kb;
+        vma.usage.uss += is_private ? pagesz_kb : 0;
+        vma.usage.pss += pagesz_kb / cur_page_counts;
         if (is_private) {
-            vma.usage.private_dirty += is_dirty ? pagesz : 0;
-            vma.usage.private_clean += is_dirty ? 0 : pagesz;
+            vma.usage.private_dirty += is_dirty ? pagesz_kb : 0;
+            vma.usage.private_clean += is_dirty ? 0 : pagesz_kb;
         } else {
-            vma.usage.shared_dirty += is_dirty ? pagesz : 0;
-            vma.usage.shared_clean += is_dirty ? 0 : pagesz;
+            vma.usage.shared_dirty += is_dirty ? pagesz_kb : 0;
+            vma.usage.shared_clean += is_dirty ? 0 : pagesz_kb;
         }
     }
     if (!get_wss) {
-        vma.usage.vss += pagesz * num_pages;
+        vma.usage.vss += pagesz_kb * num_pages;
     }
     return true;
 }
@@ -639,5 +708,69 @@
     return true;
 }
 
+Format GetFormat(std::string_view arg) {
+    if (arg == "json") {
+        return Format::JSON;
+    }
+    if (arg == "csv") {
+        return Format::CSV;
+    }
+    if (arg == "raw") {
+        return Format::RAW;
+    }
+    return Format::INVALID;
+}
+
+std::string EscapeCsvString(const std::string& raw) {
+    std::string ret;
+    for (auto it = raw.cbegin(); it != raw.cend(); it++) {
+        switch (*it) {
+            case '"':
+                ret += "\"";
+                break;
+            default:
+                ret += *it;
+                break;
+        }
+    }
+    return '"' + ret + '"';
+}
+
+std::string EscapeJsonString(const std::string& raw) {
+    std::string ret;
+    for (auto it = raw.cbegin(); it != raw.cend(); it++) {
+        switch (*it) {
+            case '\\':
+                ret += "\\\\";
+                break;
+            case '"':
+                ret += "\\\"";
+                break;
+            case '/':
+                ret += "\\/";
+                break;
+            case '\b':
+                ret += "\\b";
+                break;
+            case '\f':
+                ret += "\\f";
+                break;
+            case '\n':
+                ret += "\\n";
+                break;
+            case '\r':
+                ret += "\\r";
+                break;
+            case '\t':
+                ret += "\\t";
+                break;
+            default:
+                ret += *it;
+                break;
+        }
+    }
+    return '"' + ret + '"';
+}
+
 }  // namespace meminfo
 }  // namespace android
diff --git a/sysmeminfo.cpp b/sysmeminfo.cpp
index b7a9c70..e3fb78a 100644
--- a/sysmeminfo.cpp
+++ b/sysmeminfo.cpp
@@ -145,7 +145,7 @@
     return true;
 }
 
-uint64_t SysMemInfo::mem_zram_kb(const char* zram_dev_cstr) {
+uint64_t SysMemInfo::mem_zram_kb(const char* zram_dev_cstr) const {
     uint64_t mem_zram_total = 0;
     if (zram_dev_cstr) {
         if (!MemZramDevice(zram_dev_cstr, &mem_zram_total)) {
@@ -174,7 +174,7 @@
     return mem_zram_total / 1024;
 }
 
-bool SysMemInfo::MemZramDevice(const char* zram_dev, uint64_t* mem_zram_dev) {
+bool SysMemInfo::MemZramDevice(const char* zram_dev, uint64_t* mem_zram_dev) const {
     std::string mmstat = ::android::base::StringPrintf("%s/%s", zram_dev, "mm_stat");
     auto mmstat_fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mmstat.c_str(), "re"), fclose};
     if (mmstat_fp != nullptr) {
@@ -204,6 +204,61 @@
     return false;
 }
 
+uint64_t SysMemInfo::mem_compacted_kb(const char* zram_dev_cstr) {
+    uint64_t mem_compacted_total = 0;
+    if (zram_dev_cstr) {
+        // Fast-path, single device
+        if (!GetTotalMemCompacted(zram_dev_cstr, &mem_compacted_total)) {
+            return 0;
+        }
+        return mem_compacted_total / 1024;
+    }
+
+    // Slow path - multiple devices
+    constexpr uint32_t kMaxZramDevices = 256;
+    for (uint32_t i = 0; i < kMaxZramDevices; i++) {
+        std::string zram_dev_abspath = ::android::base::StringPrintf("/sys/block/zram%u/", i);
+        if (access(zram_dev_abspath.c_str(), F_OK)) {
+            // We assume zram devices appear in range 0-255 and appear always in sequence
+            // under /sys/block. So, stop looking for them once we find one is missing.
+            break;
+        }
+
+        uint64_t mem_compacted;
+        if (!GetTotalMemCompacted(zram_dev_abspath.c_str(), &mem_compacted)) {
+            return 0;
+        }
+
+        mem_compacted_total += mem_compacted;
+    }
+
+    return mem_compacted_total / 1024; // transform to KBs
+}
+
+// Returns the total memory compacted in bytes which corresponds to the following formula
+// compacted memory = uncompressed memory size - compressed memory size
+bool SysMemInfo::GetTotalMemCompacted(const char* zram_dev, uint64_t* out_mem_compacted) {
+    std::string mmstat = ::android::base::StringPrintf("%s/%s", zram_dev, "mm_stat");
+    auto mmstat_fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mmstat.c_str(), "re"), fclose};
+    if (mmstat_fp != nullptr) {
+        uint64_t uncompressed_size_bytes;
+        uint64_t compressed_size_bytes;
+
+        if (fscanf(mmstat_fp.get(), "%" SCNu64 "%" SCNu64, &uncompressed_size_bytes,
+                   &compressed_size_bytes) != 2) {
+            PLOG(ERROR) << "Malformed mm_stat file in: " << zram_dev;
+            *out_mem_compacted = 0;
+            return false;
+        }
+
+        *out_mem_compacted = uncompressed_size_bytes - compressed_size_bytes;
+        return true;
+    }
+
+    *out_mem_compacted = 0;
+    return false;
+}
+
 // Public methods
 uint64_t ReadVmallocInfo(const char* path) {
     uint64_t vmalloc_total = 0;
@@ -322,7 +377,7 @@
 
 bool ReadPerProcessGpuMem([[maybe_unused]] std::unordered_map<uint32_t, uint64_t>* out) {
 #if defined(__ANDROID__) && !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
-    static constexpr const char kBpfGpuMemTotalMap[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map";
+    static constexpr const char kBpfGpuMemTotalMap[] = "/sys/fs/bpf/map_gpuMem_gpu_mem_total_map";
 
     // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
     auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kBpfGpuMemTotalMap);
@@ -371,7 +426,7 @@
 bool ReadProcessGpuUsageKb([[maybe_unused]] uint32_t pid, [[maybe_unused]] uint32_t gpu_id,
                            uint64_t* size) {
 #if defined(__ANDROID__) && !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
-    static constexpr const char kBpfGpuMemTotalMap[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map";
+    static constexpr const char kBpfGpuMemTotalMap[] = "/sys/fs/bpf/map_gpuMem_gpu_mem_total_map";
 
     uint64_t gpu_mem;
 
diff --git a/tools/Android.bp b/tools/Android.bp
index d336434..71fb2e0 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -27,6 +27,7 @@
     shared_libs: [
         "libbase",
         "libmeminfo",
+        "libsmapinfo",
     ],
 }
 
@@ -56,6 +57,7 @@
         "libbase",
         "libmeminfo",
         "libprocinfo",
+        "libsmapinfo",
     ],
 }
 
@@ -71,6 +73,7 @@
     shared_libs: [
         "libbase",
         "libmeminfo",
+        "libsmapinfo",
     ],
 
     target: {
@@ -93,3 +96,18 @@
         "libmeminfo",
     ],
 }
+
+cc_binary {
+    name: "bugreport_procdump",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: ["bugreport_procdump.cpp"],
+    shared_libs: [
+        "libbase",
+        "libmeminfo",
+        "libsmapinfo",
+    ],
+}
diff --git a/tools/bugreport_procdump.cpp b/tools/bugreport_procdump.cpp
new file mode 100644
index 0000000..bce2c47
--- /dev/null
+++ b/tools/bugreport_procdump.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 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 <cstdlib>
+#include <iostream>
+
+#include <smapinfo.h>
+
+int main() {
+    bool success = ::android::smapinfo::run_bugreport_procdump(std::cout, std::cerr);
+    if (!success) {
+        exit(EXIT_FAILURE);
+    }
+    return 0;
+}
diff --git a/tools/librank.cpp b/tools/librank.cpp
index cfc3f3c..57c14c8 100644
--- a/tools/librank.cpp
+++ b/tools/librank.cpp
@@ -14,210 +14,53 @@
  * limitations under the License.
  */
 
-#include <dirent.h>
-#include <errno.h>
 #include <error.h>
 #include <inttypes.h>
 #include <linux/kernel-page-flags.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <sys/mman.h>
 #include <sys/types.h>
-#include <unistd.h>
 
-#include <algorithm>
+#include <iostream>
 #include <map>
 #include <memory>
-#include <vector>
+#include <set>
 
-#include <android-base/file.h>
 #include <android-base/parseint.h>
-#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-
 #include <meminfo/procmeminfo.h>
 
-using ::android::meminfo::MemUsage;
-using ::android::meminfo::ProcMemInfo;
-using ::android::meminfo::Vma;
+#include <processrecord.h>
+#include <smapinfo.h>
 
-// The output format that can be specifid by user.
-enum Format {
-    RAW = 0,
-    JSON,
-    CSV
-};
+using ::android::meminfo::Format;
+using ::android::meminfo::GetFormat;
+using ::android::smapinfo::SortOrder;
 
 [[noreturn]] static void usage(int exit_status) {
-    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"
-            "    -f  [raw][json][csv] Print output in the specified format.\n"
-            "        (Default format is raw text.)\n"
-            "    -h  Display this help screen.\n",
-            getprogname());
+    std::cerr << "Usage: " << getprogname() << " [ -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"
+              << "    -o  Sort (and show) processes by oom score.\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"
+              << "    -f  [raw][json][csv] Print output in the specified format.\n"
+              << "        (Default format is raw text.)\n"
+              << "    -h  Display this help screen.\n";
     exit(exit_status);
 }
 
-static void add_mem_usage(MemUsage* to, const MemUsage& from) {
-    to->vss += from.vss;
-    to->rss += from.rss;
-    to->pss += from.pss;
-    to->uss += from.uss;
-
-    to->swap += from.swap;
-
-    to->private_clean += from.private_clean;
-    to->private_dirty += from.private_dirty;
-
-    to->shared_clean += from.shared_clean;
-    to->shared_dirty += from.shared_dirty;
-}
-
-struct ProcessRecord {
-  public:
-    ProcessRecord(pid_t pid) : pid_(-1), cmdline_("") {
-        std::string fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
-        std::string cmdline;
-        if (!::android::base::ReadFileToString(fname, &cmdline)) {
-            fprintf(stderr, "Failed to read cmdline from: %s\n", fname.c_str());
-            return;
-        }
-        // We deliberately don't read the proc/<pid>cmdline file directly into 'cmdline_'
-        // because of some processes showing up cmdlines that end with "0x00 0x0A 0x00"
-        // e.g. xtra-daemon, lowi-server
-        // The .c_str() assignment below then takes care of trimming the cmdline at the first
-        // 0x00. This is how original procrank worked (luckily)
-        cmdline_ = cmdline.c_str();
-        pid_ = pid;
-        usage_.clear();
-    }
-
-    ~ProcessRecord() = default;
-
-    bool valid() const { return pid_ != -1; }
-
-    // Getters
-    pid_t pid() const { return pid_; }
-    const std::string& cmdline() const { return cmdline_; }
-    const MemUsage& usage() const { return usage_; }
-
-    // Add to the usage
-    void AddUsage(const MemUsage& mem_usage) { add_mem_usage(&usage_, mem_usage); }
-
-  private:
-    pid_t pid_;
-    std::string cmdline_;
-    MemUsage usage_;
-};
-
-struct LibRecord {
-  public:
-    LibRecord(const std::string& name) : name_(name) {}
-    ~LibRecord() = default;
-
-    const std::string& name() const { return name_; }
-    const MemUsage& usage() const { return usage_; }
-    const std::map<pid_t, ProcessRecord>& processes() const { return procs_; }
-    uint64_t pss() const { return usage_.pss; }
-    void AddUsage(const ProcessRecord& proc, const MemUsage& mem_usage) {
-        auto [it, inserted] = procs_.insert(std::pair<pid_t, ProcessRecord>(proc.pid(), proc));
-        it->second.AddUsage(mem_usage);
-        add_mem_usage(&usage_, mem_usage);
-    }
-
-  private:
-    std::string name_;
-    MemUsage usage_;
-    std::map<pid_t, ProcessRecord> procs_;
-};
-
-// List of every library / map
-static std::map<std::string, LibRecord> g_libs;
-
-// List of library/map names that we don't want to show by default
-static const std::vector<std::string> g_excluded_libs = {"[heap]", "[stack]"};
-
-// Global flags affected by command line
-static uint64_t g_pgflags = 0;
-static uint64_t g_pgflags_mask = 0;
-static uint16_t g_mapflags_mask = 0;
-static bool g_all_libs = false;
-static bool g_has_swap = false;
-static bool g_reverse_sort = false;
-static std::string g_prefix_filter = "";
-
-static bool read_all_pids(std::function<bool(pid_t pid)> for_each_pid) {
-    std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
-    if (!procdir) return false;
-
-    struct dirent* dir;
-    pid_t pid;
-    while ((dir = readdir(procdir.get()))) {
-        if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
-        if (!for_each_pid(pid)) return false;
-    }
-
-    return true;
-}
-
-static bool scan_libs_per_process(pid_t pid) {
-    ProcMemInfo pmem(pid, false, g_pgflags, g_pgflags_mask);
-    const std::vector<Vma> maps = pmem.Maps();
-    if (maps.size() == 0) {
-        // nothing to do here, continue
-        return true;
-    }
-
-    ProcessRecord proc(pid);
-    if (!proc.valid()) {
-        fprintf(stderr, "Failed to create process record for process: %d\n", pid);
-        return false;
-    }
-
-    for (auto& map : maps) {
-        // skip library / map if prefix for the path doesn't match
-        if (!g_prefix_filter.empty() && !::android::base::StartsWith(map.name, g_prefix_filter)) {
-            continue;
-        }
-        // Skip maps based on map permissions
-        if (g_mapflags_mask &&
-            ((map.flags & (PROT_READ | PROT_WRITE | PROT_EXEC)) != g_mapflags_mask)) {
-            continue;
-        }
-
-        // skip excluded library / map names
-        if (!g_all_libs && (std::find(g_excluded_libs.begin(), g_excluded_libs.end(),
-                                      map.name) != g_excluded_libs.end())) {
-            continue;
-        }
-
-        auto [it, inserted] =
-            g_libs.insert(std::pair<std::string, LibRecord>(map.name, LibRecord(map.name)));
-        it->second.AddUsage(proc, map.usage);
-
-        if (!g_has_swap && map.usage.swap) {
-            g_has_swap = true;
-        }
-    }
-
-    return true;
-}
-
 static uint16_t parse_mapflags(const char* mapflags) {
     uint16_t ret = 0;
     for (const char* p = mapflags; *p; p++) {
@@ -235,247 +78,96 @@
                 error(EXIT_FAILURE, 0, "Invalid permissions string: %s, %s", mapflags, p);
         }
     }
-
     return ret;
 }
 
-static std::string escape_csv_string(const std::string& raw) {
-  std::string ret;
-  for (auto it = raw.cbegin(); it != raw.cend(); it++) {
-    switch (*it) {
-      case '"':
-        ret += "\"\"";
-        break;
-      default:
-        ret += *it;
-        break;
-    }
-  }
-  return '"' + ret + '"';
-}
-
-std::string to_csv(LibRecord& l, ProcessRecord& p) {
-    const MemUsage& usage = p.usage();
-    return  escape_csv_string(l.name())
-            + "," + std::to_string(l.pss() / 1024)
-            + "," + escape_csv_string(p.cmdline())
-            + ",\"[" + std::to_string(p.pid()) + "]\""
-            + "," + std::to_string(usage.vss/1024)
-            + "," + std::to_string(usage.rss/1024)
-            + "," + std::to_string(usage.pss/1024)
-            + "," + std::to_string(usage.uss/1024)
-            + (g_has_swap ? "," + std::to_string(usage.swap/1024) : "");
-}
-
-static std::string escape_json_string(const std::string& raw) {
-  std::string ret;
-  for (auto it = raw.cbegin(); it != raw.cend(); it++) {
-    switch (*it) {
-      case '\\':
-        ret += "\\\\";
-        break;
-      case '"':
-        ret += "\\\"";
-        break;
-      case '/':
-        ret += "\\/";
-        break;
-      case '\b':
-        ret += "\\b";
-        break;
-      case '\f':
-        ret += "\\f";
-        break;
-      case '\n':
-        ret += "\\n";
-        break;
-      case '\r':
-        ret += "\\r";
-        break;
-      case '\t':
-        ret += "\\t";
-        break;
-      default:
-        ret += *it;
-        break;
-    }
-  }
-  return '"' + ret + '"';
-}
-
-std::string to_json(LibRecord& l, ProcessRecord& p) {
-    const MemUsage& usage = p.usage();
-    return "{\"Library\":" + escape_json_string(l.name())
-            + ",\"Total_RSS\":" + std::to_string(l.pss() / 1024)
-            + ",\"Process\":" + escape_json_string(p.cmdline())
-            + ",\"PID\":\"" + std::to_string(p.pid()) + "\""
-            + ",\"VSS\":" + std::to_string(usage.vss/1024)
-            + ",\"RSS\":" + std::to_string(usage.rss/1024)
-            + ",\"PSS\":" + std::to_string(usage.pss/1024)
-            + ",\"USS\":" + std::to_string(usage.uss/1024)
-            + (g_has_swap ? ",\"Swap\":" + std::to_string(usage.swap/1024) : "")
-            + "}";
-}
-
-static Format get_format(std::string arg) {
-    if (arg.compare("json") == 0) {
-        return JSON;
-    }
-    if (arg.compare("csv") == 0) {
-        return CSV;
-    }
-    if (arg.compare("raw") == 0) {
-        return RAW;
-    }
-    error(EXIT_FAILURE, 0, "Invalid format.");
-    return RAW;
-}
-
 int main(int argc, char* argv[]) {
+    uint64_t pgflags = 0;
+    uint64_t pgflags_mask = 0;
+
+    // Library filtering options.
+    std::string lib_prefix = "";
+    bool all_libs = false;
+    std::vector<std::string> excluded_libs = {"[heap]", "[stack]"};
+    uint16_t mapflags_mask = 0;
+
+    // Output format (raw text, JSON, CSV).
+    Format format = Format::RAW;
+
+    // Process sorting options.
+    SortOrder sort_order = SortOrder::BY_PSS;
+    bool reverse_sort = false;
+
     int opt;
-
-    auto pss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
-        return g_reverse_sort ? a.usage().pss < b.usage().pss : a.usage().pss > b.usage().pss;
-    };
-
-    auto uss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
-        return g_reverse_sort ? a.usage().uss < b.usage().uss : a.usage().uss > b.usage().uss;
-    };
-
-    auto vss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
-        return g_reverse_sort ? a.usage().vss < b.usage().vss : a.usage().vss > b.usage().vss;
-    };
-
-    auto rss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
-        return g_reverse_sort ? a.usage().rss < b.usage().rss : a.usage().rss > b.usage().rss;
-    };
-
-    auto swap_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
-        return g_reverse_sort ? a.usage().swap < b.usage().swap : a.usage().swap > b.usage().swap;
-    };
-
-    std::function<bool(const ProcessRecord&, const ProcessRecord&)> sort_func = pss_sort;
-
-    Format format = RAW;
-    while ((opt = getopt(argc, argv, "acCf:hkm:pP:uvrsR")) != -1) {
+    while ((opt = getopt(argc, argv, "acCf:hkm:opP:uvrsR")) != -1) {
         switch (opt) {
             case 'a':
-                g_all_libs = true;
+                all_libs = true;
                 break;
             case 'c':
-                g_pgflags = 0;
-                g_pgflags_mask = (1 << KPF_SWAPBACKED);
+                pgflags = 0;
+                pgflags_mask = (1 << KPF_SWAPBACKED);
                 break;
             case 'C':
-                g_pgflags = g_pgflags_mask = (1 << KPF_SWAPBACKED);
+                pgflags = (1 << KPF_SWAPBACKED);
+                pgflags_mask = (1 << KPF_SWAPBACKED);
                 break;
             case 'f':
-                format = get_format(optarg);
+                format = GetFormat(optarg);
+                if (format == Format::INVALID) {
+                    std::cerr << "Invalid format." << std::endl;
+                    usage(EXIT_FAILURE);
+                }
                 break;
             case 'h':
                 usage(EXIT_SUCCESS);
             case 'k':
-                g_pgflags = g_pgflags_mask = (1 << KPF_KSM);
+                pgflags = (1 << KPF_KSM);
+                pgflags_mask = (1 << KPF_KSM);
                 break;
             case 'm':
-                g_mapflags_mask = parse_mapflags(optarg);
+                mapflags_mask = parse_mapflags(optarg);
+                break;
+            case 'o':
+                sort_order = SortOrder::BY_OOMADJ;
                 break;
             case 'p':
-                sort_func = pss_sort;
+                sort_order = SortOrder::BY_PSS;
                 break;
             case 'P':
-                g_prefix_filter = optarg;
-                break;
-            case 'u':
-                sort_func = uss_sort;
-                break;
-            case 'v':
-                sort_func = vss_sort;
+                lib_prefix = optarg;
                 break;
             case 'r':
-                sort_func = rss_sort;
-                break;
-            case 's':
-                sort_func = swap_sort;
+                sort_order = SortOrder::BY_RSS;
                 break;
             case 'R':
-                g_reverse_sort = true;
+                reverse_sort = true;
+                break;
+            case 's':
+                sort_order = SortOrder::BY_SWAP;
+                break;
+            case 'u':
+                sort_order = SortOrder::BY_USS;
+                break;
+            case 'v':
+                sort_order = SortOrder::BY_VSS;
                 break;
             default:
                 usage(EXIT_FAILURE);
         }
     }
 
-    if (!read_all_pids(scan_libs_per_process)) {
-        error(EXIT_FAILURE, 0, "Failed to read all pids from the system");
+    std::set<pid_t> pids;
+    if (!::android::smapinfo::get_all_pids(&pids)) {
+        std::cerr << "Failed to get all pids." << std::endl;
+        exit(EXIT_FAILURE);
     }
 
-    switch (format) {
-        case RAW:
-            printf(" %6s   %7s   %6s   %6s   %6s  ", "RSStot", "VSS", "RSS", "PSS", "USS");
-            if (g_has_swap) {
-                printf(" %6s  ", "Swap");
-            }
-            printf("Name/PID\n");
-            break;
-        case CSV:
-            printf("\"Library\",\"Total_RSS\",\"Process\",\"PID\",\"VSS\",\"RSS\",\"PSS\",\"USS\"");
-            if (g_has_swap) {
-                printf(", \"Swap\"");
-            }
-            printf("\n");
-            break;
-        case JSON:
-            break;
+    bool success = ::android::smapinfo::run_librank(
+            pgflags, pgflags_mask, pids, lib_prefix, all_libs, excluded_libs, mapflags_mask, format,
+            sort_order, reverse_sort, nullptr, std::cout, std::cerr);
+    if (!success) {
+        exit(EXIT_FAILURE);
     }
-
-    std::vector<LibRecord> v_libs;
-    v_libs.reserve(g_libs.size());
-    std::transform(g_libs.begin(), g_libs.end(), std::back_inserter(v_libs),
-        [] (std::pair<std::string, LibRecord> const& pair) { return pair.second; });
-
-    // sort the libraries by their pss
-    std::sort(v_libs.begin(), v_libs.end(),
-              [](const LibRecord& l1, const LibRecord& l2) { return l1.pss() > l2.pss(); });
-
-    for (auto& lib : v_libs) {
-        if (format == RAW) {
-            printf("%6" PRIu64 "K   %7s   %6s   %6s   %6s  ", lib.pss() / 1024, "", "", "", "");
-            if (g_has_swap) {
-                printf(" %6s  ", "");
-            }
-            printf("%s\n", lib.name().c_str());
-        }
-
-        // sort all mappings first
-
-        std::vector<ProcessRecord> procs;
-        procs.reserve(lib.processes().size());
-        std::transform(lib.processes().begin(), lib.processes().end(), std::back_inserter(procs),
-            [] (std::pair<pid_t, ProcessRecord> const& pair) { return pair.second; });
-
-        std::sort(procs.begin(), procs.end(), sort_func);
-
-        for (auto& p : procs) {
-            const MemUsage& usage = p.usage();
-            switch (format) {
-                case RAW:
-                    printf(" %6s  %7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ", "",
-                        usage.vss / 1024, usage.rss / 1024, usage.pss / 1024, usage.uss / 1024);
-                    if (g_has_swap) {
-                        printf("%6" PRIu64 "K  ", usage.swap / 1024);
-                    }
-                    printf("  %s [%d]\n", p.cmdline().c_str(), p.pid());
-                    break;
-                case JSON:
-                    printf("%s\n", to_json(lib, p).c_str());
-                    break;
-                case CSV:
-                    printf("%s\n", to_csv(lib, p).c_str());
-                    break;
-            }
-        }
-    }
-
     return 0;
 }
diff --git a/tools/procmem.cpp b/tools/procmem.cpp
index 1fe8d50..619f490 100644
--- a/tools/procmem.cpp
+++ b/tools/procmem.cpp
@@ -85,15 +85,14 @@
 
 static void print_stats(std::stringstream& ss, const MemUsage& stats) {
     if (!show_wss) {
-        ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", stats.vss / 1024);
+        ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", stats.vss);
     }
 
     ss << ::android::base::StringPrintf(
             "%6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64
             "K  %6" PRIu64 "K  %6" PRIu64 "K ",
-            stats.rss / 1024, stats.pss / 1024, stats.uss / 1024, stats.shared_clean / 1024,
-            stats.shared_dirty / 1024, stats.private_clean / 1024, stats.private_dirty / 1024,
-            stats.thp / 1024);
+            stats.rss, stats.pss, stats.uss, stats.shared_clean, stats.shared_dirty,
+            stats.private_clean, stats.private_dirty, stats.thp);
 }
 
 static int show(const MemUsage& proc_stats, const std::vector<Vma>& maps) {
diff --git a/tools/procrank.cpp b/tools/procrank.cpp
index e4f247a..a5d022d 100644
--- a/tools/procrank.cpp
+++ b/tools/procrank.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,135 +14,26 @@
  * limitations under the License.
  */
 
-#include <android-base/file.h>
-#include <android-base/parseint.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <dirent.h>
-#include <errno.h>
+#include <getopt.h>
 #include <inttypes.h>
 #include <linux/kernel-page-flags.h>
-#include <linux/oom.h>
-#include <meminfo/procmeminfo.h>
-#include <meminfo/sysmeminfo.h>
-#include <procinfo/process.h>
-#include <stdio.h>
 #include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
 
 #include <iostream>
-#include <memory>
+#include <map>
 #include <set>
-#include <sstream>
 #include <unordered_map>
 #include <vector>
 
-using ::android::meminfo::MemUsage;
-using ::android::meminfo::ProcMemInfo;
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <meminfo/procmeminfo.h>
+#include <procinfo/process.h>
 
-struct ProcessRecord {
-  public:
-    ProcessRecord(pid_t pid, bool get_wss = false, uint64_t pgflags = 0, uint64_t pgflags_mask = 0)
-        : pid_(-1),
-          oomadj_(OOM_SCORE_ADJ_MAX + 1),
-          cmdline_(""),
-          proportional_swap_(0),
-          unique_swap_(0),
-          zswap_(0) {
-        std::unique_ptr<ProcMemInfo> procmem =
-                std::make_unique<ProcMemInfo>(pid, get_wss, pgflags, pgflags_mask);
-        if (procmem == nullptr) {
-            std::cerr << "Failed to create ProcMemInfo for: " << pid << std::endl;
-            return;
-        }
+#include <processrecord.h>
+#include <smapinfo.h>
 
-        std::string fname = ::android::base::StringPrintf("/proc/%d/oom_score_adj", pid);
-        auto oomscore_fp =
-                std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname.c_str(), "re"), fclose};
-        if (oomscore_fp == nullptr) {
-            std::cerr << "Failed to open oom_score_adj file: " << fname << std::endl;
-            return;
-        }
-
-        if (fscanf(oomscore_fp.get(), "%d\n", &oomadj_) != 1) {
-            std::cerr << "Failed to read oomadj from: " << fname << std::endl;
-            return;
-        }
-
-        fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
-        if (!::android::base::ReadFileToString(fname, &cmdline_)) {
-            std::cerr << "Failed to read cmdline from: " << fname << std::endl;
-            cmdline_ = "<unknown>";
-        }
-        // We deliberately don't read the proc/<pid>cmdline file directly into 'cmdline_'
-        // because of some processes showing up cmdlines that end with "0x00 0x0A 0x00"
-        // e.g. xtra-daemon, lowi-server
-        // The .c_str() assignment below then takes care of trimming the cmdline at the first
-        // 0x00. This is how original procrank worked (luckily)
-        cmdline_.resize(strlen(cmdline_.c_str()));
-        usage_or_wss_ = get_wss ? procmem->Wss() : procmem->Usage();
-        swap_offsets_ = procmem->SwapOffsets();
-        pid_ = pid;
-    }
-
-    bool valid() const { return pid_ != -1; }
-
-    void CalculateSwap(const std::vector<uint16_t>& swap_offset_array,
-                       float zram_compression_ratio) {
-        for (auto& off : swap_offsets_) {
-            proportional_swap_ += getpagesize() / swap_offset_array[off];
-            unique_swap_ += swap_offset_array[off] == 1 ? getpagesize() : 0;
-            zswap_ = proportional_swap_ * zram_compression_ratio;
-        }
-    }
-
-    // Getters
-    pid_t pid() const { return pid_; }
-    const std::string& cmdline() const { return cmdline_; }
-    int32_t oomadj() const { return oomadj_; }
-    uint64_t proportional_swap() const { return proportional_swap_; }
-    uint64_t unique_swap() const { return unique_swap_; }
-    uint64_t zswap() const { return zswap_; }
-
-    // Wrappers to ProcMemInfo
-    const std::vector<uint64_t>& SwapOffsets() const { return swap_offsets_; }
-    const MemUsage& Usage() const { return usage_or_wss_; }
-    const MemUsage& Wss() const { return usage_or_wss_; }
-
-  private:
-    pid_t pid_;
-    int32_t oomadj_;
-    std::string cmdline_;
-    uint64_t proportional_swap_;
-    uint64_t unique_swap_;
-    uint64_t zswap_;
-    MemUsage usage_or_wss_;
-    std::vector<uint64_t> swap_offsets_;
-};
-
-// Show working set instead of memory consumption
-bool show_wss = false;
-// Reset working set of each process
-bool reset_wss = false;
-// Show per-process oom_score_adj column
-bool show_oomadj = false;
-// True if the device has swap enabled
-bool has_swap = false;
-// True, if device has zram enabled
-bool has_zram = false;
-// If zram is enabled, the compression ratio is zram used / swap used.
-float zram_compression_ratio = 0.0;
-// Sort process in reverse, default is descending
-bool reverse_sort = false;
-
-// Calculated total memory usage across all processes in the system
-uint64_t total_pss = 0;
-uint64_t total_uss = 0;
-uint64_t total_swap = 0;
-uint64_t total_pswap = 0;
-uint64_t total_uswap = 0;
-uint64_t total_zswap = 0;
+using ::android::smapinfo::SortOrder;
 
 [[noreturn]] static void usage(int exit_status) {
     std::cerr << "Usage: " << getprogname() << " [ -W ] [ -v | -r | -p | -u | -s | -h ] [-d PID]"
@@ -166,219 +57,21 @@
     exit(exit_status);
 }
 
-static bool read_all_pids(std::set<pid_t>* pids) {
-    pids->clear();
-    std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
-    if (!procdir) return false;
-
-    struct dirent* dir;
-    pid_t pid;
-    while ((dir = readdir(procdir.get()))) {
-        if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
-        pids->insert(pid);
-    }
-
-    return true;
-}
-
-static bool count_swap_offsets(const ProcessRecord& proc,
-                               std::vector<uint16_t>& swap_offset_array) {
-    const std::vector<uint64_t>& swp_offs = proc.SwapOffsets();
-    for (auto& off : swp_offs) {
-        if (off >= swap_offset_array.size()) {
-            std::cerr << "swap offset " << off << " is out of bounds for process: " << proc.pid()
-                      << std::endl;
-            return false;
-        }
-
-        if (swap_offset_array[off] == USHRT_MAX) {
-            std::cerr << "swap offset " << off << " ref count overflow in process: " << proc.pid()
-                      << std::endl;
-            return false;
-        }
-
-        swap_offset_array[off]++;
-    }
-
-    return true;
-}
-
-static void print_header(std::stringstream& ss) {
-    ss.str("");
-    ss << ::android::base::StringPrintf("%5s  ", "PID");
-    if (show_oomadj) {
-        ss << ::android::base::StringPrintf("%5s  ", "oom");
-    }
-
-    if (show_wss) {
-        ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "WRss", "WPss", "WUss");
-        // now swap statistics here, working set pages by definition shouldn't end up in swap.
-    } else {
-        ss << ::android::base::StringPrintf("%8s  %7s  %7s  %7s  ", "Vss", "Rss", "Pss", "Uss");
-        if (has_swap) {
-            ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "Swap", "PSwap", "USwap");
-            if (has_zram) {
-                ss << ::android::base::StringPrintf("%7s  ", "ZSwap");
-            }
-        }
-    }
-
-    ss << "cmdline";
-}
-
-static void print_process_record(std::stringstream& ss, ProcessRecord& proc) {
-    ss << ::android::base::StringPrintf("%5d  ", proc.pid());
-    if (show_oomadj) {
-        ss << ::android::base::StringPrintf("%5d  ", proc.oomadj());
-    }
-
-    if (show_wss) {
-        ss << ::android::base::StringPrintf("%6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ",
-                                            proc.Wss().rss / 1024, proc.Wss().pss / 1024,
-                                            proc.Wss().uss / 1024);
-    } else {
-        ss << ::android::base::StringPrintf("%7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64
-                                            "K  ",
-                                            proc.Usage().vss / 1024, proc.Usage().rss / 1024,
-                                            proc.Usage().pss / 1024, proc.Usage().uss / 1024);
-        if (has_swap) {
-            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.Usage().swap / 1024);
-            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.proportional_swap() / 1024);
-            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.unique_swap() / 1024);
-            if (has_zram) {
-                ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", (proc.zswap() / 1024));
-            }
-        }
-    }
-}
-
-static void print_processes(std::stringstream& ss, std::vector<ProcessRecord>& procs,
-                            const std::vector<uint16_t>& swap_offset_array) {
-    for (auto& proc : procs) {
-        total_pss += show_wss ? proc.Wss().pss : proc.Usage().pss;
-        total_uss += show_wss ? proc.Wss().uss : proc.Usage().uss;
-        if (!show_wss && has_swap) {
-            proc.CalculateSwap(swap_offset_array, zram_compression_ratio);
-            total_swap += proc.Usage().swap;
-            total_pswap += proc.proportional_swap();
-            total_uswap += proc.unique_swap();
-            if (has_zram) {
-                total_zswap += proc.zswap();
-            }
-        }
-
-        print_process_record(ss, proc);
-        ss << proc.cmdline() << std::endl;
-    }
-}
-
-static void print_separator(std::stringstream& ss) {
-    ss << ::android::base::StringPrintf("%5s  ", "");
-    if (show_oomadj) {
-        ss << ::android::base::StringPrintf("%5s  ", "");
-    }
-
-    if (show_wss) {
-        ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "", "------", "------");
-    } else {
-        ss << ::android::base::StringPrintf("%8s  %7s  %7s  %7s  ", "", "", "------", "------");
-        if (has_swap) {
-            ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "------", "------", "------");
-            if (has_zram) {
-                ss << ::android::base::StringPrintf("%7s  ", "------");
-            }
-        }
-    }
-
-    ss << ::android::base::StringPrintf("%s", "------");
-}
-
-static void print_totals(std::stringstream& ss) {
-    ss << ::android::base::StringPrintf("%5s  ", "");
-    if (show_oomadj) {
-        ss << ::android::base::StringPrintf("%5s  ", "");
-    }
-
-    if (show_wss) {
-        ss << ::android::base::StringPrintf("%7s  %6" PRIu64 "K  %6" PRIu64 "K  ", "",
-                                            total_pss / 1024, total_uss / 1024);
-    } else {
-        ss << ::android::base::StringPrintf("%8s  %7s  %6" PRIu64 "K  %6" PRIu64 "K  ", "", "",
-                                            total_pss / 1024, total_uss / 1024);
-        if (has_swap) {
-            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_swap / 1024);
-            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_pswap / 1024);
-            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_uswap / 1024);
-            if (has_zram) {
-                ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_zswap / 1024);
-            }
-        }
-    }
-    ss << "TOTAL";
-}
-
-static void print_sysmeminfo(std::stringstream& ss, ::android::meminfo::SysMemInfo& smi) {
-    if (has_swap) {
-        ss << ::android::base::StringPrintf("ZRAM: %" PRIu64 "K physical used for %" PRIu64
-                                            "K in swap "
-                                            "(%" PRIu64 "K total swap)",
-                                            smi.mem_zram_kb(),
-                                            (smi.mem_swap_kb() - smi.mem_swap_free_kb()),
-                                            smi.mem_swap_kb())
-           << std::endl;
-    }
-
-    ss << ::android::base::StringPrintf(" RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64
-                                        "K buffers, "
-                                        "%" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64
-                                        "K slab",
-                                        smi.mem_total_kb(), smi.mem_free_kb(), smi.mem_buffers_kb(),
-                                        smi.mem_cached_kb(), smi.mem_shmem_kb(), smi.mem_slab_kb());
-}
-
 int main(int argc, char* argv[]) {
-    auto pss_sort = [](ProcessRecord& a, ProcessRecord& b) {
-        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
-        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
-        return reverse_sort ? stats_a.pss < stats_b.pss : stats_a.pss > stats_b.pss;
-    };
-
-    auto uss_sort = [](ProcessRecord& a, ProcessRecord& b) {
-        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
-        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
-        return reverse_sort ? stats_a.uss < stats_b.uss : stats_a.uss > stats_b.uss;
-    };
-
-    auto rss_sort = [](ProcessRecord& a, ProcessRecord& b) {
-        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
-        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
-        return reverse_sort ? stats_a.rss < stats_b.rss : stats_a.rss > stats_b.rss;
-    };
-
-    auto vss_sort = [](ProcessRecord& a, ProcessRecord& b) {
-        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
-        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
-        return reverse_sort ? stats_a.vss < stats_b.vss : stats_a.vss > stats_b.vss;
-    };
-
-    auto swap_sort = [](ProcessRecord& a, ProcessRecord& b) {
-        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
-        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
-        return reverse_sort ? stats_a.swap < stats_b.swap : stats_a.swap > stats_b.swap;
-    };
-
-    auto oomadj_sort = [](ProcessRecord& a, ProcessRecord& b) {
-        return reverse_sort ? a.oomadj() < b.oomadj() : a.oomadj() > b.oomadj();
-    };
-
-    // default PSS sort
-    std::function<bool(ProcessRecord & a, ProcessRecord & b)> proc_sort = pss_sort;
-
-    // count all pages by default
+    // Count all pages by default.
     uint64_t pgflags = 0;
     uint64_t pgflags_mask = 0;
 
+    // Sort by PSS descending by default.
+    SortOrder sort_order = SortOrder::BY_PSS;
+    bool reverse_sort = false;
+
+    bool get_oomadj = false;
+    bool get_wss = false;
+    bool reset_wss = false;
+
     std::vector<pid_t> descendant_filter;
+
     int opt;
     while ((opt = getopt(argc, argv, "cCd:hkoprRsuvwW")) != -1) {
         switch (opt) {
@@ -406,29 +99,29 @@
                 pgflags_mask = (1 << KPF_KSM);
                 break;
             case 'o':
-                proc_sort = oomadj_sort;
-                show_oomadj = true;
+                sort_order = SortOrder::BY_OOMADJ;
+                get_oomadj = true;
                 break;
             case 'p':
-                proc_sort = pss_sort;
+                sort_order = SortOrder::BY_PSS;
                 break;
             case 'r':
-                proc_sort = rss_sort;
+                sort_order = SortOrder::BY_RSS;
                 break;
             case 'R':
                 reverse_sort = true;
                 break;
             case 's':
-                proc_sort = swap_sort;
+                sort_order = SortOrder::BY_SWAP;
                 break;
             case 'u':
-                proc_sort = uss_sort;
+                sort_order = SortOrder::BY_USS;
                 break;
             case 'v':
-                proc_sort = vss_sort;
+                sort_order = SortOrder::BY_VSS;
                 break;
             case 'w':
-                show_wss = true;
+                get_wss = true;
                 break;
             case 'W':
                 reset_wss = true;
@@ -439,9 +132,8 @@
     }
 
     std::set<pid_t> pids;
-    std::vector<ProcessRecord> procs;
-    if (!read_all_pids(&pids)) {
-        std::cerr << "Failed to read pids" << std::endl;
+    if (!::android::smapinfo::get_all_pids(&pids)) {
+        std::cerr << "Failed to get all pids." << std::endl;
         exit(EXIT_FAILURE);
     }
 
@@ -486,103 +178,20 @@
 
     if (reset_wss) {
         for (pid_t pid : pids) {
-            if (!ProcMemInfo::ResetWorkingSet(pid)) {
+            if (!::android::meminfo::ProcMemInfo::ResetWorkingSet(pid)) {
                 std::cerr << "Failed to reset working set of all processes" << std::endl;
                 exit(EXIT_FAILURE);
             }
         }
-        // we are done, all other options passed to procrank are ignored in the presence of '-W'
+        // Other options passed to procrank are ignored if reset_wss is true.
         return 0;
     }
 
-    ::android::meminfo::SysMemInfo smi;
-    if (!smi.ReadMemInfo()) {
-        std::cerr << "Failed to get system memory info" << std::endl;
+    bool success = ::android::smapinfo::run_procrank(pgflags, pgflags_mask, pids, get_oomadj,
+                                                     get_wss, sort_order, reverse_sort, nullptr,
+                                                     std::cout, std::cerr);
+    if (!success) {
         exit(EXIT_FAILURE);
     }
-
-    // Figure out swap and zram
-    uint64_t swap_total = smi.mem_swap_kb() * 1024;
-    has_swap = swap_total > 0;
-    // Allocate the swap array
-    std::vector<uint16_t> swap_offset_array(swap_total / getpagesize() + 1, 0);
-    if (has_swap) {
-        has_zram = smi.mem_zram_kb() > 0;
-        if (has_zram) {
-            zram_compression_ratio = static_cast<float>(smi.mem_zram_kb()) /
-                                     (smi.mem_swap_kb() - smi.mem_swap_free_kb());
-        }
-    }
-
-    // Mark each swap offset used by the process as we find them for calculating proportional
-    // swap usage later.
-    for (pid_t pid : pids) {
-        ProcessRecord proc(pid, show_wss, pgflags, pgflags_mask);
-        if (!proc.valid()) {
-            // Check to see if the process is still around, skip the process if the proc
-            // directory is inaccessible. It was most likely killed while creating the process
-            // record
-            std::string procdir = ::android::base::StringPrintf("/proc/%d", pid);
-            if (access(procdir.c_str(), F_OK | R_OK)) continue;
-
-            // Warn if we failed to gather process stats even while it is still alive.
-            // Return success here, so we continue to print stats for other processes.
-            std::cerr << "warning: failed to create process record for: " << pid << std::endl;
-            continue;
-        }
-
-        // Skip processes with no memory mappings
-        uint64_t vss = show_wss ? proc.Wss().vss : proc.Usage().vss;
-        if (vss == 0) continue;
-
-        // collect swap_offset counts from all processes in 1st pass
-        if (!show_wss && has_swap && !count_swap_offsets(proc, swap_offset_array)) {
-            std::cerr << "Failed to count swap offsets for process: " << pid << std::endl;
-            std::cerr << "Failed to read all pids from the system" << std::endl;
-            exit(EXIT_FAILURE);
-        }
-
-        procs.emplace_back(std::move(proc));
-    }
-
-    std::stringstream ss;
-    if (procs.empty()) {
-        // This would happen in corner cases where procrank is being run to find KSM usage on a
-        // system with no KSM and combined with working set determination as follows
-        //   procrank -w -u -k
-        //   procrank -w -s -k
-        //   procrank -w -o -k
-        ss << "<empty>" << std::endl << std::endl;
-        print_sysmeminfo(ss, smi);
-        ss << std::endl;
-        std::cout << ss.str();
-        return 0;
-    }
-
-    // Sort all process records, default is PSS descending
-    std::sort(procs.begin(), procs.end(), proc_sort);
-
-    // start dumping output in string stream
-    print_header(ss);
-    ss << std::endl;
-
-    // 2nd pass to calculate and get per process stats to add them up
-    print_processes(ss, procs, swap_offset_array);
-
-    // Add separator to output
-    print_separator(ss);
-    ss << std::endl;
-
-    // Add totals to output
-    print_totals(ss);
-    ss << std::endl << std::endl;
-
-    // Add system information at the end
-    print_sysmeminfo(ss, smi);
-    ss << std::endl;
-
-    // dump on the screen
-    std::cout << ss.str();
-
     return 0;
 }
diff --git a/tools/showmap.cpp b/tools/showmap.cpp
index 452e895..198dd7b 100644
--- a/tools/showmap.cpp
+++ b/tools/showmap.cpp
@@ -23,231 +23,35 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <iomanip>
+#include <iostream>
+#include <map>
 #include <memory>
 #include <string>
-#include <vector>
 
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <meminfo/procmeminfo.h>
 
-using ::android::meminfo::Vma;
+#include <processrecord.h>
+#include <smapinfo.h>
 
-struct VmaInfo {
-    Vma vma;
-    bool is_bss;
-    uint32_t count;
+using ::android::meminfo::Format;
+using ::android::meminfo::GetFormat;
 
-    VmaInfo() = default;
-    VmaInfo(const Vma& v) : vma(v), is_bss(false), count(1) {}
-    VmaInfo(const Vma& v, bool bss) : vma(v), is_bss(bss), count(1) {}
-    VmaInfo(const Vma& v, const std::string& name, bool bss) : vma(v), is_bss(bss), count(1) {
-        vma.name = name;
-    }
-};
-
-// Global options
-static std::string g_filename = "";
-static bool g_merge_by_names = false;
-static bool g_terse = false;
-static bool g_verbose = false;
-static bool g_show_addr = false;
-static bool g_quiet = false;
-static pid_t g_pid = -1;
-
-static VmaInfo g_total;
-static std::vector<VmaInfo> g_vmas;
-
-[[noreturn]] static void usage(const char* progname, int exit_status) {
-    fprintf(stderr,
-            "%s [-aqtv] [-f FILE] PID\n"
-            "-a\taddresses (show virtual memory map)\n"
-            "-q\tquiet (don't show error if map could not be read)\n"
-            "-t\tterse (show only items with private pages)\n"
-            "-v\tverbose (don't coalesce maps with the same name)\n"
-            "-f\tFILE (read from input from FILE instead of PID)\n",
-            progname);
+[[noreturn]] static void usage(int exit_status) {
+    std::cerr << "showmap [-aqtv] [-f FILE] PID\n"
+              << "-a\taddresses (show virtual memory map)\n"
+              << "-q\tquiet (don't show error if map could not be read)\n"
+              << "-t\tterse (show only items with private pages)\n"
+              << "-v\tverbose (don't coalesce maps with the same name)\n"
+              << "-f\tFILE (read from input from FILE instead of PID)\n"
+              << "-o\t[raw][json][csv] Print output in the specified format.\n"
+              << "  \tDefault output format is raw text.)\n";
 
     exit(exit_status);
 }
 
-static bool is_library(const std::string& name) {
-    return (name.size() > 4) && (name[0] == '/') && ::android::base::EndsWith(name, ".so");
-}
-
-static bool insert_before(const VmaInfo& a, const VmaInfo& b) {
-    if (g_show_addr) {
-        return (a.vma.start < b.vma.start || (a.vma.start == b.vma.start && a.vma.end < b.vma.end));
-    }
-
-    return strcmp(a.vma.name.c_str(), b.vma.name.c_str()) < 0;
-}
-
-static void collect_vma(const Vma& vma) {
-    if (g_vmas.empty()) {
-        g_vmas.emplace_back(vma);
-        return;
-    }
-
-    VmaInfo current(vma);
-    VmaInfo& last = g_vmas.back();
-    // determine if this is bss;
-    if (vma.name.empty()) {
-        if (last.vma.end == current.vma.start && is_library(last.vma.name)) {
-            current.vma.name = last.vma.name;
-            current.is_bss = true;
-        } else {
-            current.vma.name = "[anon]";
-        }
-    }
-
-    std::vector<VmaInfo>::iterator it;
-    for (it = g_vmas.begin(); it != g_vmas.end(); it++) {
-        if (g_merge_by_names && (it->vma.name == current.vma.name)) {
-            it->vma.usage.vss += current.vma.usage.vss;
-            it->vma.usage.rss += current.vma.usage.rss;
-            it->vma.usage.pss += current.vma.usage.pss;
-
-            it->vma.usage.shared_clean += current.vma.usage.shared_clean;
-            it->vma.usage.shared_dirty += current.vma.usage.shared_dirty;
-            it->vma.usage.private_clean += current.vma.usage.private_clean;
-            it->vma.usage.private_dirty += current.vma.usage.private_dirty;
-            it->vma.usage.swap += current.vma.usage.swap;
-            it->vma.usage.swap_pss += current.vma.usage.swap_pss;
-
-            it->vma.usage.anon_huge_pages += current.vma.usage.anon_huge_pages;
-            it->vma.usage.shmem_pmd_mapped += current.vma.usage.shmem_pmd_mapped;
-            it->vma.usage.file_pmd_mapped += current.vma.usage.file_pmd_mapped;
-            it->vma.usage.shared_hugetlb += current.vma.usage.shared_hugetlb;
-            it->vma.usage.private_hugetlb += current.vma.usage.private_hugetlb;
-
-            it->is_bss &= current.is_bss;
-            it->count++;
-
-            break;
-        }
-
-        if (insert_before(current, *it)) {
-            g_vmas.insert(it, current);
-            break;
-        }
-    }
-
-    if (it == g_vmas.end()) {
-        g_vmas.emplace_back(current);
-    }
-}
-
-static void print_header() {
-    const char* addr1 = g_show_addr ? "           start              end " : "";
-    const char* addr2 = g_show_addr ? "            addr             addr " : "";
-
-    printf("%s virtual                     shared   shared  private  private                   "
-           "Anon      Shmem     File       Shared   Private\n",
-           addr1);
-    printf("%s    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS "
-           "HugePages PmdMapped PmdMapped  Hugetlb  Hugetlb",
-           addr2);
-    if (!g_verbose && !g_show_addr) {
-        printf("   # ");
-    }
-    if (g_verbose) {
-        printf(" flags ");
-    }
-    printf(" object\n");
-}
-
-static void print_divider() {
-    if (g_show_addr) {
-        printf("-------- -------- ");
-    }
-    printf("-------- -------- -------- -------- -------- -------- -------- -------- -------- "
-           "--------- --------- --------- -------- -------- ");
-    if (!g_verbose && !g_show_addr) {
-        printf("---- ");
-    }
-    if (g_verbose) {
-        printf("------ ");
-    }
-    printf("------------------------------\n");
-}
-
-static void print_vmainfo(const VmaInfo& v, bool total) {
-    if (g_show_addr) {
-        if (total) {
-            printf("                                  ");
-        } else {
-            printf("%16" PRIx64 " %16" PRIx64 " ", v.vma.start, v.vma.end);
-        }
-    }
-    printf("%8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64
-           " %8" PRIu64 " %8" PRIu64 " %9" PRIu64 " %9" PRIu64 " %9" PRIu64 " %8" PRIu64
-           " %8" PRIu64 " ",
-           v.vma.usage.vss, v.vma.usage.rss, v.vma.usage.pss, v.vma.usage.shared_clean,
-           v.vma.usage.shared_dirty, v.vma.usage.private_clean, v.vma.usage.private_dirty,
-           v.vma.usage.swap, v.vma.usage.swap_pss, v.vma.usage.anon_huge_pages,
-           v.vma.usage.shmem_pmd_mapped, v.vma.usage.file_pmd_mapped, v.vma.usage.shared_hugetlb,
-           v.vma.usage.private_hugetlb);
-    if (!g_verbose && !g_show_addr) {
-        printf("%4" PRIu32 " ", v.count);
-    }
-    if (g_verbose) {
-        if (total) {
-            printf("       ");
-        } else {
-            std::string flags_str("---");
-            if (v.vma.flags & PROT_READ) flags_str[0] = 'r';
-            if (v.vma.flags & PROT_WRITE) flags_str[1] = 'w';
-            if (v.vma.flags & PROT_EXEC) flags_str[2] = 'x';
-
-            printf("%6s ", flags_str.c_str());
-        }
-    }
-}
-
-static int showmap(void) {
-    if (!::android::meminfo::ForEachVmaFromFile(g_filename, collect_vma)) {
-        if (!g_quiet) {
-            fprintf(stderr, "Failed to parse file %s\n", g_filename.c_str());
-        }
-        return 1;
-    }
-
-    print_header();
-    print_divider();
-
-    for (const auto& v : g_vmas) {
-        g_total.vma.usage.vss += v.vma.usage.vss;
-        g_total.vma.usage.rss += v.vma.usage.rss;
-        g_total.vma.usage.pss += v.vma.usage.pss;
-
-        g_total.vma.usage.private_clean += v.vma.usage.private_clean;
-        g_total.vma.usage.private_dirty += v.vma.usage.private_dirty;
-        g_total.vma.usage.shared_clean += v.vma.usage.shared_clean;
-        g_total.vma.usage.shared_dirty += v.vma.usage.shared_dirty;
-
-        g_total.vma.usage.swap += v.vma.usage.swap;
-        g_total.vma.usage.swap_pss += v.vma.usage.swap_pss;
-        g_total.count += v.count;
-
-        if (g_terse && !(v.vma.usage.private_dirty || v.vma.usage.private_clean)) {
-            continue;
-        }
-
-        print_vmainfo(v, false);
-        printf("%s%s\n", v.vma.name.c_str(), v.is_bss ? " [bss]" : "");
-    }
-
-    print_divider();
-    print_header();
-    print_divider();
-
-    print_vmainfo(g_total, true);
-    printf("TOTAL\n");
-
-    return 0;
-}
-
 int main(int argc, char* argv[]) {
     signal(SIGPIPE, SIG_IGN);
     struct option longopts[] = {
@@ -255,46 +59,70 @@
             {0, 0, nullptr, 0},
     };
 
+    // Printing options.
+    bool terse = false;
+    bool quiet = false;
+
+    // Output options.
+    bool show_addr = false;
+    bool verbose = false;
+    Format format = Format::RAW;
+
+    // 'pid' will be ignored if a file is specified.
+    std::string filename;
+    pid_t pid = 0;
+
     int opt;
-    while ((opt = getopt_long(argc, argv, "tvaqf:h", longopts, nullptr)) != -1) {
+    while ((opt = getopt_long(argc, argv, "tvaqf:o:h", longopts, nullptr)) != -1) {
         switch (opt) {
             case 't':
-                g_terse = true;
+                terse = true;
                 break;
             case 'a':
-                g_show_addr = true;
+                show_addr = true;
                 break;
             case 'v':
-                g_verbose = true;
+                verbose = true;
                 break;
             case 'q':
-                g_quiet = true;
+                quiet = true;
                 break;
             case 'f':
-                g_filename = optarg;
+                filename = optarg;
+                break;
+            case 'o':
+                format = GetFormat(optarg);
+                if (format == Format::INVALID) {
+                    std::cerr << "Invalid format.\n";
+                    usage(EXIT_FAILURE);
+                }
                 break;
             case 'h':
-                usage(argv[0], EXIT_SUCCESS);
+                usage(EXIT_SUCCESS);
             default:
-                usage(argv[0], EXIT_FAILURE);
+                usage(EXIT_FAILURE);
         }
     }
 
-    if (g_filename.empty()) {
+    if (filename.empty()) {
         if ((argc - 1) < optind) {
-            fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
-            usage(argv[0], EXIT_FAILURE);
+            std::cerr << "Invalid arguments: Must provide <pid> at the end\n";
+            usage(EXIT_FAILURE);
         }
 
-        g_pid = atoi(argv[optind]);
-        if (g_pid <= 0) {
-            fprintf(stderr, "Invalid process id %s\n", argv[optind]);
-            usage(argv[0], EXIT_FAILURE);
+        pid = atoi(argv[optind]);
+        if (pid <= 0) {
+            std::cerr << "Invalid process id " << argv[optind] << "\n";
+            usage(EXIT_FAILURE);
         }
-
-        g_filename = ::android::base::StringPrintf("/proc/%d/smaps", g_pid);
+        // run_showmap will read directly from this file and ignore the pid argument.
+        filename = ::android::base::StringPrintf("/proc/%d/smaps", pid);
     }
 
-    g_merge_by_names = !g_verbose && !g_show_addr;
-    return showmap();
+    bool success = ::android::smapinfo::run_showmap(pid, filename, terse, verbose, show_addr, quiet,
+                                                    format, nullptr, std::cout, std::cerr);
+    if (!success) {
+        exit(EXIT_FAILURE);
+    }
+    return 0;
 }
diff --git a/tools/wsstop.cpp b/tools/wsstop.cpp
index 368d04e..c0c6a86 100644
--- a/tools/wsstop.cpp
+++ b/tools/wsstop.cpp
@@ -69,9 +69,8 @@
     printf("%16" PRIx64 " %16" PRIx64 " ", v.start, v.end);
     printf("%8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64
            "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K ",
-           v.usage.vss / 1024, v.usage.rss / 1024, v.usage.pss / 1024, v.usage.shared_clean / 1024,
-           v.usage.shared_dirty / 1024, v.usage.private_clean / 1024, v.usage.private_dirty / 1024,
-           v.usage.swap / 1024, v.usage.swap_pss / 1024);
+           v.usage.vss, v.usage.rss, v.usage.pss, v.usage.shared_clean, v.usage.shared_dirty,
+           v.usage.private_clean, v.usage.private_dirty, v.usage.swap, v.usage.swap_pss);
     printf("%s\n", v.name.c_str());
 }
 
diff --git a/vts/vts_meminfo_test.cpp b/vts/vts_meminfo_test.cpp
index cec46d5..a3f3652 100644
--- a/vts/vts_meminfo_test.cpp
+++ b/vts/vts_meminfo_test.cpp
@@ -89,7 +89,7 @@
     }
 }
 
-// /sys/fs/bpf/map_gpu_mem_gpu_mem_total_map support is required for devices launching with
+// /sys/fs/bpf/map_gpuMem_gpu_mem_total_map support is required for devices launching with
 // Android S and having 5.4 or higher kernel version.
 TEST(SysMemInfo, TestGpuTotalUsageKb) {
     uint64_t size;