Home | History | Annotate | Download | only in dexlayout
      1 /*
      2  * Copyright (C) 2016 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  * Implementation file of the dex layout visualization.
     17  *
     18  * This is a tool to read dex files into an internal representation,
     19  * reorganize the representation, and emit dex files with a better
     20  * file layout.
     21  */
     22 
     23 #include "dex_visualize.h"
     24 
     25 #include <inttypes.h>
     26 #include <stdio.h>
     27 
     28 #include <functional>
     29 #include <memory>
     30 #include <vector>
     31 
     32 #include <android-base/logging.h>
     33 
     34 #include "dex_ir.h"
     35 #include "dexlayout.h"
     36 #include "jit/profile_compilation_info.h"
     37 
     38 namespace art {
     39 
     40 static std::string MultidexName(const std::string& prefix,
     41                                 size_t dex_file_index,
     42                                 const std::string& suffix) {
     43   return prefix + ((dex_file_index > 0) ? std::to_string(dex_file_index + 1) : "") + suffix;
     44 }
     45 
     46 class Dumper {
     47  public:
     48   // Colors are based on the type of the section in MapList.
     49   explicit Dumper(dex_ir::Header* header)
     50       : out_file_(nullptr),
     51         sorted_sections_(
     52             dex_ir::GetSortedDexFileSections(header, dex_ir::SortDirection::kSortDescending)) { }
     53 
     54   bool OpenAndPrintHeader(size_t dex_index) {
     55     // Open the file and emit the gnuplot prologue.
     56     out_file_ = fopen(MultidexName("layout", dex_index, ".gnuplot").c_str(), "w");
     57     if (out_file_ == nullptr) {
     58       return false;
     59     }
     60     fprintf(out_file_, "set terminal png size 1920,1080\n");
     61     fprintf(out_file_, "set output \"%s\"\n", MultidexName("layout", dex_index, ".png").c_str());
     62     fprintf(out_file_, "set title \"%s\"\n", MultidexName("classes", dex_index, ".dex").c_str());
     63     fprintf(out_file_, "set xlabel \"Page offset into dex\"\n");
     64     fprintf(out_file_, "set ylabel \"ClassDef index\"\n");
     65     fprintf(out_file_, "set xtics rotate out (");
     66     bool printed_one = false;
     67 
     68     for (const dex_ir::DexFileSection& s : sorted_sections_) {
     69       if (s.size > 0) {
     70         if (printed_one) {
     71           fprintf(out_file_, ", ");
     72         }
     73         fprintf(out_file_, "\"%s\" %d", s.name.c_str(), s.offset / kPageSize);
     74         printed_one = true;
     75       }
     76     }
     77     fprintf(out_file_, ")\n");
     78     fprintf(out_file_,
     79             "plot \"-\" using 1:2:3:4:5 with vector nohead linewidth 1 lc variable notitle\n");
     80     return true;
     81   }
     82 
     83   int GetColor(uint32_t offset) const {
     84     // The dread linear search to find the right section for the reference.
     85     uint16_t section = 0;
     86     for (const dex_ir::DexFileSection& file_section : sorted_sections_) {
     87       if (file_section.offset < offset) {
     88         section = file_section.type;
     89         break;
     90       }
     91     }
     92     // And a lookup table from type to color.
     93     ColorMapType::const_iterator iter = kColorMap.find(section);
     94     if (iter != kColorMap.end()) {
     95       return iter->second;
     96     }
     97     return 0;
     98   }
     99 
    100   void DumpAddressRange(uint32_t from, uint32_t size, int class_index) {
    101     const uint32_t low_page = from / kPageSize;
    102     const uint32_t high_page = (size > 0) ? (from + size - 1) / kPageSize : low_page;
    103     const uint32_t size_delta = high_page - low_page;
    104     fprintf(out_file_, "%d %d %d 0 %d\n", low_page, class_index, size_delta, GetColor(from));
    105   }
    106 
    107   void DumpAddressRange(const dex_ir::Item* item, int class_index) {
    108     if (item != nullptr) {
    109       DumpAddressRange(item->GetOffset(), item->GetSize(), class_index);
    110     }
    111   }
    112 
    113   void DumpStringData(const dex_ir::StringData* string_data, int class_index) {
    114     DumpAddressRange(string_data, class_index);
    115   }
    116 
    117   void DumpStringId(const dex_ir::StringId* string_id, int class_index) {
    118     DumpAddressRange(string_id, class_index);
    119     if (string_id == nullptr) {
    120       return;
    121     }
    122     DumpStringData(string_id->DataItem(), class_index);
    123   }
    124 
    125   void DumpTypeId(const dex_ir::TypeId* type_id, int class_index) {
    126     DumpAddressRange(type_id, class_index);
    127     DumpStringId(type_id->GetStringId(), class_index);
    128   }
    129 
    130   void DumpFieldId(const dex_ir::FieldId* field_id, int class_index) {
    131     DumpAddressRange(field_id, class_index);
    132     if (field_id == nullptr) {
    133       return;
    134     }
    135     DumpTypeId(field_id->Class(), class_index);
    136     DumpTypeId(field_id->Type(), class_index);
    137     DumpStringId(field_id->Name(), class_index);
    138   }
    139 
    140   void DumpFieldItem(const dex_ir::FieldItem* field, int class_index) {
    141     DumpAddressRange(field, class_index);
    142     if (field == nullptr) {
    143       return;
    144     }
    145     DumpFieldId(field->GetFieldId(), class_index);
    146   }
    147 
    148   void DumpProtoId(const dex_ir::ProtoId* proto_id, int class_index) {
    149     DumpAddressRange(proto_id, class_index);
    150     if (proto_id == nullptr) {
    151       return;
    152     }
    153     DumpStringId(proto_id->Shorty(), class_index);
    154     const dex_ir::TypeList* type_list = proto_id->Parameters();
    155     if (type_list != nullptr) {
    156       for (const dex_ir::TypeId* t : *type_list->GetTypeList()) {
    157         DumpTypeId(t, class_index);
    158       }
    159     }
    160     DumpTypeId(proto_id->ReturnType(), class_index);
    161   }
    162 
    163   void DumpMethodId(const dex_ir::MethodId* method_id, int class_index) {
    164     DumpAddressRange(method_id, class_index);
    165     if (method_id == nullptr) {
    166       return;
    167     }
    168     DumpTypeId(method_id->Class(), class_index);
    169     DumpProtoId(method_id->Proto(), class_index);
    170     DumpStringId(method_id->Name(), class_index);
    171   }
    172 
    173   void DumpMethodItem(dex_ir::MethodItem* method,
    174                       const DexFile* dex_file,
    175                       int class_index,
    176                       ProfileCompilationInfo* profile_info) {
    177     if (profile_info != nullptr) {
    178       uint32_t method_idx = method->GetMethodId()->GetIndex();
    179       if (!profile_info->GetMethodHotness(MethodReference(dex_file, method_idx)).IsHot()) {
    180         return;
    181       }
    182     }
    183     DumpAddressRange(method, class_index);
    184     if (method == nullptr) {
    185       return;
    186     }
    187     DumpMethodId(method->GetMethodId(), class_index);
    188     const dex_ir::CodeItem* code_item = method->GetCodeItem();
    189     if (code_item != nullptr) {
    190       DumpAddressRange(code_item, class_index);
    191       const dex_ir::CodeFixups* fixups = code_item->GetCodeFixups();
    192       if (fixups != nullptr) {
    193         for (dex_ir::TypeId* type_id : fixups->TypeIds()) {
    194           DumpTypeId(type_id, class_index);
    195         }
    196         for (dex_ir::StringId* string_id : fixups->StringIds()) {
    197           DumpStringId(string_id, class_index);
    198         }
    199         for (dex_ir::MethodId* method_id : fixups->MethodIds()) {
    200           DumpMethodId(method_id, class_index);
    201         }
    202         for (dex_ir::FieldId* field_id : fixups->FieldIds()) {
    203           DumpFieldId(field_id, class_index);
    204         }
    205       }
    206     }
    207   }
    208 
    209   ~Dumper() {
    210     fclose(out_file_);
    211   }
    212 
    213  private:
    214   using ColorMapType = std::map<uint16_t, int>;
    215   const ColorMapType kColorMap = {
    216     { DexFile::kDexTypeHeaderItem, 1 },
    217     { DexFile::kDexTypeStringIdItem, 2 },
    218     { DexFile::kDexTypeTypeIdItem, 3 },
    219     { DexFile::kDexTypeProtoIdItem, 4 },
    220     { DexFile::kDexTypeFieldIdItem, 5 },
    221     { DexFile::kDexTypeMethodIdItem, 6 },
    222     { DexFile::kDexTypeClassDefItem, 7 },
    223     { DexFile::kDexTypeTypeList, 8 },
    224     { DexFile::kDexTypeAnnotationSetRefList, 9 },
    225     { DexFile::kDexTypeAnnotationSetItem, 10 },
    226     { DexFile::kDexTypeClassDataItem, 11 },
    227     { DexFile::kDexTypeCodeItem, 12 },
    228     { DexFile::kDexTypeStringDataItem, 13 },
    229     { DexFile::kDexTypeDebugInfoItem, 14 },
    230     { DexFile::kDexTypeAnnotationItem, 15 },
    231     { DexFile::kDexTypeEncodedArrayItem, 16 },
    232     { DexFile::kDexTypeAnnotationsDirectoryItem, 16 }
    233   };
    234 
    235   FILE* out_file_;
    236   std::vector<dex_ir::DexFileSection> sorted_sections_;
    237 
    238   DISALLOW_COPY_AND_ASSIGN(Dumper);
    239 };
    240 
    241 /*
    242  * Dumps a gnuplot data file showing the parts of the dex_file that belong to each class.
    243  * If profiling information is present, it dumps only those classes that are marked as hot.
    244  */
    245 void VisualizeDexLayout(dex_ir::Header* header,
    246                         const DexFile* dex_file,
    247                         size_t dex_file_index,
    248                         ProfileCompilationInfo* profile_info) {
    249   std::unique_ptr<Dumper> dumper(new Dumper(header));
    250   if (!dumper->OpenAndPrintHeader(dex_file_index)) {
    251     LOG(ERROR) << "Could not open output file.";
    252     return;
    253   }
    254 
    255   const uint32_t class_defs_size = header->GetCollections().ClassDefsSize();
    256   for (uint32_t class_index = 0; class_index < class_defs_size; class_index++) {
    257     dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(class_index);
    258     dex::TypeIndex type_idx(class_def->ClassType()->GetIndex());
    259     if (profile_info != nullptr && !profile_info->ContainsClass(*dex_file, type_idx)) {
    260       continue;
    261     }
    262     dumper->DumpAddressRange(class_def, class_index);
    263     // Type id.
    264     dumper->DumpTypeId(class_def->ClassType(), class_index);
    265     // Superclass type id.
    266     dumper->DumpTypeId(class_def->Superclass(), class_index);
    267     // Interfaces.
    268     // TODO(jeffhao): get TypeList from class_def to use Item interface.
    269     static constexpr uint32_t kInterfaceSizeKludge = 8;
    270     dumper->DumpAddressRange(class_def->InterfacesOffset(), kInterfaceSizeKludge, class_index);
    271     // Source file info.
    272     dumper->DumpStringId(class_def->SourceFile(), class_index);
    273     // Annotations.
    274     dumper->DumpAddressRange(class_def->Annotations(), class_index);
    275     // TODO(sehr): walk the annotations and dump them.
    276     // Class data.
    277     dex_ir::ClassData* class_data = class_def->GetClassData();
    278     if (class_data != nullptr) {
    279       dumper->DumpAddressRange(class_data, class_index);
    280       if (class_data->StaticFields()) {
    281         for (auto& field_item : *class_data->StaticFields()) {
    282           dumper->DumpFieldItem(field_item.get(), class_index);
    283         }
    284       }
    285       if (class_data->InstanceFields()) {
    286         for (auto& field_item : *class_data->InstanceFields()) {
    287           dumper->DumpFieldItem(field_item.get(), class_index);
    288         }
    289       }
    290       if (class_data->DirectMethods()) {
    291         for (auto& method_item : *class_data->DirectMethods()) {
    292           dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info);
    293         }
    294       }
    295       if (class_data->VirtualMethods()) {
    296         for (auto& method_item : *class_data->VirtualMethods()) {
    297           dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info);
    298         }
    299       }
    300     }
    301   }  // for
    302 }
    303 
    304 static uint32_t FindNextByteAfterSection(dex_ir::Header* header,
    305                                          const std::vector<dex_ir::DexFileSection>& sorted_sections,
    306                                          size_t section_index) {
    307   for (size_t i = section_index + 1; i < sorted_sections.size(); ++i) {
    308     const dex_ir::DexFileSection& section = sorted_sections.at(i);
    309     if (section.size != 0) {
    310       return section.offset;
    311     }
    312   }
    313   return header->FileSize();
    314 }
    315 
    316 /*
    317  * Dumps the offset and size of sections within the file.
    318  */
    319 void ShowDexSectionStatistics(dex_ir::Header* header, size_t dex_file_index) {
    320   // Compute the (multidex) class file name).
    321   fprintf(stdout, "%s (%d bytes)\n",
    322           MultidexName("classes", dex_file_index, ".dex").c_str(),
    323           header->FileSize());
    324   fprintf(stdout, "section      offset    items    bytes    pages pct\n");
    325   std::vector<dex_ir::DexFileSection> sorted_sections =
    326       GetSortedDexFileSections(header, dex_ir::SortDirection::kSortAscending);
    327   for (size_t i = 0; i < sorted_sections.size(); ++i) {
    328     const dex_ir::DexFileSection& file_section = sorted_sections[i];
    329     uint32_t bytes = 0;
    330     if (file_section.size > 0) {
    331       bytes = FindNextByteAfterSection(header, sorted_sections, i) - file_section.offset;
    332     }
    333     fprintf(stdout,
    334             "%-10s %8d %8d %8d %8d %%%02d\n",
    335             file_section.name.c_str(),
    336             file_section.offset,
    337             file_section.size,
    338             bytes,
    339             RoundUp(bytes, kPageSize) / kPageSize,
    340             100 * bytes / header->FileSize());
    341   }
    342   fprintf(stdout, "\n");
    343 }
    344 
    345 }  // namespace art
    346