blob: 8997146c7456be3c7dc7a1309e550b0f0d919759 [file] [log] [blame]
David Sehrcdcfde72016-09-26 07:44:04 -07001/*
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"
Calin Juravle33083d62017-01-18 15:29:12 -080034#include "jit/profile_compilation_info.h"
David Sehrcdcfde72016-09-26 07:44:04 -070035
36namespace art {
37
David Sehr93357492017-03-09 08:02:44 -080038std::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
David Sehrcdcfde72016-09-26 07:44:04 -070044struct FileSection {
45 public:
46 std::string name_;
47 uint16_t type_;
48 std::function<uint32_t(const dex_ir::Collections&)> size_fn_;
49 std::function<uint32_t(const dex_ir::Collections&)> offset_fn_;
50};
51
David Sehr93357492017-03-09 08:02:44 -080052static uint32_t HeaderOffset(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) {
53 return 0;
54}
55
56static uint32_t HeaderSize(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) {
57 // Size is in elements, so there is only one header.
58 return 1;
59}
60
David Sehrcdcfde72016-09-26 07:44:04 -070061static const std::vector<FileSection> kFileSections = {
62 {
David Sehr93357492017-03-09 08:02:44 -080063 "Header",
64 DexFile::kDexTypeHeaderItem,
65 &HeaderSize,
66 &HeaderOffset,
67 }, {
David Sehrcdcfde72016-09-26 07:44:04 -070068 "StringId",
69 DexFile::kDexTypeStringIdItem,
70 &dex_ir::Collections::StringIdsSize,
71 &dex_ir::Collections::StringIdsOffset
72 }, {
73 "TypeId",
74 DexFile::kDexTypeTypeIdItem,
75 &dex_ir::Collections::TypeIdsSize,
76 &dex_ir::Collections::TypeIdsOffset
77 }, {
78 "ProtoId",
79 DexFile::kDexTypeProtoIdItem,
80 &dex_ir::Collections::ProtoIdsSize,
81 &dex_ir::Collections::ProtoIdsOffset
82 }, {
83 "FieldId",
84 DexFile::kDexTypeFieldIdItem,
85 &dex_ir::Collections::FieldIdsSize,
86 &dex_ir::Collections::FieldIdsOffset
87 }, {
88 "MethodId",
89 DexFile::kDexTypeMethodIdItem,
90 &dex_ir::Collections::MethodIdsSize,
91 &dex_ir::Collections::MethodIdsOffset
92 }, {
93 "ClassDef",
94 DexFile::kDexTypeClassDefItem,
95 &dex_ir::Collections::ClassDefsSize,
96 &dex_ir::Collections::ClassDefsOffset
97 }, {
98 "StringData",
99 DexFile::kDexTypeStringDataItem,
100 &dex_ir::Collections::StringDatasSize,
101 &dex_ir::Collections::StringDatasOffset
102 }, {
103 "TypeList",
104 DexFile::kDexTypeTypeList,
105 &dex_ir::Collections::TypeListsSize,
106 &dex_ir::Collections::TypeListsOffset
107 }, {
108 "EncArr",
109 DexFile::kDexTypeEncodedArrayItem,
Jeff Haoa8621002016-10-04 18:13:44 +0000110 &dex_ir::Collections::EncodedArrayItemsSize,
111 &dex_ir::Collections::EncodedArrayItemsOffset
David Sehrcdcfde72016-09-26 07:44:04 -0700112 }, {
113 "Annotation",
114 DexFile::kDexTypeAnnotationItem,
Jeff Haoa8621002016-10-04 18:13:44 +0000115 &dex_ir::Collections::AnnotationItemsSize,
116 &dex_ir::Collections::AnnotationItemsOffset
David Sehrcdcfde72016-09-26 07:44:04 -0700117 }, {
118 "AnnoSet",
119 DexFile::kDexTypeAnnotationSetItem,
Jeff Haoa8621002016-10-04 18:13:44 +0000120 &dex_ir::Collections::AnnotationSetItemsSize,
121 &dex_ir::Collections::AnnotationSetItemsOffset
David Sehrcdcfde72016-09-26 07:44:04 -0700122 }, {
123 "AnnoSetRL",
124 DexFile::kDexTypeAnnotationSetRefList,
125 &dex_ir::Collections::AnnotationSetRefListsSize,
126 &dex_ir::Collections::AnnotationSetRefListsOffset
127 }, {
128 "AnnoDir",
129 DexFile::kDexTypeAnnotationsDirectoryItem,
Jeff Haoa8621002016-10-04 18:13:44 +0000130 &dex_ir::Collections::AnnotationsDirectoryItemsSize,
131 &dex_ir::Collections::AnnotationsDirectoryItemsOffset
David Sehrcdcfde72016-09-26 07:44:04 -0700132 }, {
133 "DebugInfo",
134 DexFile::kDexTypeDebugInfoItem,
Jeff Haoa8621002016-10-04 18:13:44 +0000135 &dex_ir::Collections::DebugInfoItemsSize,
136 &dex_ir::Collections::DebugInfoItemsOffset
David Sehrcdcfde72016-09-26 07:44:04 -0700137 }, {
138 "CodeItem",
139 DexFile::kDexTypeCodeItem,
140 &dex_ir::Collections::CodeItemsSize,
141 &dex_ir::Collections::CodeItemsOffset
142 }, {
143 "ClassData",
144 DexFile::kDexTypeClassDataItem,
145 &dex_ir::Collections::ClassDatasSize,
146 &dex_ir::Collections::ClassDatasOffset
147 }
148};
149
David Sehr93357492017-03-09 08:02:44 -0800150static constexpr bool kSortAscending = false;
151static constexpr bool kSortDescending = true;
152
153static std::vector<const FileSection*> GetSortedSections(
154 const dex_ir::Collections& collections,
155 bool sort_descending) {
156 std::vector<const FileSection*> sorted_sections;
157 // Build the table that will map from offset to color
158 for (const FileSection& s : kFileSections) {
159 sorted_sections.push_back(&s);
160 }
161 // Sort by offset.
162 std::sort(sorted_sections.begin(),
163 sorted_sections.end(),
164 [&](const FileSection* a, const FileSection* b) {
165 if (sort_descending) {
166 return a->offset_fn_(collections) > b->offset_fn_(collections);
167 } else {
168 return a->offset_fn_(collections) < b->offset_fn_(collections);
169 }
170 });
171 return sorted_sections;
172}
173
David Sehrcdcfde72016-09-26 07:44:04 -0700174class Dumper {
175 public:
176 // Colors are based on the type of the section in MapList.
David Sehr93357492017-03-09 08:02:44 -0800177 explicit Dumper(const dex_ir::Collections& collections)
178 : collections_(collections), out_file_(nullptr),
179 sorted_sections_(GetSortedSections(collections, kSortDescending)) { }
180
181 bool OpenAndPrintHeader(size_t dex_index) {
David Sehrcdcfde72016-09-26 07:44:04 -0700182 // Open the file and emit the gnuplot prologue.
David Sehr93357492017-03-09 08:02:44 -0800183 out_file_ = fopen(MultidexName("layout", dex_index, ".gnuplot").c_str(), "w");
184 if (out_file_ == nullptr) {
185 return false;
David Sehrcdcfde72016-09-26 07:44:04 -0700186 }
David Sehrcdcfde72016-09-26 07:44:04 -0700187 fprintf(out_file_, "set terminal png size 1920,1080\n");
David Sehr93357492017-03-09 08:02:44 -0800188 fprintf(out_file_, "set output \"%s\"\n", MultidexName("layout", dex_index, ".png").c_str());
189 fprintf(out_file_, "set title \"%s\"\n", MultidexName("classes", dex_index, ".dex").c_str());
David Sehrcdcfde72016-09-26 07:44:04 -0700190 fprintf(out_file_, "set xlabel \"Page offset into dex\"\n");
191 fprintf(out_file_, "set ylabel \"ClassDef index\"\n");
192 fprintf(out_file_, "set xtics rotate out (");
David Sehrcdcfde72016-09-26 07:44:04 -0700193 bool printed_one = false;
194 for (const FileSection& s : kFileSections) {
David Sehr93357492017-03-09 08:02:44 -0800195 if (s.size_fn_(collections_) > 0) {
David Sehrcdcfde72016-09-26 07:44:04 -0700196 if (printed_one) {
197 fprintf(out_file_, ", ");
198 }
David Sehr93357492017-03-09 08:02:44 -0800199 fprintf(out_file_, "\"%s\" %d", s.name_.c_str(), s.offset_fn_(collections_) / kPageSize);
David Sehrcdcfde72016-09-26 07:44:04 -0700200 printed_one = true;
201 }
202 }
203 fprintf(out_file_, ")\n");
204 fprintf(out_file_,
205 "plot \"-\" using 1:2:3:4:5 with vector nohead linewidth 1 lc variable notitle\n");
David Sehr93357492017-03-09 08:02:44 -0800206 return true;
David Sehrcdcfde72016-09-26 07:44:04 -0700207 }
208
209 int GetColor(uint32_t offset) const {
210 // The dread linear search to find the right section for the reference.
211 uint16_t section = 0;
David Sehr93357492017-03-09 08:02:44 -0800212 for (const FileSection* file_section : sorted_sections_) {
213 if (file_section->offset_fn_(collections_) < offset) {
214 section = file_section->type_;
David Sehrcdcfde72016-09-26 07:44:04 -0700215 break;
216 }
217 }
218 // And a lookup table from type to color.
219 ColorMapType::const_iterator iter = kColorMap.find(section);
220 if (iter != kColorMap.end()) {
221 return iter->second;
222 }
223 return 0;
224 }
225
226 void DumpAddressRange(uint32_t from, uint32_t size, int class_index) {
227 const uint32_t low_page = from / kPageSize;
228 const uint32_t high_page = (size > 0) ? (from + size - 1) / kPageSize : low_page;
229 const uint32_t size_delta = high_page - low_page;
230 fprintf(out_file_, "%d %d %d 0 %d\n", low_page, class_index, size_delta, GetColor(from));
231 }
232
233 void DumpAddressRange(const dex_ir::Item* item, int class_index) {
234 if (item != nullptr) {
235 DumpAddressRange(item->GetOffset(), item->GetSize(), class_index);
236 }
237 }
238
239 void DumpStringData(const dex_ir::StringData* string_data, int class_index) {
240 DumpAddressRange(string_data, class_index);
241 }
242
243 void DumpStringId(const dex_ir::StringId* string_id, int class_index) {
244 DumpAddressRange(string_id, class_index);
245 if (string_id == nullptr) {
246 return;
247 }
248 DumpStringData(string_id->DataItem(), class_index);
249 }
250
251 void DumpTypeId(const dex_ir::TypeId* type_id, int class_index) {
252 DumpAddressRange(type_id, class_index);
253 DumpStringId(type_id->GetStringId(), class_index);
254 }
255
256 void DumpFieldId(const dex_ir::FieldId* field_id, int class_index) {
257 DumpAddressRange(field_id, class_index);
258 if (field_id == nullptr) {
259 return;
260 }
261 DumpTypeId(field_id->Class(), class_index);
262 DumpTypeId(field_id->Type(), class_index);
263 DumpStringId(field_id->Name(), class_index);
264 }
265
266 void DumpFieldItem(const dex_ir::FieldItem* field, int class_index) {
267 DumpAddressRange(field, class_index);
268 if (field == nullptr) {
269 return;
270 }
271 DumpFieldId(field->GetFieldId(), class_index);
272 }
273
274 void DumpProtoId(const dex_ir::ProtoId* proto_id, int class_index) {
275 DumpAddressRange(proto_id, class_index);
276 if (proto_id == nullptr) {
277 return;
278 }
279 DumpStringId(proto_id->Shorty(), class_index);
Jeff Haoa8621002016-10-04 18:13:44 +0000280 const dex_ir::TypeList* type_list = proto_id->Parameters();
281 if (type_list != nullptr) {
282 for (const dex_ir::TypeId* t : *type_list->GetTypeList()) {
283 DumpTypeId(t, class_index);
284 }
David Sehrcdcfde72016-09-26 07:44:04 -0700285 }
286 DumpTypeId(proto_id->ReturnType(), class_index);
287 }
288
289 void DumpMethodId(const dex_ir::MethodId* method_id, int class_index) {
290 DumpAddressRange(method_id, class_index);
291 if (method_id == nullptr) {
292 return;
293 }
294 DumpTypeId(method_id->Class(), class_index);
295 DumpProtoId(method_id->Proto(), class_index);
296 DumpStringId(method_id->Name(), class_index);
297 }
298
Jeff Haoea7c6292016-11-14 18:10:16 -0800299 void DumpMethodItem(dex_ir::MethodItem* method,
300 const DexFile* dex_file,
301 int class_index,
302 ProfileCompilationInfo* profile_info) {
303 if (profile_info != nullptr) {
David Sehrcdcfde72016-09-26 07:44:04 -0700304 uint32_t method_idx = method->GetMethodId()->GetIndex();
Jeff Haoea7c6292016-11-14 18:10:16 -0800305 if (!profile_info->ContainsMethod(MethodReference(dex_file, method_idx))) {
David Sehrcdcfde72016-09-26 07:44:04 -0700306 return;
307 }
308 }
309 DumpAddressRange(method, class_index);
310 if (method == nullptr) {
311 return;
312 }
313 DumpMethodId(method->GetMethodId(), class_index);
314 const dex_ir::CodeItem* code_item = method->GetCodeItem();
315 if (code_item != nullptr) {
316 DumpAddressRange(code_item, class_index);
David Sehrd1e44e22016-10-06 17:09:32 -0700317 const dex_ir::CodeFixups* fixups = code_item->GetCodeFixups();
318 if (fixups != nullptr) {
319 std::vector<dex_ir::TypeId*>* type_ids = fixups->TypeIds();
320 for (dex_ir::TypeId* type_id : *type_ids) {
321 DumpTypeId(type_id, class_index);
322 }
323 std::vector<dex_ir::StringId*>* string_ids = fixups->StringIds();
324 for (dex_ir::StringId* string_id : *string_ids) {
325 DumpStringId(string_id, class_index);
326 }
327 std::vector<dex_ir::MethodId*>* method_ids = fixups->MethodIds();
328 for (dex_ir::MethodId* method_id : *method_ids) {
329 DumpMethodId(method_id, class_index);
330 }
331 std::vector<dex_ir::FieldId*>* field_ids = fixups->FieldIds();
332 for (dex_ir::FieldId* field_id : *field_ids) {
333 DumpFieldId(field_id, class_index);
334 }
335 }
David Sehrcdcfde72016-09-26 07:44:04 -0700336 }
337 }
338
339 ~Dumper() {
340 fclose(out_file_);
341 }
342
343 private:
David Sehrcdcfde72016-09-26 07:44:04 -0700344 using ColorMapType = std::map<uint16_t, int>;
345 const ColorMapType kColorMap = {
346 { DexFile::kDexTypeHeaderItem, 1 },
347 { DexFile::kDexTypeStringIdItem, 2 },
348 { DexFile::kDexTypeTypeIdItem, 3 },
349 { DexFile::kDexTypeProtoIdItem, 4 },
350 { DexFile::kDexTypeFieldIdItem, 5 },
351 { DexFile::kDexTypeMethodIdItem, 6 },
352 { DexFile::kDexTypeClassDefItem, 7 },
353 { DexFile::kDexTypeTypeList, 8 },
354 { DexFile::kDexTypeAnnotationSetRefList, 9 },
355 { DexFile::kDexTypeAnnotationSetItem, 10 },
356 { DexFile::kDexTypeClassDataItem, 11 },
357 { DexFile::kDexTypeCodeItem, 12 },
358 { DexFile::kDexTypeStringDataItem, 13 },
359 { DexFile::kDexTypeDebugInfoItem, 14 },
360 { DexFile::kDexTypeAnnotationItem, 15 },
361 { DexFile::kDexTypeEncodedArrayItem, 16 },
362 { DexFile::kDexTypeAnnotationsDirectoryItem, 16 }
363 };
364
David Sehr93357492017-03-09 08:02:44 -0800365 const dex_ir::Collections& collections_;
David Sehrcdcfde72016-09-26 07:44:04 -0700366 FILE* out_file_;
David Sehr93357492017-03-09 08:02:44 -0800367 std::vector<const FileSection*> sorted_sections_;
David Sehrcdcfde72016-09-26 07:44:04 -0700368
369 DISALLOW_COPY_AND_ASSIGN(Dumper);
370};
371
372/*
373 * Dumps a gnuplot data file showing the parts of the dex_file that belong to each class.
374 * If profiling information is present, it dumps only those classes that are marked as hot.
375 */
Jeff Haoea7c6292016-11-14 18:10:16 -0800376void VisualizeDexLayout(dex_ir::Header* header,
377 const DexFile* dex_file,
378 size_t dex_file_index,
379 ProfileCompilationInfo* profile_info) {
David Sehr93357492017-03-09 08:02:44 -0800380 std::unique_ptr<Dumper> dumper(new Dumper(header->GetCollections()));
381 if (!dumper->OpenAndPrintHeader(dex_file_index)) {
382 fprintf(stderr, "Could not open output file.\n");
383 return;
384 }
David Sehrcdcfde72016-09-26 07:44:04 -0700385
386 const uint32_t class_defs_size = header->GetCollections().ClassDefsSize();
387 for (uint32_t class_index = 0; class_index < class_defs_size; class_index++) {
388 dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(class_index);
Andreas Gampea5b09a62016-11-17 15:21:22 -0800389 dex::TypeIndex type_idx(class_def->ClassType()->GetIndex());
Jeff Haoea7c6292016-11-14 18:10:16 -0800390 if (profile_info != nullptr && !profile_info->ContainsClass(*dex_file, type_idx)) {
David Sehrcdcfde72016-09-26 07:44:04 -0700391 continue;
392 }
393 dumper->DumpAddressRange(class_def, class_index);
394 // Type id.
395 dumper->DumpTypeId(class_def->ClassType(), class_index);
396 // Superclass type id.
397 dumper->DumpTypeId(class_def->Superclass(), class_index);
398 // Interfaces.
399 // TODO(jeffhao): get TypeList from class_def to use Item interface.
400 static constexpr uint32_t kInterfaceSizeKludge = 8;
401 dumper->DumpAddressRange(class_def->InterfacesOffset(), kInterfaceSizeKludge, class_index);
402 // Source file info.
403 dumper->DumpStringId(class_def->SourceFile(), class_index);
404 // Annotations.
405 dumper->DumpAddressRange(class_def->Annotations(), class_index);
406 // TODO(sehr): walk the annotations and dump them.
407 // Class data.
408 dex_ir::ClassData* class_data = class_def->GetClassData();
409 if (class_data != nullptr) {
410 dumper->DumpAddressRange(class_data, class_index);
411 if (class_data->StaticFields()) {
412 for (auto& field_item : *class_data->StaticFields()) {
413 dumper->DumpFieldItem(field_item.get(), class_index);
414 }
415 }
416 if (class_data->InstanceFields()) {
417 for (auto& field_item : *class_data->InstanceFields()) {
418 dumper->DumpFieldItem(field_item.get(), class_index);
419 }
420 }
421 if (class_data->DirectMethods()) {
422 for (auto& method_item : *class_data->DirectMethods()) {
Jeff Haoea7c6292016-11-14 18:10:16 -0800423 dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info);
David Sehrcdcfde72016-09-26 07:44:04 -0700424 }
425 }
426 if (class_data->VirtualMethods()) {
427 for (auto& method_item : *class_data->VirtualMethods()) {
Jeff Haoea7c6292016-11-14 18:10:16 -0800428 dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info);
David Sehrcdcfde72016-09-26 07:44:04 -0700429 }
430 }
431 }
432 } // for
433}
434
David Sehr93357492017-03-09 08:02:44 -0800435/*
436 * Dumps the offset and size of sections within the file.
437 */
438void ShowDexSectionStatistics(dex_ir::Header* header, size_t dex_file_index) {
439 // Compute the (multidex) class file name).
440 fprintf(stdout, "%s\n", MultidexName("classes", dex_file_index, ".dex").c_str());
441 fprintf(stdout, "section offset items\n");
442 const dex_ir::Collections& collections = header->GetCollections();
443 std::vector<const FileSection*> sorted_sections(GetSortedSections(collections, kSortAscending));
444 for (const FileSection* file_section : sorted_sections) {
445 fprintf(stdout, "%-10s 0x%08x 0x%08x\n",
446 file_section->name_.c_str(),
447 file_section->offset_fn_(collections),
448 file_section->size_fn_(collections));
449 }
450 fprintf(stdout, "\n");
451}
452
David Sehrcdcfde72016-09-26 07:44:04 -0700453} // namespace art