Add OAT file mapping to dexdiag

Adds the ability to visualize the executable mapped sections of an OAT
file.

Bug: 37217968
Test: test-art-target-gtest-dexdiag_test
Change-Id: Id9f33885ee38a3753e781f955127eb8e1b83fe4d
diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc
index 3edf051..688201b 100644
--- a/dexlayout/dexdiag.cc
+++ b/dexlayout/dexdiag.cc
@@ -21,9 +21,12 @@
 
 #include <iostream>
 #include <memory>
+#include <vector>
 
 #include "android-base/stringprintf.h"
 
+#include "base/stringpiece.h"
+
 #include "dex_file.h"
 #include "dex_ir.h"
 #include "dex_ir_builder.h"
@@ -37,9 +40,13 @@
 
 static constexpr size_t kLineLength = 32;
 
-static bool g_show_key = false;
 static bool g_verbose = false;
-static bool g_show_statistics = false;
+
+// The width needed to print a file page offset (32-bit).
+static constexpr int kPageCountWidth =
+    static_cast<int>(std::numeric_limits<uint32_t>::digits10);
+// Display the sections.
+static constexpr char kSectionHeader[] = "Section name";
 
 struct DexSectionInfo {
  public:
@@ -88,12 +95,73 @@
   DISALLOW_COPY_AND_ASSIGN(PageCount);
 };
 
