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