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 #include "pagemap/pagemap.h"
     35 #include "runtime.h"
     36 #include "vdex_file.h"
     37 
     38 namespace art {
     39 
     40 using android::base::StringPrintf;
     41 
     42 static constexpr size_t kLineLength = 32;
     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 static char PageTypeChar(uint16_t type) {
    169   if (kDexSectionInfoMap.find(type) == kDexSectionInfoMap.end()) {
    170     return '-';
    171   }
    172   return kDexSectionInfoMap.find(type)->second.letter;
    173 }
    174 
    175 static uint16_t FindSectionTypeForPage(size_t page,
    176                                        const std::vector<dex_ir::DexFileSection>& sections) {
    177   for (const auto& section : sections) {
    178     size_t first_page_of_section = section.offset / kPageSize;
    179     // Only consider non-empty sections.
    180     if (section.size == 0) {
    181       continue;
    182     }
    183     // Attribute the page to the highest-offset section that starts before the page.
    184     if (first_page_of_section <= page) {
    185       return section.type;
    186     }
    187   }
    188   // If there's no non-zero sized section with an offset below offset we're looking for, it
    189   // must be the header.
    190   return DexFile::kDexTypeHeaderItem;
    191 }
    192 
    193 static void ProcessPageMap(uint64_t* pagemap,
    194                            size_t start,
    195                            size_t end,
    196                            const std::vector<dex_ir::DexFileSection>& sections,
    197                            PageCount* page_counts) {
    198   for (size_t page = start; page < end; ++page) {
    199     char type_char = '.';
    200     if (PM_PAGEMAP_PRESENT(pagemap[page])) {
    201       const size_t dex_page_offset = page - start;
    202       uint16_t type = FindSectionTypeForPage(dex_page_offset, sections);
    203       page_counts->Increment(type);
    204       type_char = PageTypeChar(type);
    205     }
    206     if (g_verbose) {
    207       std::cout << type_char;
    208       if ((page - start) % kLineLength == kLineLength - 1) {
    209         std::cout << std::endl;
    210       }
    211     }
    212   }
    213   if (g_verbose) {
    214     if ((end - start) % kLineLength != 0) {
    215       std::cout << std::endl;
    216     }
    217   }
    218 }
    219 
    220 static void DisplayDexStatistics(size_t start,
    221                                  size_t end,
    222                                  const PageCount& resident_pages,
    223                                  const std::vector<dex_ir::DexFileSection>& sections,
    224                                  Printer* printer) {
    225   // Compute the total possible sizes for sections.
    226   PageCount mapped_pages;
    227   DCHECK_GE(end, start);
    228   size_t total_mapped_pages = end - start;
    229   if (total_mapped_pages == 0) {
    230     return;
    231   }
    232   for (size_t page = start; page < end; ++page) {
    233     const size_t dex_page_offset = page - start;
    234     mapped_pages.Increment(FindSectionTypeForPage(dex_page_offset, sections));
    235   }
    236   size_t total_resident_pages = 0;
    237   printer->PrintHeader();
    238   for (size_t i = sections.size(); i > 0; --i) {
    239     const dex_ir::DexFileSection& section = sections[i - 1];
    240     const uint16_t type = section.type;
    241     const DexSectionInfo& section_info = kDexSectionInfoMap.find(type)->second;
    242     size_t pages_resident = resident_pages.Get(type);
    243     double percent_resident = 0;
    244     if (mapped_pages.Get(type) > 0) {
    245       percent_resident = 100.0 * pages_resident / mapped_pages.Get(type);
    246     }
    247     printer->PrintOne(section_info.name.c_str(),
    248                       pages_resident,
    249                       mapped_pages.Get(type),
    250                       percent_resident,
    251                       100.0 * pages_resident / total_mapped_pages);
    252     total_resident_pages += pages_resident;
    253   }
    254   double percent_of_total = 100.0 * total_resident_pages / total_mapped_pages;
    255   printer->PrintOne("GRAND TOTAL",
    256                     total_resident_pages,
    257                     total_mapped_pages,
    258                     percent_of_total,
    259                     percent_of_total);
    260   printer->PrintSkipLine();
    261 }
    262 
    263 static void ProcessOneDexMapping(uint64_t* pagemap,
    264                                  uint64_t map_start,
    265                                  const DexFile* dex_file,
    266                                  uint64_t vdex_start,
    267                                  Printer* printer) {
    268   uint64_t dex_file_start = reinterpret_cast<uint64_t>(dex_file->Begin());
    269   size_t dex_file_size = dex_file->Size();
    270   if (dex_file_start < vdex_start) {
    271     std::cerr << "Dex file start offset for "
    272               << dex_file->GetLocation().c_str()
    273               << " is incorrect: map start "
    274               << StringPrintf("%" PRIx64 " > dex start %" PRIx64 "\n", map_start, dex_file_start)
    275               << std::endl;
    276     return;
    277   }
    278   uint64_t start_page = (dex_file_start - vdex_start) / kPageSize;
    279   uint64_t start_address = start_page * kPageSize;
    280   uint64_t end_page = RoundUp(start_address + dex_file_size, kPageSize) / kPageSize;
    281   std::cout << "DEX "
    282             << dex_file->GetLocation().c_str()
    283             << StringPrintf(": %" PRIx64 "-%" PRIx64,
    284                             map_start + start_page * kPageSize,
    285                             map_start + end_page * kPageSize)
    286             << std::endl;
    287   // Build a list of the dex file section types, sorted from highest offset to lowest.
    288   std::vector<dex_ir::DexFileSection> sections;
    289   {
    290     std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file));
    291     sections = dex_ir::GetSortedDexFileSections(header.get(),
    292                                                 dex_ir::SortDirection::kSortDescending);
    293   }
    294   PageCount section_resident_pages;
    295   ProcessPageMap(pagemap, start_page, end_page, sections, &section_resident_pages);
    296   DisplayDexStatistics(start_page, end_page, section_resident_pages, sections, printer);
    297 }
    298 
    299 static bool DisplayMappingIfFromVdexFile(pm_map_t* map, Printer* printer) {
    300   // Confirm that the map is from a vdex file.
    301   static const char* suffixes[] = { ".vdex" };
    302   std::string vdex_name;
    303   bool found = false;
    304   for (size_t j = 0; j < sizeof(suffixes) / sizeof(suffixes[0]); ++j) {
    305     if (strstr(pm_map_name(map), suffixes[j]) != nullptr) {
    306       vdex_name = pm_map_name(map);
    307       found = true;
    308       break;
    309     }
    310   }
    311   if (!found) {
    312     return true;
    313   }
    314   // Extract all the dex files from the vdex file.
    315   std::string error_msg;
    316   std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_name,
    317                                                 false /*writeable*/,
    318                                                 false /*low_4gb*/,
    319                                                 false /*unquicken */,
    320                                                 &error_msg /*out*/));
    321   if (vdex == nullptr) {
    322     std::cerr << "Could not open vdex file "
    323               << vdex_name
    324               << ": error "
    325               << error_msg
    326               << std::endl;
    327     return false;
    328   }
    329 
    330   std::vector<std::unique_ptr<const DexFile>> dex_files;
    331   if (!vdex->OpenAllDexFiles(&dex_files, &error_msg)) {
    332     std::cerr << "Dex files could not be opened for "
    333               << vdex_name
    334               << ": error "
    335               << error_msg
    336               << std::endl;
    337   }
    338   // Open the page mapping (one uint64_t per page) for the entire vdex mapping.
    339   uint64_t* pagemap;
    340   size_t len;
    341   if (pm_map_pagemap(map, &pagemap, &len) != 0) {
    342     std::cerr << "Error creating pagemap." << std::endl;
    343     return false;
    344   }
    345   // Process the dex files.
    346   std::cout << "MAPPING "
    347             << pm_map_name(map)
    348             << StringPrintf(": %" PRIx64 "-%" PRIx64, pm_map_start(map), pm_map_end(map))
    349             << std::endl;
    350   for (const auto& dex_file : dex_files) {
    351     ProcessOneDexMapping(pagemap,
    352                          pm_map_start(map),
    353                          dex_file.get(),
    354                          reinterpret_cast<uint64_t>(vdex->Begin()),
    355                          printer);
    356   }
    357   free(pagemap);
    358   return true;
    359 }
    360 
    361 static void ProcessOneOatMapping(uint64_t* pagemap, size_t size, Printer* printer) {
    362   size_t resident_page_count = 0;
    363   for (size_t page = 0; page < size; ++page) {
    364     char type_char = '.';
    365     if (PM_PAGEMAP_PRESENT(pagemap[page])) {
    366       ++resident_page_count;
    367       type_char = '*';
    368     }
    369     if (g_verbose) {
    370       std::cout << type_char;
    371       if (page % kLineLength == kLineLength - 1) {
    372         std::cout << std::endl;
    373       }
    374     }
    375   }
    376   if (g_verbose) {
    377     if (size % kLineLength != 0) {
    378       std::cout << std::endl;
    379     }
    380   }
    381   double percent_of_total = 100.0 * resident_page_count / size;
    382   printer->PrintHeader();
    383   printer->PrintOne("EXECUTABLE", resident_page_count, size, percent_of_total, percent_of_total);
    384   printer->PrintSkipLine();
    385 }
    386 
    387 static bool DisplayMappingIfFromOatFile(pm_map_t* map, Printer* printer) {
    388   // Confirm that the map is from a vdex file.
    389   static const char* suffixes[] = { ".odex", ".oat" };
    390   std::string vdex_name;
    391   bool found = false;
    392   for (size_t j = 0; j < sizeof(suffixes) / sizeof(suffixes[0]); ++j) {
    393     if (strstr(pm_map_name(map), suffixes[j]) != nullptr) {
    394       vdex_name = pm_map_name(map);
    395       found = true;
    396       break;
    397     }
    398   }
    399   if (!found) {
    400     return true;
    401   }
    402   // Open the page mapping (one uint64_t per page) for the entire vdex mapping.
    403   uint64_t* pagemap;
    404   size_t len;
    405   if (pm_map_pagemap(map, &pagemap, &len) != 0) {
    406     std::cerr << "Error creating pagemap." << std::endl;
    407     return false;
    408   }
    409   // Process the dex files.
    410   std::cout << "MAPPING "
    411             << pm_map_name(map)
    412             << StringPrintf(": %" PRIx64 "-%" PRIx64, pm_map_start(map), pm_map_end(map))
    413             << std::endl;
    414   ProcessOneOatMapping(pagemap, len, printer);
    415   free(pagemap);
    416   return true;
    417 }
    418 
    419 static bool FilterByNameContains(const std::string& mapped_file_name,
    420                                  const std::vector<std::string>& name_filters) {
    421   // If no filters were set, everything matches.
    422   if (name_filters.empty()) {
    423     return true;
    424   }
    425   for (const auto& name_contains : name_filters) {
    426     if (mapped_file_name.find(name_contains) != std::string::npos) {
    427       return true;
    428     }
    429   }
    430   return false;
    431 }
    432 
    433 static void Usage(const char* cmd) {
    434   std::cerr << "Usage: " << cmd << " [options] pid" << std::endl
    435             << "    --contains=<string>:  Display sections containing string." << std::endl
    436             << "    --help:               Shows this message." << std::endl
    437             << "    --verbose:            Makes displays verbose." << std::endl;
    438   PrintLetterKey();
    439 }
    440 
    441 static int DexDiagMain(int argc, char* argv[]) {
    442   if (argc < 2) {
    443     Usage(argv[0]);
    444     return EXIT_FAILURE;
    445   }
    446 
    447   std::vector<std::string> name_filters;
    448   // TODO: add option to track usage by class name, etc.
    449   for (int i = 1; i < argc - 1; ++i) {
    450     const StringPiece option(argv[i]);
    451     if (option == "--help") {
    452       Usage(argv[0]);
    453       return EXIT_SUCCESS;
    454     } else if (option == "--verbose") {
    455       g_verbose = true;
    456     } else if (option.starts_with("--contains=")) {
    457       std::string contains(option.substr(strlen("--contains=")).data());
    458       name_filters.push_back(contains);
    459     } else {
    460       Usage(argv[0]);
    461       return EXIT_FAILURE;
    462     }
    463   }
    464 
    465   // Art specific set up.
    466   InitLogging(argv, Runtime::Aborter);
    467   MemMap::Init();
    468 
    469   pid_t pid;
    470   char* endptr;
    471   pid = (pid_t)strtol(argv[argc - 1], &endptr, 10);
    472   if (*endptr != '\0' || kill(pid, 0) != 0) {
    473     std::cerr << StringPrintf("Invalid PID \"%s\".\n", argv[argc - 1]) << std::endl;
    474     return EXIT_FAILURE;
    475   }
    476 
    477   // get libpagemap kernel information.
    478   pm_kernel_t* ker;
    479   if (pm_kernel_create(&ker) != 0) {
    480     std::cerr << "Error creating kernel interface -- does this kernel have pagemap?" << std::endl;
    481     return EXIT_FAILURE;
    482   }
    483 
    484   // get libpagemap process information.
    485   pm_process_t* proc;
    486   if (pm_process_create(ker, pid, &proc) != 0) {
    487     std::cerr << "Error creating process interface -- does process "
    488               << pid
    489               << " really exist?"
    490               << std::endl;
    491     return EXIT_FAILURE;
    492   }
    493 
    494   // Get the set of mappings by the specified process.
    495   pm_map_t** maps;
    496   size_t num_maps;
    497   if (pm_process_maps(proc, &maps, &num_maps) != 0) {
    498     std::cerr << "Error listing maps." << std::endl;
    499     return EXIT_FAILURE;
    500   }
    501 
    502   // Process the mappings that are due to DEX files.
    503   Printer printer;
    504   for (size_t i = 0; i < num_maps; ++i) {
    505     std::string mapped_file_name = pm_map_name(maps[i]);
    506     // Filter by name contains options (if any).
    507     if (!FilterByNameContains(mapped_file_name, name_filters)) {
    508       continue;
    509     }
    510     if (!DisplayMappingIfFromVdexFile(maps[i], &printer)) {
    511       return EXIT_FAILURE;
    512     } else if (!DisplayMappingIfFromOatFile(maps[i], &printer)) {
    513       return EXIT_FAILURE;
    514     }
    515   }
    516 
    517   return EXIT_SUCCESS;
    518 }
    519 
    520 }  // namespace art
    521 
    522 int main(int argc, char* argv[]) {
    523   return art::DexDiagMain(argc, argv);
    524 }
    525