-static void PrintLetterKey() {
-  std::cout << "letter section_type" << std::endl;
-  for (const auto& p : kDexSectionInfoMap) {
-    const DexSectionInfo& section_info = p.second;
-    std::cout << section_info.letter << "      " << section_info.name.c_str() << std::endl;
+class Printer {
+ public:
+  Printer() : section_header_width_(ComputeHeaderWidth()) {
   }
+
+  void PrintHeader() const {
+    std::cout << StringPrintf("%-*s %*s %*s %% of   %% of",
+                              section_header_width_,
+                              kSectionHeader,
+                              kPageCountWidth,
+                              "resident",
+                              kPageCountWidth,
+                              "total"
+                              )
+              << std::endl;
+    std::cout << StringPrintf("%-*s %*s %*s sect.  total",
+                              section_header_width_,
+                              "",
+                              kPageCountWidth,
+                              "pages",
+                              kPageCountWidth,
+                              "pages")
+              << std::endl;
+  }
+
+  void PrintOne(const char* name,
+                size_t resident,
+                size_t mapped,
+                double percent_of_section,
+                double percent_of_total) const {
+    // 6.2 is sufficient to print 0-100% with two decimal places of accuracy.
+    std::cout << StringPrintf("%-*s %*zd %*zd %6.2f %6.2f",
+                              section_header_width_,
+                              name,
+                              kPageCountWidth,
+                              resident,
+                              kPageCountWidth,
+                              mapped,
+                              percent_of_section,
+                              percent_of_total)
+              << std::endl;
+  }
+
+  void PrintSkipLine() const { std::cout << std::endl; }
+
+  // Computes the width of the section header column in the table (for fixed formatting).
+  static int ComputeHeaderWidth() {
+    int header_width = 0;
+    for (const auto& pair : kDexSectionInfoMap) {
+      const DexSectionInfo& section_info = pair.second;
+      header_width = std::max(header_width, static_cast<int>(section_info.name.length()));
+    }
+    return header_width;
+  }
+
+ private:
+  const int section_header_width_;
+};
+
+static void PrintLetterKey() {
+  std::cout << "L pagetype" << std::endl;
+  for (const auto& pair : kDexSectionInfoMap) {
+    const DexSectionInfo& section_info = pair.second;
+    std::cout << section_info.letter << " " << section_info.name.c_str() << std::endl;
+  }
+  std::cout << "* (Executable page resident)" << std::endl;
+  std::cout << ". (Mapped page not resident)" << std::endl;
 }
 
 static char PageTypeChar(uint16_t type) {
@@ -150,7 +218,8 @@
 static void DisplayDexStatistics(size_t start,
                                  size_t end,
                                  const PageCount& resident_pages,
-                                 const std::vector<dex_ir::DexFileSection>& sections) {
+                                 const std::vector<dex_ir::DexFileSection>& sections,
+                                 Printer* printer) {
   // Compute the total possible sizes for sections.
   PageCount mapped_pages;
   DCHECK_GE(end, start);
@@ -162,34 +231,7 @@
     mapped_pages.Increment(FindSectionTypeForPage(page, sections));
   }
   size_t total_resident_pages = 0;
-  // Compute the width of the section header column in the table (for fixed formatting).
-  int section_header_width = 0;
-  for (const auto& section_info : kDexSectionInfoMap) {
-    section_header_width = std::max(section_header_width,
-                                    static_cast<int>(section_info.second.name.length()));
-  }
-  // The width needed to print a file page offset (32-bit).
-  static constexpr int kPageCountWidth =
-      static_cast<int>(std::numeric_limits<uint32_t>::digits10);
-  // Display the sections.
-  static constexpr char kSectionHeader[] = "Section name";
-  std::cout << StringPrintf("%-*s %*s %*s %% of   %% of",
-                            section_header_width,
-                            kSectionHeader,
-                            kPageCountWidth,
-                            "resident",
-                            kPageCountWidth,
-                            "total"
-                            )
-            << std::endl;
-  std::cout << StringPrintf("%-*s %*s %*s sect.  total",
-                            section_header_width,
-                            "",
-                            kPageCountWidth,
-                            "pages",
-                            kPageCountWidth,
-                            "pages")
-            << std::endl;
+  printer->PrintHeader();
   for (size_t i = sections.size(); i > 0; --i) {
     const dex_ir::DexFileSection& section = sections[i - 1];
     const uint16_t type = section.type;
@@ -199,35 +241,27 @@
     if (mapped_pages.Get(type) > 0) {
       percent_resident = 100.0 * pages_resident / mapped_pages.Get(type);
     }
-    // 6.2 is sufficient to print 0-100% with two decimal places of accuracy.
-    std::cout << StringPrintf("%-*s %*zd %*zd %6.2f %6.2f",
-                              section_header_width,
-                              section_info.name.c_str(),
-                              kPageCountWidth,
-                              pages_resident,
-                              kPageCountWidth,
-                              mapped_pages.Get(type),
-                              percent_resident,
-                              100.0 * pages_resident / total_mapped_pages)
-              << std::endl;
+    printer->PrintOne(section_info.name.c_str(),
+                      pages_resident,
+                      mapped_pages.Get(type),
+                      percent_resident,
+                      100.0 * pages_resident / total_mapped_pages);
     total_resident_pages += pages_resident;
   }
-  std::cout << StringPrintf("%-*s %*zd %*zd        %6.2f",
-                            section_header_width,
-                            "GRAND TOTAL",
-                            kPageCountWidth,
-                            total_resident_pages,
-                            kPageCountWidth,
-                            total_mapped_pages,
-                            100.0 * total_resident_pages / total_mapped_pages)
-            << std::endl
-            << std::endl;
+  double percent_of_total = 100.0 * total_resident_pages / total_mapped_pages;
+  printer->PrintOne("GRAND TOTAL",
+                    total_resident_pages,
+                    total_mapped_pages,
+                    percent_of_total,
+                    percent_of_total);
+  printer->PrintSkipLine();
 }
 
 static void ProcessOneDexMapping(uint64_t* pagemap,
                                  uint64_t map_start,
                                  const DexFile* dex_file,
-                                 uint64_t vdex_start) {
+                                 uint64_t vdex_start,
+                                 Printer* printer) {
   uint64_t dex_file_start = reinterpret_cast<uint64_t>(dex_file->Begin());
   size_t dex_file_size = dex_file->Size();
   if (dex_file_start < vdex_start) {
@@ -256,12 +290,10 @@
   }
   PageCount section_resident_pages;
   ProcessPageMap(pagemap, start_page, end_page, sections, &section_resident_pages);
-  if (g_show_statistics) {
-    DisplayDexStatistics(start_page, end_page, section_resident_pages, sections);
-  }
+  DisplayDexStatistics(start_page, end_page, section_resident_pages, sections, printer);
 }
 
-static bool DisplayMappingIfFromVdexFile(pm_map_t* map) {
+static bool DisplayMappingIfFromVdexFile(pm_map_t* map, Printer* printer) {
   // Confirm that the map is from a vdex file.
   static const char* suffixes[] = { ".vdex" };
   std::string vdex_name;
@@ -284,9 +316,9 @@
                                                 &error_msg /*out*/));
   if (vdex == nullptr) {
     std::cerr << "Could not open vdex file "
-              << vdex_name.c_str()
+              << vdex_name
               << ": error "
-              << error_msg.c_str()
+              << error_msg
               << std::endl;
     return false;
   }
@@ -294,9 +326,9 @@
   std::vector<std::unique_ptr<const DexFile>> dex_files;
   if (!vdex->OpenAllDexFiles(&dex_files, &error_msg)) {
     std::cerr << "Dex files could not be opened for "
-              << vdex_name.c_str()
+              << vdex_name
               << ": error "
-              << error_msg.c_str()
+              << error_msg
               << std::endl;
   }
   // Open the page mapping (one uint64_t per page) for the entire vdex mapping.
@@ -315,18 +347,91 @@
     ProcessOneDexMapping(pagemap,
                          pm_map_start(map),
                          dex_file.get(),
-                         reinterpret_cast<uint64_t>(vdex->Begin()));
+                         reinterpret_cast<uint64_t>(vdex->Begin()),
+                         printer);
   }
   free(pagemap);
   return true;
 }
 
