Home | History | Annotate | Download | only in dexlayout
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *    http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <errno.h>
     18 #include <inttypes.h>
     19 #include <stdint.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 
     23 #include <iostream>
     24 #include <memory>
     25 #include <vector>
     26 
     27 #include "android-base/stringprintf.h"
     28 
     29 #include "base/stringpiece.h"
     30 
     31 #include "dex_file.h"
     32 #include "dex_ir.h"
     33 #include "dex_ir_builder.h"
     34 #ifdef ART_TARGET_ANDROID
     35 #include "pagemap/pagemap.h"
     36 #endif
     37 #include "runtime.h"
     38 #include "vdex_file.h"
     39 
     40 namespace art {
     41 
     42 using android::base::StringPrintf;
     43 
     44 static bool g_verbose = false;
     45 
     46 // The width needed to print a file page offset (32-bit).
     47 static constexpr int kPageCountWidth =
     48     static_cast<int>(std::numeric_limits<uint32_t>::digits10);
     49 // Display the sections.
     50 static constexpr char kSectionHeader[] = "Section name";
     51 
     52 struct DexSectionInfo {
     53  public:
     54   std::string name;
     55   char letter;
     56 };
     57 
     58 static const std::map<uint16_t, DexSectionInfo> kDexSectionInfoMap = {
     59   { DexFile::kDexTypeHeaderItem, { "Header", 'H' } },
     60   { DexFile::kDexTypeStringIdItem, { "StringId", 'S' } },
     61   { DexFile::kDexTypeTypeIdItem, { "TypeId", 'T' } },
     62   { DexFile::kDexTypeProtoIdItem, { "ProtoId", 'P' } },
     63   { DexFile::kDexTypeFieldIdItem, { "FieldId", 'F' } },
     64   { DexFile::kDexTypeMethodIdItem, { "MethodId", 'M' } },
     65   { DexFile::kDexTypeClassDefItem, { "ClassDef", 'C' } },
     66   { DexFile::kDexTypeCallSiteIdItem, { "CallSiteId", 'z' } },
     67   { DexFile::kDexTypeMethodHandleItem, { "MethodHandle", 'Z' } },
     68   { DexFile::kDexTypeMapList, { "TypeMap", 'L' } },
     69   { DexFile::kDexTypeTypeList, { "TypeList", 't' } },
     70   { DexFile::kDexTypeAnnotationSetRefList, { "AnnotationSetReferenceItem", '1' } },
     71   { DexFile::kDexTypeAnnotationSetItem, { "AnnotationSetItem", '2' } },
     72   { DexFile::kDexTypeClassDataItem, { "ClassData", 'c' } },
     73   { DexFile::kDexTypeCodeItem, { "CodeItem", 'X' } },
     74   { DexFile::kDexTypeStringDataItem, { "StringData", 's' } },
     75   { DexFile::kDexTypeDebugInfoItem, { "DebugInfo", 'D' } },
     76   { DexFile::kDexTypeAnnotationItem, { "AnnotationItem", '3' } },
     77   { DexFile::kDexTypeEncodedArrayItem, { "EncodedArrayItem", 'E' } },
     78   { DexFile::kDexTypeAnnotationsDirectoryItem, { "AnnotationsDirectoryItem", '4' } }
     79 };
     80 
     81 class PageCount {
     82  public:
     83   PageCount() {
     84     for (auto it = kDexSectionInfoMap.begin(); it != kDexSectionInfoMap.end(); ++it) {
     85       map_[it->first] = 0;
     86     }
     87   }
     88   void Increment(uint16_t type) {
     89     map_[type]++;
     90   }
     91   size_t Get(uint16_t type) const {
     92     return map_.at(type);
     93   }
     94  private:
     95   std::map<uint16_t, size_t> map_;
     96   DISALLOW_COPY_AND_ASSIGN(PageCount);
     97 };
     98 
     99 class Printer {
    100  public:
    101   Printer() : section_header_width_(ComputeHeaderWidth()) {
    102   }
    103 
    104   void PrintHeader() const {
    105     std::cout << StringPrintf("%-*s %*s %*s %% of   %% of",
    106                               section_header_width_,
    107                               kSectionHeader,
    108                               kPageCountWidth,
    109                               "resident",
    110                               kPageCountWidth,
    111                               "total"
    112                               )
    113               << std::endl;
    114     std::cout << StringPrintf("%-*s %*s %*s sect.  total",
    115                               section_header_width_,
    116                               "",
    117                               kPageCountWidth,
    118                               "pages",
    119                               kPageCountWidth,
    120                               "pages")
    121               << std::endl;
    122   }
    123 
    124   void PrintOne(const char* name,
    125                 size_t resident,
    126                 size_t mapped,
    127                 double percent_of_section,
    128                 double percent_of_total) const {
    129     // 6.2 is sufficient to print 0-100% with two decimal places of accuracy.
    130     std::cout << StringPrintf("%-*s %*zd %*zd %6.2f %6.2f",
    131                               section_header_width_,
    132                               name,
    133                               kPageCountWidth,
    134                               resident,
    135                               kPageCountWidth,
    136                               mapped,
    137                               percent_of_section,
    138                               percent_of_total)
    139               << std::endl;
    140   }
    141 
    142   void PrintSkipLine() const { std::cout << std::endl; }
    143 
    144   // Computes the width of the section header column in the table (for fixed formatting).
    145   static int ComputeHeaderWidth() {
    146     int header_width = 0;
    147     for (const auto& pair : kDexSectionInfoMap) {
    148       const DexSectionInfo& section_info = pair.second;
    149       header_width = std::max(header_width, static_cast<int>(section_info.name.length()));
    150     }
    151     return header_width;
    152   }
    153 
    154  private:
    155   const int section_header_width_;
    156 };
    157 
    158 static void PrintLetterKey() {
    159   std::cout << "L pagetype" << std::endl;
    160   for (const auto& pair : kDexSectionInfoMap) {
    161     const DexSectionInfo& section_info = pair.second;
    162     std::cout << section_info.letter << " " << section_info.name.c_str() << std::endl;
    163   }
    164   std::cout << "* (Executable page resident)" << std::endl;
    165   std::cout << ". (Mapped page not resident)" << std::endl;
    166 }
    167 
    168 #ifdef ART_TARGET_ANDROID
    169 static char PageTypeChar(uint16_t type) {
    170   if (kDexSectionInfoMap.find(type) == kDexSectionInfoMap.end()) {
    171     return '-';
    172   }
    173   return kDexSectionInfoMap.find(type)->second.letter;
    174 }
    175 
    176 static uint16_t FindSectionTypeForPage(size_t page,
    177                                        const std::vector<dex_ir::DexFileSection>& sections) {
    178   for (const auto& section : sections) {
    179     size_t first_page_of_section = section.offset / kPageSize;
    180     // Only consider non-empty sections.
    181     if (section.size == 0) {
    182       continue;
    183     }
    184     // Attribute the page to the highest-offset section that starts before the page.
    185     if (first_page_of_section <= page) {
    186       return section.type;
    187     }
    188   }
    189   // If there's no non-zero sized section with an offset below offset we're looking for, it
    190   // must be the header.
    191   return DexFile::kDexTypeHeaderItem;
    192 }
    193 
    194 static void ProcessPageMap(uint64_t* pagemap,
    195                            size_t start,
    196                            size_t end,
    197                            const std::vector<dex_ir::DexFileSection>& sections,
    198                            PageCount* page_counts) {
    199   static constexpr size_t kLineLength = 32;
    200   for (size_t page = start; page < end; ++page) {
    201     char type_char = '.';
    202     if (PM_PAGEMAP_PRESENT(pagemap[page])) {
    203       const size_t dex_page_offset = page - start;
    204       uint16_t type = FindSectionTypeForPage(dex_page_offset, sections);
    205       page_counts->Increment(type);
    206       type_char = PageTypeChar(type);
    207     }
    208     if (g_verbose) {
    209       std::cout << type_char;
    210       if ((page - start) % kLineLength == kLineLength - 1) {
    211         std::cout << std::endl;
    212       }
    213     }
    214   }
    215   if (g_verbose) {
    216     if ((end - start) % kLineLength != 0) {
    217       std::cout << std::endl;
    218     }
    219   }
    220 }
    221 
    222 static void DisplayDexStatistics(size_t start,
    223                                  size_t end,
    224                                  const PageCount& resident_pages,
    225                                  const std::vector<dex_ir::DexFileSection>& sections,
    226                                  Printer* printer) {
    227   // Compute the total possible sizes for sections.
    228   PageCount mapped_pages;
    229   DCHECK_GE(end, start);
    230   size_t total_mapped_pages = end - start;
    231   if (total_mapped_pages == 0) {
    232     return;
    233   }
    234   for (size_t page = start; page < end; ++page) {
    235     const size_t dex_page_offset = page - start;
    236     mapped_pages.Increment(FindSectionTypeForPage(dex_page_offset, sections));
    237   }
    238   size_t total_resident_pages = 0;
    239   printer->PrintHeader();
    240   for (size_t i = sections.size(); i > 0; --i) {
    241     const dex_ir::DexFileSection& section = sections[i - 1];
    242     const uint16_t type = section.type;
    243     const DexSectionInfo& section_info = kDexSectionInfoMap.find(type)->second;
    244     size_t pages_resident = resident_pages.Get(type);
    245     double percent_resident = 0;
    246     if (mapped_pages.Get(type) > 0) {
    247       percent_resident = 100.0 * pages_resident / mapped_pages.Get(type);
    248     }
    249     printer->PrintOne(section_info.name.c_str(),
    250                       pages_resident,
    251                       mapped_pages.Get(type),
    252                       percent_resident,
    253                       100.0 * pages_resident / total_mapped_pages);
    254     total_resident_pages += pages_resident;
    255   }
    256   double percent_of_total = 100.0 * total_resident_pages / total_mapped_pages;
    257   printer->PrintOne("GRAND TOTAL",
    258                     total_resident_pages,
    259                     total_mapped_pages,
    260                     percent_of_total,
    261                     percent_of_total);
    262   printer->PrintSkipLine();
    263 }
    264 
    265 static void ProcessOneDexMapping(uint64_t* pagemap,
    266                                  uint64_t map_start,
    267                                  const DexFile* dex_file,
    268                                  uint64_t vdex_start,
    269                                  Printer* printer) {
    270   uint64_t dex_file_start = reinterpret_cast<uint64_t>(dex_file->Begin());
    271   size_t dex_file_size = dex_file->Size();
    272   if (dex_file_start < vdex_start) {
    273     std::cerr << "Dex file start offset for "
    274               << dex_file->GetLocation().c_str()
    275               << " is incorrect: map start "
    276               << StringPrintf("%" PRIx64 " > dex start %" PRIx64 "\n", map_start, dex_file_start)
    277               << std::endl;
    278     return;
    279   }
    280   uint64_t start_page = (dex_file_start - vdex_start) / kPageSize;
    281   uint64_t start_address = start_page * kPageSize;
    282   uint64_t end_page = RoundUp(start_address + dex_file_size, kPageSize) / kPageSize;
    283   std::cout << "DEX "
    284             << dex_file->GetLocation().c_str()
    285             << StringPrintf(": %" PRIx64 "-%" PRIx64,
    286                             map_start + start_page * kPageSize,
    287                             map_start + end_page * kPageSize)
    288             << std::endl;
    289   // Build a list of the dex file section types, sorted from highest offset to lowest.
    290   std::vector<dex_ir::DexFileSection> sections;
    291   {
    292     std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file));
    293     sections = dex_ir::GetSortedDexFileSections(header.get(),
    294                                                 dex_ir::SortDirection::kSortDescending);
    295   }
    296   PageCount section_resident_pages;
    297   ProcessPageMap(pagemap, start_page, end_page, sections, &section_resident_pages);
    298   DisplayDexStatistics(start_page, end_page, section_resident_pages, sections, printer);
    299 }
    300 
    301 static bool IsVdexFileMapping(const std::string& mapped_name) {
    302   // Confirm that the map is from a vdex file.
    303   static const char* suffixes[] = { ".vdex" };
    304   for (const char* suffix : suffixes) {
    305     size_t match_loc = mapped_name.find(suffix);
    306     if (match_loc != std::string::npos && mapped_name.length() == match_loc + strlen(suffix)) {
    307       return true;
    308     }
    309   }
    310   return false;
    311 }
    312 
    313 static bool DisplayMappingIfFromVdexFile(pm_map_t* map, Printer* printer) {
    314   std::string vdex_name = pm_map_name(map);
    315   // Extract all the dex files from the vdex file.
    316   std::string error_msg;
    317   std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_name,
    318                                                 false /*writeable*/,
    319                                                 false /*low_4gb*/,
    320                                                 false /*unquicken */,
    321                                                 &error_msg /*out*/));
    322   if (vdex == nullptr) {
    323     std::cerr << "Could not open vdex file "
    324               << vdex_name
    325               << ": error "
    326               << error_msg
    327               << std::endl;
    328     return false;
    329   }
    330 
    331   std::vector<std::unique_ptr<const DexFile>> dex_files;
    332   if (!vdex->OpenAllDexFiles(&dex_files, &error_msg)) {
    333     std::cerr << "Dex files could not be opened for "
    334               << vdex_name
    335               << ": error "
    336               << error_msg
    337               << std::endl;
    338     return false;
    339   }
    340   // Open the page mapping (one uint64_t per page) for the entire vdex mapping.
    341   uint64_t* pagemap;
    342   size_t len;
    343   if (pm_map_pagemap(map, &pagemap, &len) != 0) {
    344     std::cerr << "Error creating pagemap." << std::endl;
    345     return false;
    346   }
    347   // Process the dex files.
    348   std::cout << "MAPPING "
    349             << pm_map_name(map)
    350             << StringPrintf(": %" PRIx64 "-%" PRIx64, pm_map_start(map), pm_map_end(map))
    351             << std::endl;
    352   for (const auto& dex_file : dex_files) {
    353     ProcessOneDexMapping(pagemap,
    354                          pm_map_start(map),
    355                          dex_file.get(),
    356                          reinterpret_cast<uint64_t>(vdex->Begin()),
    357                          printer);
    358   }
    359   free(pagemap);
    360   return true;
    361 }
    362 
    363 static void ProcessOneOatMapping(uint64_t* pagemap, size_t size, Printer* printer) {
    364   static constexpr size_t kLineLength = 32;
    365   size_t resident_page_count = 0;
    366   for (size_t page = 0; page < size; ++page) {
    367     char type_char = '.';
    368     if (PM_PAGEMAP_PRESENT(pagemap[page])) {
    369       ++resident_page_count;
    370       type_char = '*';
    371     }
    372     if (g_verbose) {
    373       std::cout << type_char;
    374       if (page % kLineLength == kLineLength - 1) {
    375         std::cout << std::endl;
    376       }
    377     }
    378   }
    379   if (g_verbose) {
    380     if (size % kLineLength != 0) {
    381       std::cout << std::endl;
    382     }
    383   }
    384   double percent_of_total = 100.0 * resident_page_count / size;
    385   printer->PrintHeader();
    386   printer->PrintOne("EXECUTABLE", resident_page_count, size, percent_of_total, percent_of_total);
    387   printer->PrintSkipLine();
    388 }
    389 
    390 static bool IsOatFileMapping(const std::string& mapped_name) {
    391   // Confirm that the map is from an oat file.
    392   static const char* suffixes[] = { ".odex", ".oat" };
    393   for (const char* suffix : suffixes) {
    394     size_t match_loc = mapped_name.find(suffix);
    395     if (match_loc != std::string::npos && mapped_name.length() == match_loc + strlen(suffix)) {
    396       return true;
    397     }
    398   }
    399   return false;
    400 }
    401 
    402 static bool DisplayMappingIfFromOatFile(pm_map_t* map, Printer* printer) {
    403   // Open the page mapping (one uint64_t per page) for the entire vdex mapping.
    404   uint64_t* pagemap;
    405   size_t len;
    406   if (pm_map_pagemap(map, &pagemap, &len) != 0) {
    407     std::cerr << "Error creating pagemap." << std::endl;
    408     return false;
    409   }
    410   // Process the dex files.
    411   std::cout << "MAPPING "
    412             << pm_map_name(map)
    413             << StringPrintf(": %" PRIx64 "-%" PRIx64, pm_map_start(map), pm_map_end(map))
    414             << std::endl;
    415   ProcessOneOatMapping(pagemap, len, printer);
    416   free(pagemap);
    417   return true;
    418 }
    419 
    420 static bool FilterByNameContains(const std::string& mapped_file_name,
    421                                  const std::vector<std::string>& name_filters) {
    422   // If no filters were set, everything matches.
    423   if (name_filters.empty()) {
    424     return true;
    425   }
    426   for (const auto& name_contains : name_filters) {
    427     if (mapped_file_name.find(name_contains) != std::string::npos) {
    428       return true;
    429     }
    430   }
    431   return false;
    432 }
    433 #endif
    434 
    435 static void Usage(const char* cmd) {
    436   std::cout << "Usage: " << cmd << " [options] pid" << std::endl
    437             << "    --contains=<string>:  Display sections containing string." << std::endl
    438             << "    --help:               Shows this message." << std::endl
    439             << "    --verbose:            Makes displays verbose." << std::endl;
    440   PrintLetterKey();
    441 }
    442 
    443 static int DexDiagMain(int argc, char* argv[]) {
    444   if (argc < 2) {
    445     Usage(argv[0]);
    446     return EXIT_FAILURE;
    447   }
    448 
    449   std::vector<std::string> name_filters;
    450   // TODO: add option to track usage by class name, etc.
    451   for (int i = 1; i < argc - 1; ++i) {
    452     const StringPiece option(argv[i]);
    453     if (option == "--help") {
    454       Usage(argv[0]);
    455       return EXIT_SUCCESS;
    456     } else if (option == "--verbose") {
    457       g_verbose = true;
    458     } else if (option.starts_with("--contains=")) {
    459       std::string contains(option.substr(strlen("--contains=")).data());
    460       name_filters.push_back(contains);
    461     } else {
    462       Usage(argv[0]);
    463       return EXIT_FAILURE;
    464     }
    465   }
    466 
    467   // Art specific set up.
    468   InitLogging(argv, Runtime::Abort);
    469   MemMap::Init();
    470 
    471 #ifdef ART_TARGET_ANDROID
    472   pid_t pid;
    473   char* endptr;
    474   pid = (pid_t)strtol(argv[argc - 1], &endptr, 10);
    475   if (*endptr != '\0' || kill(pid, 0) != 0) {
    476     std::cerr << StringPrintf("Invalid PID \"%s\".\n", argv[argc - 1]) << std::endl;
    477     return EXIT_FAILURE;
    478   }
    479 
    480   // get libpagemap kernel information.
    481   pm_kernel_t* ker;
    482   if (pm_kernel_create(&ker) != 0) {
    483     std::cerr << "Error creating kernel interface -- does this kernel have pagemap?" << std::endl;
    484     return EXIT_FAILURE;
    485   }
    486 
    487   // get libpagemap process information.
    488   pm_process_t* proc;
    489   if (pm_process_create(ker, pid, &proc) != 0) {
    490     std::cerr << "Error creating process interface -- does process "
    491               << pid
    492               << " really exist?"
    493               << std::endl;
    494     return EXIT_FAILURE;
    495   }
    496 
    497   // Get the set of mappings by the specified process.
    498   pm_map_t** maps;
    499   size_t num_maps;
    500   if (pm_process_maps(proc, &maps, &num_maps) != 0) {
    501     std::cerr << "Error listing maps." << std::endl;
    502     return EXIT_FAILURE;
    503   }
    504 
    505   bool match_found = false;
    506   // Process the mappings that are due to vdex or oat files.
    507   Printer printer;
    508   for (size_t i = 0; i < num_maps; ++i) {
    509     std::string mapped_file_name = pm_map_name(maps[i]);
    510     // Filter by name contains options (if any).
    511     if (!FilterByNameContains(mapped_file_name, name_filters)) {
    512       continue;
    513     }
    514     if (IsVdexFileMapping(mapped_file_name)) {
    515       if (!DisplayMappingIfFromVdexFile(maps[i], &printer)) {
    516         return EXIT_FAILURE;
    517       }
    518       match_found = true;
    519     } else if (IsOatFileMapping(mapped_file_name)) {
    520       if (!DisplayMappingIfFromOatFile(maps[i], &printer)) {
    521         return EXIT_FAILURE;
    522       }
    523       match_found = true;
    524     }
    525   }
    526   if (!match_found) {
    527     std::cerr << "No relevant memory maps were found." << std::endl;
    528     return EXIT_FAILURE;
    529   }
    530 #endif
    531 
    532   return EXIT_SUCCESS;
    533 }
    534 
    535 }  // namespace art
    536 
    537 int main(int argc, char* argv[]) {
    538   return art::DexDiagMain(argc, argv);
    539 }
    540