OSDN Git Service

Merge cherrypicks of [2481491, 2481800, 2481782, 2481665, 2481547, 2481358, 2481802...
[android-x86/art.git] / dexlayout / dex_visualize.cc
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->ContainsMethod(MethodReference(dex_file, method_idx))) {
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