+static void ProcessOneOatMapping(uint64_t* pagemap, size_t size, Printer* printer) {
+  size_t resident_page_count = 0;
+  for (size_t page = 0; page < size; ++page) {
+    char type_char = '.';
+    if (PM_PAGEMAP_PRESENT(pagemap[page])) {
+      ++resident_page_count;
+      type_char = '*';
+    }
+    if (g_verbose) {
+      std::cout << type_char;
+      if (page % kLineLength == kLineLength - 1) {
+        std::cout << std::endl;
+      }
+    }
+  }
+  if (g_verbose) {
+    if (size % kLineLength != 0) {
+      std::cout << std::endl;
+    }
+  }
+  double percent_of_total = 100.0 * resident_page_count / size;
+  printer->PrintHeader();
+  printer->PrintOne("EXECUTABLE", resident_page_count, size, percent_of_total, percent_of_total);
+  printer->PrintSkipLine();
+}
+
+static bool DisplayMappingIfFromOatFile(pm_map_t* map, Printer* printer) {
+  // Confirm that the map is from a vdex file.
+  static const char* suffixes[] = { ".odex", ".oat" };
+  std::string vdex_name;
+  bool found = false;
+  for (size_t j = 0; j < sizeof(suffixes) / sizeof(suffixes[0]); ++j) {
+    if (strstr(pm_map_name(map), suffixes[j]) != nullptr) {
+      vdex_name = pm_map_name(map);
+      found = true;
+      break;
+    }
+  }
+  if (!found) {
+    return true;
+  }
+  // Open the page mapping (one uint64_t per page) for the entire vdex mapping.
+  uint64_t* pagemap;
+  size_t len;
+  if (pm_map_pagemap(map, &pagemap, &len) != 0) {
+    std::cerr << "Error creating pagemap." << std::endl;
+    return false;
+  }
+  // Process the dex files.
+  std::cout << "MAPPING "
+            << pm_map_name(map)
+            << StringPrintf(": %zx-%zx", pm_map_start(map), pm_map_end(map))
+            << std::endl;
+  ProcessOneOatMapping(pagemap, len, printer);
+  free(pagemap);
+  return true;
+}
+
+static bool FilterByNameContains(const std::string& mapped_file_name,
+                                 const std::vector<std::string>& name_filters) {
+  // If no filters were set, everything matches.
+  if (name_filters.empty()) {
+    return true;
+  }
+  for (const auto& name_contains : name_filters) {
+    if (mapped_file_name.find(name_contains) != std::string::npos) {
+      return true;
+    }
+  }
+  return false;
+}
 
 static void Usage(const char* cmd) {
-  std::cerr << "Usage: " << cmd << " [-k] [-s] [-v] pid" << std::endl
-            << "    -k Shows a key to verbose display characters." << std::endl
-            << "    -s Shows section statistics for individual dex files." << std::endl
-            << "    -v Verbosely displays resident pages for dex files." << std::endl;
+  std::cerr << "Usage: " << cmd << " [options] pid" << std::endl
+            << "    --contains=<string>:  Display sections containing string." << std::endl
+            << "    --help:               Shows this message." << std::endl
+            << "    --verbose:            Makes displays verbose." << std::endl;
+  PrintLetterKey();
 }
 
 static int DexDiagMain(int argc, char* argv[]) {
@@ -335,14 +440,18 @@
     return EXIT_FAILURE;
   }
 
+  std::vector<std::string> name_filters;
   // TODO: add option to track usage by class name, etc.
   for (int i = 1; i < argc - 1; ++i) {
-    if (strcmp(argv[i], "-k") == 0) {
-      g_show_key = true;
-    } else if (strcmp(argv[i], "-s") == 0) {
-      g_show_statistics = true;
-    } else if (strcmp(argv[i], "-v") == 0) {
+    const StringPiece option(argv[i]);
+    if (option == "--help") {
+      Usage(argv[0]);
+      return EXIT_SUCCESS;
+    } else if (option == "--verbose") {
       g_verbose = true;
+    } else if (option.starts_with("--contains=")) {
+      std::string contains(option.substr(strlen("--contains=")).data());
+      name_filters.push_back(contains);
     } else {
       Usage(argv[0]);
       return EXIT_FAILURE;
@@ -387,16 +496,21 @@
   }
 
   // Process the mappings that are due to DEX files.
+  Printer printer;
   for (size_t i = 0; i < num_maps; ++i) {
-    if (!DisplayMappingIfFromVdexFile(maps[i])) {
+    std::string mapped_file_name = pm_map_name(maps[i]);
+    // Filter by name contains options (if any).
+    if (!FilterByNameContains(mapped_file_name, name_filters)) {
+      continue;
+    }
+    if (!DisplayMappingIfFromVdexFile(maps[i], &printer)) {
+      return EXIT_FAILURE;
+    } else if (!DisplayMappingIfFromOatFile(maps[i], &printer)) {
       return EXIT_FAILURE;
     }
   }
 
-  if (g_show_key) {
-    PrintLetterKey();
-  }
-  return 0;
+  return EXIT_SUCCESS;
 }
 
 }  // namespace art