diff options
| -rw-r--r-- | dexlayout/dexdiag.cc | 274 |
1 files changed, 194 insertions, 80 deletions
diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc index 3edf0514f3..688201b6b8 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 @@ using android::base::StringPrintf; 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 @@ class PageCount { DISALLOW_COPY_AND_ASSIGN(PageCount); }; +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 << "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; + 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 ProcessPageMap(uint64_t* pagemap, 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 @@ static void DisplayDexStatistics(size_t start, 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 @@ static void DisplayDexStatistics(size_t start, 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 @@ static void ProcessOneDexMapping(uint64_t* pagemap, } 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 @@ static bool DisplayMappingIfFromVdexFile(pm_map_t* map) { &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 @@ static bool DisplayMappingIfFromVdexFile(pm_map_t* map) { 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 @@ static bool DisplayMappingIfFromVdexFile(pm_map_t* map) { 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 @@ static int DexDiagMain(int argc, char* argv[]) { 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 @@ static int DexDiagMain(int argc, char* argv[]) { } // 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 |