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, §ion_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