1 /*
2 * Copyright (C) 2011 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 <stdio.h>
18 #include <stdlib.h>
19
20 #include <fstream>
21 #include <iostream>
22 #include <map>
23 #include <set>
24 #include <string>
25 #include <unordered_map>
26 #include <unordered_set>
27 #include <vector>
28
29 #include "android-base/logging.h"
30 #include "android-base/stringprintf.h"
31 #include "android-base/strings.h"
32
33 #include "arch/instruction_set_features.h"
34 #include "art_field-inl.h"
35 #include "art_method-inl.h"
36 #include "base/bit_utils_iterator.h"
37 #include "base/os.h"
38 #include "base/safe_map.h"
39 #include "base/stl_util.h"
40 #include "base/unix_file/fd_file.h"
41 #include "class_linker-inl.h"
42 #include "class_linker.h"
43 #include "compiled_method.h"
44 #include "debug/debug_info.h"
45 #include "debug/elf_debug_writer.h"
46 #include "debug/method_debug_info.h"
47 #include "dex/code_item_accessors-inl.h"
48 #include "dex/descriptors_names.h"
49 #include "dex/dex_file-inl.h"
50 #include "dex/dex_instruction-inl.h"
51 #include "dex/string_reference.h"
52 #include "disassembler.h"
53 #include "gc/accounting/space_bitmap-inl.h"
54 #include "gc/space/image_space.h"
55 #include "gc/space/large_object_space.h"
56 #include "gc/space/space-inl.h"
57 #include "image-inl.h"
58 #include "imtable-inl.h"
59 #include "indenter.h"
60 #include "subtype_check.h"
61 #include "index_bss_mapping.h"
62 #include "interpreter/unstarted_runtime.h"
63 #include "linker/buffered_output_stream.h"
64 #include "linker/elf_builder.h"
65 #include "linker/file_output_stream.h"
66 #include "mirror/array-inl.h"
67 #include "mirror/class-inl.h"
68 #include "mirror/dex_cache-inl.h"
69 #include "mirror/object-inl.h"
70 #include "mirror/object_array-inl.h"
71 #include "oat.h"
72 #include "oat_file-inl.h"
73 #include "oat_file_manager.h"
74 #include "scoped_thread_state_change-inl.h"
75 #include "stack.h"
76 #include "stack_map.h"
77 #include "thread_list.h"
78 #include "type_lookup_table.h"
79 #include "vdex_file.h"
80 #include "verifier/method_verifier.h"
81 #include "verifier/verifier_deps.h"
82 #include "well_known_classes.h"
83
84 #include <sys/stat.h>
85 #include "cmdline.h"
86
87 namespace art {
88
89 using android::base::StringPrintf;
90
91 const char* image_methods_descriptions_[] = {
92 "kResolutionMethod",
93 "kImtConflictMethod",
94 "kImtUnimplementedMethod",
95 "kSaveAllCalleeSavesMethod",
96 "kSaveRefsOnlyMethod",
97 "kSaveRefsAndArgsMethod",
98 "kSaveEverythingMethod",
99 "kSaveEverythingMethodForClinit",
100 "kSaveEverythingMethodForSuspendCheck",
101 };
102
103 const char* image_roots_descriptions_[] = {
104 "kDexCaches",
105 "kClassRoots",
106 "kClassLoader",
107 };
108
109 // Map is so that we don't allocate multiple dex files for the same OatDexFile.
110 static std::map<const OatFile::OatDexFile*,
111 std::unique_ptr<const DexFile>> opened_dex_files;
112
OpenDexFile(const OatFile::OatDexFile * oat_dex_file,std::string * error_msg)113 const DexFile* OpenDexFile(const OatFile::OatDexFile* oat_dex_file, std::string* error_msg) {
114 DCHECK(oat_dex_file != nullptr);
115 auto it = opened_dex_files.find(oat_dex_file);
116 if (it != opened_dex_files.end()) {
117 return it->second.get();
118 }
119 const DexFile* ret = oat_dex_file->OpenDexFile(error_msg).release();
120 opened_dex_files.emplace(oat_dex_file, std::unique_ptr<const DexFile>(ret));
121 return ret;
122 }
123
124 template <typename ElfTypes>
125 class OatSymbolizer FINAL {
126 public:
OatSymbolizer(const OatFile * oat_file,const std::string & output_name,bool no_bits)127 OatSymbolizer(const OatFile* oat_file, const std::string& output_name, bool no_bits) :
128 oat_file_(oat_file),
129 builder_(nullptr),
130 output_name_(output_name.empty() ? "symbolized.oat" : output_name),
131 no_bits_(no_bits) {
132 }
133
Symbolize()134 bool Symbolize() {
135 const InstructionSet isa = oat_file_->GetOatHeader().GetInstructionSet();
136 std::unique_ptr<const InstructionSetFeatures> features = InstructionSetFeatures::FromBitmap(
137 isa, oat_file_->GetOatHeader().GetInstructionSetFeaturesBitmap());
138
139 std::unique_ptr<File> elf_file(OS::CreateEmptyFile(output_name_.c_str()));
140 if (elf_file == nullptr) {
141 return false;
142 }
143 std::unique_ptr<linker::BufferedOutputStream> output_stream =
144 std::make_unique<linker::BufferedOutputStream>(
145 std::make_unique<linker::FileOutputStream>(elf_file.get()));
146 builder_.reset(new linker::ElfBuilder<ElfTypes>(isa, features.get(), output_stream.get()));
147
148 builder_->Start();
149
150 auto* rodata = builder_->GetRoData();
151 auto* text = builder_->GetText();
152
153 const uint8_t* rodata_begin = oat_file_->Begin();
154 const size_t rodata_size = oat_file_->GetOatHeader().GetExecutableOffset();
155 if (!no_bits_) {
156 rodata->Start();
157 rodata->WriteFully(rodata_begin, rodata_size);
158 rodata->End();
159 }
160
161 const uint8_t* text_begin = oat_file_->Begin() + rodata_size;
162 const size_t text_size = oat_file_->End() - text_begin;
163 if (!no_bits_) {
164 text->Start();
165 text->WriteFully(text_begin, text_size);
166 text->End();
167 }
168
169 if (isa == InstructionSet::kMips || isa == InstructionSet::kMips64) {
170 builder_->WriteMIPSabiflagsSection();
171 }
172 builder_->PrepareDynamicSection(elf_file->GetPath(),
173 rodata_size,
174 text_size,
175 oat_file_->BssSize(),
176 oat_file_->BssMethodsOffset(),
177 oat_file_->BssRootsOffset(),
178 oat_file_->VdexSize());
179 builder_->WriteDynamicSection();
180
181 const OatHeader& oat_header = oat_file_->GetOatHeader();
182 #define DO_TRAMPOLINE(fn_name) \
183 if (oat_header.Get ## fn_name ## Offset() != 0) { \
184 debug::MethodDebugInfo info = {}; \
185 info.custom_name = #fn_name; \
186 info.isa = oat_header.GetInstructionSet(); \
187 info.is_code_address_text_relative = true; \
188 size_t code_offset = oat_header.Get ## fn_name ## Offset(); \
189 code_offset -= CompiledCode::CodeDelta(oat_header.GetInstructionSet()); \
190 info.code_address = code_offset - oat_header.GetExecutableOffset(); \
191 info.code_size = 0; /* The symbol lasts until the next symbol. */ \
192 method_debug_infos_.push_back(std::move(info)); \
193 }
194 DO_TRAMPOLINE(InterpreterToInterpreterBridge)
195 DO_TRAMPOLINE(InterpreterToCompiledCodeBridge)
196 DO_TRAMPOLINE(JniDlsymLookup);
197 DO_TRAMPOLINE(QuickGenericJniTrampoline);
198 DO_TRAMPOLINE(QuickImtConflictTrampoline);
199 DO_TRAMPOLINE(QuickResolutionTrampoline);
200 DO_TRAMPOLINE(QuickToInterpreterBridge);
201 #undef DO_TRAMPOLINE
202
203 Walk();
204
205 // TODO: Try to symbolize link-time thunks?
206 // This would require disassembling all methods to find branches outside the method code.
207
208 // TODO: Add symbols for dex bytecode in the .dex section.
209
210 debug::DebugInfo debug_info{};
211 debug_info.compiled_methods = ArrayRef<const debug::MethodDebugInfo>(method_debug_infos_);
212
213 debug::WriteDebugInfo(builder_.get(),
214 debug_info,
215 dwarf::DW_DEBUG_FRAME_FORMAT,
216 true /* write_oat_patches */);
217
218 builder_->End();
219
220 bool ret_value = builder_->Good();
221
222 builder_.reset();
223 output_stream.reset();
224
225 if (elf_file->FlushCloseOrErase() != 0) {
226 return false;
227 }
228 elf_file.reset();
229
230 return ret_value;
231 }
232
Walk()233 void Walk() {
234 std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles();
235 for (size_t i = 0; i < oat_dex_files.size(); i++) {
236 const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
237 CHECK(oat_dex_file != nullptr);
238 WalkOatDexFile(oat_dex_file);
239 }
240 }
241
WalkOatDexFile(const OatFile::OatDexFile * oat_dex_file)242 void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file) {
243 std::string error_msg;
244 const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg);
245 if (dex_file == nullptr) {
246 return;
247 }
248 for (size_t class_def_index = 0;
249 class_def_index < dex_file->NumClassDefs();
250 class_def_index++) {
251 const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
252 OatClassType type = oat_class.GetType();
253 switch (type) {
254 case kOatClassAllCompiled:
255 case kOatClassSomeCompiled:
256 WalkOatClass(oat_class, *dex_file, class_def_index);
257 break;
258
259 case kOatClassNoneCompiled:
260 case kOatClassMax:
261 // Ignore.
262 break;
263 }
264 }
265 }
266
WalkOatClass(const OatFile::OatClass & oat_class,const DexFile & dex_file,uint32_t class_def_index)267 void WalkOatClass(const OatFile::OatClass& oat_class,
268 const DexFile& dex_file,
269 uint32_t class_def_index) {
270 const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
271 const uint8_t* class_data = dex_file.GetClassData(class_def);
272 if (class_data == nullptr) { // empty class such as a marker interface?
273 return;
274 }
275 // Note: even if this is an interface or a native class, we still have to walk it, as there
276 // might be a static initializer.
277 ClassDataItemIterator it(dex_file, class_data);
278 uint32_t class_method_idx = 0;
279 it.SkipAllFields();
280 for (; it.HasNextMethod(); it.Next()) {
281 WalkOatMethod(oat_class.GetOatMethod(class_method_idx++),
282 dex_file,
283 class_def_index,
284 it.GetMemberIndex(),
285 it.GetMethodCodeItem(),
286 it.GetMethodAccessFlags());
287 }
288 DCHECK(!it.HasNext());
289 }
290
WalkOatMethod(const OatFile::OatMethod & oat_method,const DexFile & dex_file,uint32_t class_def_index,uint32_t dex_method_index,const DexFile::CodeItem * code_item,uint32_t method_access_flags)291 void WalkOatMethod(const OatFile::OatMethod& oat_method,
292 const DexFile& dex_file,
293 uint32_t class_def_index,
294 uint32_t dex_method_index,
295 const DexFile::CodeItem* code_item,
296 uint32_t method_access_flags) {
297 if ((method_access_flags & kAccAbstract) != 0) {
298 // Abstract method, no code.
299 return;
300 }
301 const OatHeader& oat_header = oat_file_->GetOatHeader();
302 const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader();
303 if (method_header == nullptr || method_header->GetCodeSize() == 0) {
304 // No code.
305 return;
306 }
307
308 uint32_t entry_point = oat_method.GetCodeOffset() - oat_header.GetExecutableOffset();
309 // Clear Thumb2 bit.
310 const void* code_address = EntryPointToCodePointer(reinterpret_cast<void*>(entry_point));
311
312 debug::MethodDebugInfo info = {};
313 DCHECK(info.custom_name.empty());
314 info.dex_file = &dex_file;
315 info.class_def_index = class_def_index;
316 info.dex_method_index = dex_method_index;
317 info.access_flags = method_access_flags;
318 info.code_item = code_item;
319 info.isa = oat_header.GetInstructionSet();
320 info.deduped = !seen_offsets_.insert(oat_method.GetCodeOffset()).second;
321 info.is_native_debuggable = oat_header.IsNativeDebuggable();
322 info.is_optimized = method_header->IsOptimized();
323 info.is_code_address_text_relative = true;
324 info.code_address = reinterpret_cast<uintptr_t>(code_address);
325 info.code_size = method_header->GetCodeSize();
326 info.frame_size_in_bytes = method_header->GetFrameSizeInBytes();
327 info.code_info = info.is_optimized ? method_header->GetOptimizedCodeInfoPtr() : nullptr;
328 info.cfi = ArrayRef<uint8_t>();
329 method_debug_infos_.push_back(info);
330 }
331
332 private:
333 const OatFile* oat_file_;
334 std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder_;
335 std::vector<debug::MethodDebugInfo> method_debug_infos_;
336 std::unordered_set<uint32_t> seen_offsets_;
337 const std::string output_name_;
338 bool no_bits_;
339 };
340
341 class OatDumperOptions {
342 public:
OatDumperOptions(bool dump_vmap,bool dump_code_info_stack_maps,bool disassemble_code,bool absolute_addresses,const char * class_filter,const char * method_filter,bool list_classes,bool list_methods,bool dump_header_only,const char * export_dex_location,const char * app_image,const char * app_oat,uint32_t addr2instr)343 OatDumperOptions(bool dump_vmap,
344 bool dump_code_info_stack_maps,
345 bool disassemble_code,
346 bool absolute_addresses,
347 const char* class_filter,
348 const char* method_filter,
349 bool list_classes,
350 bool list_methods,
351 bool dump_header_only,
352 const char* export_dex_location,
353 const char* app_image,
354 const char* app_oat,
355 uint32_t addr2instr)
356 : dump_vmap_(dump_vmap),
357 dump_code_info_stack_maps_(dump_code_info_stack_maps),
358 disassemble_code_(disassemble_code),
359 absolute_addresses_(absolute_addresses),
360 class_filter_(class_filter),
361 method_filter_(method_filter),
362 list_classes_(list_classes),
363 list_methods_(list_methods),
364 dump_header_only_(dump_header_only),
365 export_dex_location_(export_dex_location),
366 app_image_(app_image),
367 app_oat_(app_oat),
368 addr2instr_(addr2instr),
369 class_loader_(nullptr) {}
370
371 const bool dump_vmap_;
372 const bool dump_code_info_stack_maps_;
373 const bool disassemble_code_;
374 const bool absolute_addresses_;
375 const char* const class_filter_;
376 const char* const method_filter_;
377 const bool list_classes_;
378 const bool list_methods_;
379 const bool dump_header_only_;
380 const char* const export_dex_location_;
381 const char* const app_image_;
382 const char* const app_oat_;
383 uint32_t addr2instr_;
384 Handle<mirror::ClassLoader>* class_loader_;
385 };
386
387 class OatDumper {
388 public:
OatDumper(const OatFile & oat_file,const OatDumperOptions & options)389 OatDumper(const OatFile& oat_file, const OatDumperOptions& options)
390 : oat_file_(oat_file),
391 oat_dex_files_(oat_file.GetOatDexFiles()),
392 options_(options),
393 resolved_addr2instr_(0),
394 instruction_set_(oat_file_.GetOatHeader().GetInstructionSet()),
395 disassembler_(Disassembler::Create(instruction_set_,
396 new DisassemblerOptions(
397 options_.absolute_addresses_,
398 oat_file.Begin(),
399 oat_file.End(),
400 true /* can_read_literals_ */,
401 Is64BitInstructionSet(instruction_set_)
402 ? &Thread::DumpThreadOffset<PointerSize::k64>
403 : &Thread::DumpThreadOffset<PointerSize::k32>))) {
404 CHECK(options_.class_loader_ != nullptr);
405 CHECK(options_.class_filter_ != nullptr);
406 CHECK(options_.method_filter_ != nullptr);
407 AddAllOffsets();
408 }
409
~OatDumper()410 ~OatDumper() {
411 delete disassembler_;
412 }
413
GetInstructionSet()414 InstructionSet GetInstructionSet() {
415 return instruction_set_;
416 }
417
418 typedef std::vector<std::unique_ptr<const DexFile>> DexFileUniqV;
419
Dump(std::ostream & os)420 bool Dump(std::ostream& os) {
421 bool success = true;
422 const OatHeader& oat_header = oat_file_.GetOatHeader();
423
424 os << "MAGIC:\n";
425 os << oat_header.GetMagic() << "\n\n";
426
427 os << "LOCATION:\n";
428 os << oat_file_.GetLocation() << "\n\n";
429
430 os << "CHECKSUM:\n";
431 os << StringPrintf("0x%08x\n\n", oat_header.GetChecksum());
432
433 os << "INSTRUCTION SET:\n";
434 os << oat_header.GetInstructionSet() << "\n\n";
435
436 {
437 std::unique_ptr<const InstructionSetFeatures> features(
438 InstructionSetFeatures::FromBitmap(oat_header.GetInstructionSet(),
439 oat_header.GetInstructionSetFeaturesBitmap()));
440 os << "INSTRUCTION SET FEATURES:\n";
441 os << features->GetFeatureString() << "\n\n";
442 }
443
444 os << "DEX FILE COUNT:\n";
445 os << oat_header.GetDexFileCount() << "\n\n";
446
447 #define DUMP_OAT_HEADER_OFFSET(label, offset) \
448 os << label " OFFSET:\n"; \
449 os << StringPrintf("0x%08x", oat_header.offset()); \
450 if (oat_header.offset() != 0 && options_.absolute_addresses_) { \
451 os << StringPrintf(" (%p)", oat_file_.Begin() + oat_header.offset()); \
452 } \
453 os << StringPrintf("\n\n");
454
455 DUMP_OAT_HEADER_OFFSET("EXECUTABLE", GetExecutableOffset);
456 DUMP_OAT_HEADER_OFFSET("INTERPRETER TO INTERPRETER BRIDGE",
457 GetInterpreterToInterpreterBridgeOffset);
458 DUMP_OAT_HEADER_OFFSET("INTERPRETER TO COMPILED CODE BRIDGE",
459 GetInterpreterToCompiledCodeBridgeOffset);
460 DUMP_OAT_HEADER_OFFSET("JNI DLSYM LOOKUP",
461 GetJniDlsymLookupOffset);
462 DUMP_OAT_HEADER_OFFSET("QUICK GENERIC JNI TRAMPOLINE",
463 GetQuickGenericJniTrampolineOffset);
464 DUMP_OAT_HEADER_OFFSET("QUICK IMT CONFLICT TRAMPOLINE",
465 GetQuickImtConflictTrampolineOffset);
466 DUMP_OAT_HEADER_OFFSET("QUICK RESOLUTION TRAMPOLINE",
467 GetQuickResolutionTrampolineOffset);
468 DUMP_OAT_HEADER_OFFSET("QUICK TO INTERPRETER BRIDGE",
469 GetQuickToInterpreterBridgeOffset);
470 #undef DUMP_OAT_HEADER_OFFSET
471
472 os << "IMAGE PATCH DELTA:\n";
473 os << StringPrintf("%d (0x%08x)\n\n",
474 oat_header.GetImagePatchDelta(),
475 oat_header.GetImagePatchDelta());
476
477 os << "IMAGE FILE LOCATION OAT CHECKSUM:\n";
478 os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum());
479
480 os << "IMAGE FILE LOCATION OAT BEGIN:\n";
481 os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatDataBegin());
482
483 // Print the key-value store.
484 {
485 os << "KEY VALUE STORE:\n";
486 size_t index = 0;
487 const char* key;
488 const char* value;
489 while (oat_header.GetStoreKeyValuePairByIndex(index, &key, &value)) {
490 os << key << " = " << value << "\n";
491 index++;
492 }
493 os << "\n";
494 }
495
496 if (options_.absolute_addresses_) {
497 os << "BEGIN:\n";
498 os << reinterpret_cast<const void*>(oat_file_.Begin()) << "\n\n";
499
500 os << "END:\n";
501 os << reinterpret_cast<const void*>(oat_file_.End()) << "\n\n";
502 }
503
504 os << "SIZE:\n";
505 os << oat_file_.Size() << "\n\n";
506
507 os << std::flush;
508
509 // If set, adjust relative address to be searched
510 if (options_.addr2instr_ != 0) {
511 resolved_addr2instr_ = options_.addr2instr_ + oat_header.GetExecutableOffset();
512 os << "SEARCH ADDRESS (executable offset + input):\n";
513 os << StringPrintf("0x%08x\n\n", resolved_addr2instr_);
514 }
515
516 // Dumping the dex file overview is compact enough to do even if header only.
517 DexFileData cumulative;
518 for (size_t i = 0; i < oat_dex_files_.size(); i++) {
519 const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
520 CHECK(oat_dex_file != nullptr);
521 std::string error_msg;
522 const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg);
523 if (dex_file == nullptr) {
524 os << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation() << "': "
525 << error_msg;
526 continue;
527 }
528 DexFileData data(*dex_file);
529 os << "Dex file data for " << dex_file->GetLocation() << "\n";
530 data.Dump(os);
531 os << "\n";
532 const DexLayoutSections* const layout_sections = oat_dex_file->GetDexLayoutSections();
533 if (layout_sections != nullptr) {
534 os << "Layout data\n";
535 os << *layout_sections;
536 os << "\n";
537 }
538
539 cumulative.Add(data);
540
541 // Dump .bss entries.
542 DumpBssEntries(
543 os,
544 "ArtMethod",
545 oat_dex_file->GetMethodBssMapping(),
546 dex_file->NumMethodIds(),
547 static_cast<size_t>(GetInstructionSetPointerSize(instruction_set_)),
548 [=](uint32_t index) { return dex_file->PrettyMethod(index); });
549 DumpBssEntries(
550 os,
551 "Class",
552 oat_dex_file->GetTypeBssMapping(),
553 dex_file->NumTypeIds(),
554 sizeof(GcRoot<mirror::Class>),
555 [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
556 DumpBssEntries(
557 os,
558 "String",
559 oat_dex_file->GetStringBssMapping(),
560 dex_file->NumStringIds(),
561 sizeof(GcRoot<mirror::Class>),
562 [=](uint32_t index) { return dex_file->StringDataByIdx(dex::StringIndex(index)); });
563 }
564 os << "Cumulative dex file data\n";
565 cumulative.Dump(os);
566 os << "\n";
567
568 if (!options_.dump_header_only_) {
569 VariableIndentationOutputStream vios(&os);
570 VdexFile::VerifierDepsHeader vdex_header = oat_file_.GetVdexFile()->GetVerifierDepsHeader();
571 if (vdex_header.IsValid()) {
572 std::string error_msg;
573 std::vector<const DexFile*> dex_files;
574 for (size_t i = 0; i < oat_dex_files_.size(); i++) {
575 const DexFile* dex_file = OpenDexFile(oat_dex_files_[i], &error_msg);
576 if (dex_file == nullptr) {
577 os << "Error opening dex file: " << error_msg << std::endl;
578 return false;
579 }
580 dex_files.push_back(dex_file);
581 }
582 verifier::VerifierDeps deps(dex_files, oat_file_.GetVdexFile()->GetVerifierDepsData());
583 deps.Dump(&vios);
584 } else {
585 os << "UNRECOGNIZED vdex file, magic "
586 << vdex_header.GetMagic()
587 << ", verifier deps version "
588 << vdex_header.GetVerifierDepsVersion()
589 << ", dex section version "
590 << vdex_header.GetDexSectionVersion()
591 << "\n";
592 }
593 for (size_t i = 0; i < oat_dex_files_.size(); i++) {
594 const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
595 CHECK(oat_dex_file != nullptr);
596 if (!DumpOatDexFile(os, *oat_dex_file)) {
597 success = false;
598 }
599 }
600 }
601
602 if (options_.export_dex_location_) {
603 std::string error_msg;
604 std::string vdex_filename = GetVdexFilename(oat_file_.GetLocation());
605 if (!OS::FileExists(vdex_filename.c_str())) {
606 os << "File " << vdex_filename.c_str() << " does not exist\n";
607 return false;
608 }
609
610 DexFileUniqV vdex_dex_files;
611 std::unique_ptr<const VdexFile> vdex_file = OpenVdexUnquicken(vdex_filename,
612 &vdex_dex_files,
613 &error_msg);
614 if (vdex_file.get() == nullptr) {
615 os << "Failed to open vdex file: " << error_msg << "\n";
616 return false;
617 }
618 if (oat_dex_files_.size() != vdex_dex_files.size()) {
619 os << "Dex files number in Vdex file does not match Dex files number in Oat file: "
620 << vdex_dex_files.size() << " vs " << oat_dex_files_.size() << '\n';
621 return false;
622 }
623
624 size_t i = 0;
625 for (const auto& vdex_dex_file : vdex_dex_files) {
626 const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
627 CHECK(oat_dex_file != nullptr);
628 CHECK(vdex_dex_file != nullptr);
629 if (!ExportDexFile(os, *oat_dex_file, vdex_dex_file.get())) {
630 success = false;
631 }
632 i++;
633 }
634 }
635
636 {
637 os << "OAT FILE STATS:\n";
638 VariableIndentationOutputStream vios(&os);
639 stats_.Dump(vios);
640 }
641
642 os << std::flush;
643 return success;
644 }
645
ComputeSize(const void * oat_data)646 size_t ComputeSize(const void* oat_data) {
647 if (reinterpret_cast<const uint8_t*>(oat_data) < oat_file_.Begin() ||
648 reinterpret_cast<const uint8_t*>(oat_data) > oat_file_.End()) {
649 return 0; // Address not in oat file
650 }
651 uintptr_t begin_offset = reinterpret_cast<uintptr_t>(oat_data) -
652 reinterpret_cast<uintptr_t>(oat_file_.Begin());
653 auto it = offsets_.upper_bound(begin_offset);
654 CHECK(it != offsets_.end());
655 uintptr_t end_offset = *it;
656 return end_offset - begin_offset;
657 }
658
GetOatInstructionSet()659 InstructionSet GetOatInstructionSet() {
660 return oat_file_.GetOatHeader().GetInstructionSet();
661 }
662
GetQuickOatCode(ArtMethod * m)663 const void* GetQuickOatCode(ArtMethod* m) REQUIRES_SHARED(Locks::mutator_lock_) {
664 for (size_t i = 0; i < oat_dex_files_.size(); i++) {
665 const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
666 CHECK(oat_dex_file != nullptr);
667 std::string error_msg;
668 const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg);
669 if (dex_file == nullptr) {
670 LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
671 << "': " << error_msg;
672 } else {
673 const char* descriptor = m->GetDeclaringClassDescriptor();
674 const DexFile::ClassDef* class_def =
675 OatDexFile::FindClassDef(*dex_file, descriptor, ComputeModifiedUtf8Hash(descriptor));
676 if (class_def != nullptr) {
677 uint16_t class_def_index = dex_file->GetIndexForClassDef(*class_def);
678 const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
679 size_t method_index = m->GetMethodIndex();
680 return oat_class.GetOatMethod(method_index).GetQuickCode();
681 }
682 }
683 }
684 return nullptr;
685 }
686
687 // Returns nullptr and updates error_msg if the Vdex file cannot be opened, otherwise all Dex
688 // files are fully unquickened and stored in dex_files
OpenVdexUnquicken(const std::string & vdex_filename,DexFileUniqV * dex_files,std::string * error_msg)689 std::unique_ptr<const VdexFile> OpenVdexUnquicken(const std::string& vdex_filename,
690 /* out */ DexFileUniqV* dex_files,
691 /* out */ std::string* error_msg) {
692 std::unique_ptr<const File> file(OS::OpenFileForReading(vdex_filename.c_str()));
693 if (file == nullptr) {
694 *error_msg = "Could not open file " + vdex_filename + " for reading.";
695 return nullptr;
696 }
697
698 int64_t vdex_length = file->GetLength();
699 if (vdex_length == -1) {
700 *error_msg = "Could not read the length of file " + vdex_filename;
701 return nullptr;
702 }
703
704 std::unique_ptr<MemMap> mmap(MemMap::MapFile(
705 file->GetLength(),
706 PROT_READ | PROT_WRITE,
707 MAP_PRIVATE,
708 file->Fd(),
709 /* start offset */ 0,
710 /* low_4gb */ false,
711 vdex_filename.c_str(),
712 error_msg));
713 if (mmap == nullptr) {
714 *error_msg = "Failed to mmap file " + vdex_filename + ": " + *error_msg;
715 return nullptr;
716 }
717
718 std::unique_ptr<VdexFile> vdex_file(new VdexFile(mmap.release()));
719 if (!vdex_file->IsValid()) {
720 *error_msg = "Vdex file is not valid";
721 return nullptr;
722 }
723
724 DexFileUniqV tmp_dex_files;
725 if (!vdex_file->OpenAllDexFiles(&tmp_dex_files, error_msg)) {
726 *error_msg = "Failed to open Dex files from Vdex: " + *error_msg;
727 return nullptr;
728 }
729
730 vdex_file->Unquicken(MakeNonOwningPointerVector(tmp_dex_files),
731 /* decompile_return_instruction */ true);
732
733 *dex_files = std::move(tmp_dex_files);
734 return vdex_file;
735 }
736
737 struct Stats {
738 enum ByteKind {
739 kByteKindCode,
740 kByteKindQuickMethodHeader,
741 kByteKindCodeInfoLocationCatalog,
742 kByteKindCodeInfoDexRegisterMap,
743 kByteKindCodeInfoEncoding,
744 kByteKindCodeInfoInvokeInfo,
745 kByteKindCodeInfoStackMasks,
746 kByteKindCodeInfoRegisterMasks,
747 kByteKindStackMapNativePc,
748 kByteKindStackMapDexPc,
749 kByteKindStackMapDexRegisterMap,
750 kByteKindStackMapInlineInfoIndex,
751 kByteKindStackMapRegisterMaskIndex,
752 kByteKindStackMapStackMaskIndex,
753 kByteKindInlineInfoMethodIndexIdx,
754 kByteKindInlineInfoDexPc,
755 kByteKindInlineInfoExtraData,
756 kByteKindInlineInfoDexRegisterMap,
757 kByteKindInlineInfoIsLast,
758 kByteKindCount,
759 // Special ranges for std::accumulate convenience.
760 kByteKindStackMapFirst = kByteKindStackMapNativePc,
761 kByteKindStackMapLast = kByteKindStackMapStackMaskIndex,
762 kByteKindInlineInfoFirst = kByteKindInlineInfoMethodIndexIdx,
763 kByteKindInlineInfoLast = kByteKindInlineInfoIsLast,
764 };
765 int64_t bits[kByteKindCount] = {};
766 // Since code has deduplication, seen tracks already seen pointers to avoid double counting
767 // deduplicated code and tables.
768 std::unordered_set<const void*> seen;
769
770 // Returns true if it was newly added.
AddBitsIfUniqueart::OatDumper::Stats771 bool AddBitsIfUnique(ByteKind kind, int64_t count, const void* address) {
772 if (seen.insert(address).second == true) {
773 // True means the address was not already in the set.
774 AddBits(kind, count);
775 return true;
776 }
777 return false;
778 }
779
AddBitsart::OatDumper::Stats780 void AddBits(ByteKind kind, int64_t count) {
781 bits[kind] += count;
782 }
783
Dumpart::OatDumper::Stats784 void Dump(VariableIndentationOutputStream& os) {
785 const int64_t sum = std::accumulate(bits, bits + kByteKindCount, 0u);
786 os.Stream() << "Dumping cumulative use of " << sum / kBitsPerByte << " accounted bytes\n";
787 if (sum > 0) {
788 Dump(os, "Code ", bits[kByteKindCode], sum);
789 Dump(os, "QuickMethodHeader ", bits[kByteKindQuickMethodHeader], sum);
790 Dump(os, "CodeInfoEncoding ", bits[kByteKindCodeInfoEncoding], sum);
791 Dump(os, "CodeInfoLocationCatalog ", bits[kByteKindCodeInfoLocationCatalog], sum);
792 Dump(os, "CodeInfoDexRegisterMap ", bits[kByteKindCodeInfoDexRegisterMap], sum);
793 Dump(os, "CodeInfoStackMasks ", bits[kByteKindCodeInfoStackMasks], sum);
794 Dump(os, "CodeInfoRegisterMasks ", bits[kByteKindCodeInfoRegisterMasks], sum);
795 Dump(os, "CodeInfoInvokeInfo ", bits[kByteKindCodeInfoInvokeInfo], sum);
796 // Stack map section.
797 const int64_t stack_map_bits = std::accumulate(bits + kByteKindStackMapFirst,
798 bits + kByteKindStackMapLast + 1,
799 0u);
800 Dump(os, "CodeInfoStackMap ", stack_map_bits, sum);
801 {
802 ScopedIndentation indent1(&os);
803 Dump(os,
804 "StackMapNativePc ",
805 bits[kByteKindStackMapNativePc],
806 stack_map_bits,
807 "stack map");
808 Dump(os,
809 "StackMapDexPcEncoding ",
810 bits[kByteKindStackMapDexPc],
811 stack_map_bits,
812 "stack map");
813 Dump(os,
814 "StackMapDexRegisterMap ",
815 bits[kByteKindStackMapDexRegisterMap],
816 stack_map_bits,
817 "stack map");
818 Dump(os,
819 "StackMapInlineInfoIndex ",
820 bits[kByteKindStackMapInlineInfoIndex],
821 stack_map_bits,
822 "stack map");
823 Dump(os,
824 "StackMapRegisterMaskIndex ",
825 bits[kByteKindStackMapRegisterMaskIndex],
826 stack_map_bits,
827 "stack map");
828 Dump(os,
829 "StackMapStackMaskIndex ",
830 bits[kByteKindStackMapStackMaskIndex],
831 stack_map_bits,
832 "stack map");
833 }
834 // Inline info section.
835 const int64_t inline_info_bits = std::accumulate(bits + kByteKindInlineInfoFirst,
836 bits + kByteKindInlineInfoLast + 1,
837 0u);
838 Dump(os, "CodeInfoInlineInfo ", inline_info_bits, sum);
839 {
840 ScopedIndentation indent1(&os);
841 Dump(os,
842 "InlineInfoMethodIndexIdx ",
843 bits[kByteKindInlineInfoMethodIndexIdx],
844 inline_info_bits,
845 "inline info");
846 Dump(os,
847 "InlineInfoDexPc ",
848 bits[kByteKindStackMapDexPc],
849 inline_info_bits,
850 "inline info");
851 Dump(os,
852 "InlineInfoExtraData ",
853 bits[kByteKindInlineInfoExtraData],
854 inline_info_bits,
855 "inline info");
856 Dump(os,
857 "InlineInfoDexRegisterMap ",
858 bits[kByteKindInlineInfoDexRegisterMap],
859 inline_info_bits,
860 "inline info");
861 Dump(os,
862 "InlineInfoIsLast ",
863 bits[kByteKindInlineInfoIsLast],
864 inline_info_bits,
865 "inline info");
866 }
867 }
868 os.Stream() << "\n" << std::flush;
869 }
870
871 private:
Dumpart::OatDumper::Stats872 void Dump(VariableIndentationOutputStream& os,
873 const char* name,
874 int64_t size,
875 int64_t total,
876 const char* sum_of = "total") {
877 const double percent = (static_cast<double>(size) / static_cast<double>(total)) * 100;
878 os.Stream() << StringPrintf("%s = %8" PRId64 " (%2.0f%% of %s)\n",
879 name,
880 size / kBitsPerByte,
881 percent,
882 sum_of);
883 }
884 };
885
886 private:
AddAllOffsets()887 void AddAllOffsets() {
888 // We don't know the length of the code for each method, but we need to know where to stop
889 // when disassembling. What we do know is that a region of code will be followed by some other
890 // region, so if we keep a sorted sequence of the start of each region, we can infer the length
891 // of a piece of code by using upper_bound to find the start of the next region.
892 for (size_t i = 0; i < oat_dex_files_.size(); i++) {
893 const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
894 CHECK(oat_dex_file != nullptr);
895 std::string error_msg;
896 const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg);
897 if (dex_file == nullptr) {
898 LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
899 << "': " << error_msg;
900 continue;
901 }
902 offsets_.insert(reinterpret_cast<uintptr_t>(&dex_file->GetHeader()));
903 for (size_t class_def_index = 0;
904 class_def_index < dex_file->NumClassDefs();
905 class_def_index++) {
906 const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
907 const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
908 const uint8_t* class_data = dex_file->GetClassData(class_def);
909 if (class_data != nullptr) {
910 ClassDataItemIterator it(*dex_file, class_data);
911 it.SkipAllFields();
912 uint32_t class_method_index = 0;
913 while (it.HasNextMethod()) {
914 AddOffsets(oat_class.GetOatMethod(class_method_index++));
915 it.Next();
916 }
917 }
918 }
919 }
920
921 // If the last thing in the file is code for a method, there won't be an offset for the "next"
922 // thing. Instead of having a special case in the upper_bound code, let's just add an entry
923 // for the end of the file.
924 offsets_.insert(oat_file_.Size());
925 }
926
AlignCodeOffset(uint32_t maybe_thumb_offset)927 static uint32_t AlignCodeOffset(uint32_t maybe_thumb_offset) {
928 return maybe_thumb_offset & ~0x1; // TODO: Make this Thumb2 specific.
929 }
930
AddOffsets(const OatFile::OatMethod & oat_method)931 void AddOffsets(const OatFile::OatMethod& oat_method) {
932 uint32_t code_offset = oat_method.GetCodeOffset();
933 if (oat_file_.GetOatHeader().GetInstructionSet() == InstructionSet::kThumb2) {
934 code_offset &= ~0x1;
935 }
936 offsets_.insert(code_offset);
937 offsets_.insert(oat_method.GetVmapTableOffset());
938 }
939
940 // Dex file data, may be for multiple different dex files.
941 class DexFileData {
942 public:
DexFileData()943 DexFileData() {}
944
DexFileData(const DexFile & dex_file)945 explicit DexFileData(const DexFile& dex_file)
946 : num_string_ids_(dex_file.NumStringIds()),
947 num_method_ids_(dex_file.NumMethodIds()),
948 num_field_ids_(dex_file.NumFieldIds()),
949 num_type_ids_(dex_file.NumTypeIds()),
950 num_class_defs_(dex_file.NumClassDefs()) {
951 for (size_t class_def_index = 0; class_def_index < num_class_defs_; ++class_def_index) {
952 const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
953 WalkClass(dex_file, class_def);
954 }
955 }
956
Add(const DexFileData & other)957 void Add(const DexFileData& other) {
958 AddAll(unique_string_ids_from_code_, other.unique_string_ids_from_code_);
959 num_string_ids_from_code_ += other.num_string_ids_from_code_;
960 AddAll(dex_code_item_ptrs_, other.dex_code_item_ptrs_);
961 dex_code_bytes_ += other.dex_code_bytes_;
962 num_string_ids_ += other.num_string_ids_;
963 num_method_ids_ += other.num_method_ids_;
964 num_field_ids_ += other.num_field_ids_;
965 num_type_ids_ += other.num_type_ids_;
966 num_class_defs_ += other.num_class_defs_;
967 }
968
Dump(std::ostream & os)969 void Dump(std::ostream& os) {
970 os << "Num string ids: " << num_string_ids_ << "\n";
971 os << "Num method ids: " << num_method_ids_ << "\n";
972 os << "Num field ids: " << num_field_ids_ << "\n";
973 os << "Num type ids: " << num_type_ids_ << "\n";
974 os << "Num class defs: " << num_class_defs_ << "\n";
975 os << "Unique strings loaded from dex code: " << unique_string_ids_from_code_.size() << "\n";
976 os << "Total strings loaded from dex code: " << num_string_ids_from_code_ << "\n";
977 os << "Number of unique dex code items: " << dex_code_item_ptrs_.size() << "\n";
978 os << "Total number of dex code bytes: " << dex_code_bytes_ << "\n";
979 }
980
981 private:
982 // All of the elements from one container to another.
983 template <typename Dest, typename Src>
AddAll(Dest & dest,const Src & src)984 static void AddAll(Dest& dest, const Src& src) {
985 dest.insert(src.begin(), src.end());
986 }
987
WalkClass(const DexFile & dex_file,const DexFile::ClassDef & class_def)988 void WalkClass(const DexFile& dex_file, const DexFile::ClassDef& class_def) {
989 const uint8_t* class_data = dex_file.GetClassData(class_def);
990 if (class_data == nullptr) { // empty class such as a marker interface?
991 return;
992 }
993 ClassDataItemIterator it(dex_file, class_data);
994 it.SkipAllFields();
995 while (it.HasNextMethod()) {
996 WalkCodeItem(dex_file, it.GetMethodCodeItem());
997 it.Next();
998 }
999 DCHECK(!it.HasNext());
1000 }
1001
WalkCodeItem(const DexFile & dex_file,const DexFile::CodeItem * code_item)1002 void WalkCodeItem(const DexFile& dex_file, const DexFile::CodeItem* code_item) {
1003 if (code_item == nullptr) {
1004 return;
1005 }
1006 CodeItemInstructionAccessor instructions(dex_file, code_item);
1007
1008 // If we inserted a new dex code item pointer, add to total code bytes.
1009 const uint16_t* code_ptr = instructions.Insns();
1010 if (dex_code_item_ptrs_.insert(code_ptr).second) {
1011 dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]);
1012 }
1013
1014 for (const DexInstructionPcPair& inst : instructions) {
1015 switch (inst->Opcode()) {
1016 case Instruction::CONST_STRING: {
1017 const dex::StringIndex string_index(inst->VRegB_21c());
1018 unique_string_ids_from_code_.insert(StringReference(&dex_file, string_index));
1019 ++num_string_ids_from_code_;
1020 break;
1021 }
1022 case Instruction::CONST_STRING_JUMBO: {
1023 const dex::StringIndex string_index(inst->VRegB_31c());
1024 unique_string_ids_from_code_.insert(StringReference(&dex_file, string_index));
1025 ++num_string_ids_from_code_;
1026 break;
1027 }
1028 default:
1029 break;
1030 }
1031 }
1032 }
1033
1034 // Unique string ids loaded from dex code.
1035 std::set<StringReference> unique_string_ids_from_code_;
1036
1037 // Total string ids loaded from dex code.
1038 size_t num_string_ids_from_code_ = 0;
1039
1040 // Unique code pointers.
1041 std::set<const void*> dex_code_item_ptrs_;
1042
1043 // Total "unique" dex code bytes.
1044 size_t dex_code_bytes_ = 0;
1045
1046 // Other dex ids.
1047 size_t num_string_ids_ = 0;
1048 size_t num_method_ids_ = 0;
1049 size_t num_field_ids_ = 0;
1050 size_t num_type_ids_ = 0;
1051 size_t num_class_defs_ = 0;
1052 };
1053
DumpOatDexFile(std::ostream & os,const OatFile::OatDexFile & oat_dex_file)1054 bool DumpOatDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) {
1055 bool success = true;
1056 bool stop_analysis = false;
1057 os << "OatDexFile:\n";
1058 os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str());
1059 os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum());
1060
1061 const uint8_t* const oat_file_begin = oat_dex_file.GetOatFile()->Begin();
1062 if (oat_dex_file.GetOatFile()->ContainsDexCode()) {
1063 const uint8_t* const vdex_file_begin = oat_dex_file.GetOatFile()->DexBegin();
1064
1065 // Print data range of the dex file embedded inside the corresponding vdex file.
1066 const uint8_t* const dex_file_pointer = oat_dex_file.GetDexFilePointer();
1067 uint32_t dex_offset = dchecked_integral_cast<uint32_t>(dex_file_pointer - vdex_file_begin);
1068 os << StringPrintf(
1069 "dex-file: 0x%08x..0x%08x\n",
1070 dex_offset,
1071 dchecked_integral_cast<uint32_t>(dex_offset + oat_dex_file.FileSize() - 1));
1072 } else {
1073 os << StringPrintf("dex-file not in VDEX file\n");
1074 }
1075
1076 // Create the dex file early. A lot of print-out things depend on it.
1077 std::string error_msg;
1078 const DexFile* const dex_file = OpenDexFile(&oat_dex_file, &error_msg);
1079 if (dex_file == nullptr) {
1080 os << "NOT FOUND: " << error_msg << "\n\n";
1081 os << std::flush;
1082 return false;
1083 }
1084
1085 // Print lookup table, if it exists.
1086 if (oat_dex_file.GetLookupTableData() != nullptr) {
1087 uint32_t table_offset = dchecked_integral_cast<uint32_t>(
1088 oat_dex_file.GetLookupTableData() - oat_file_begin);
1089 uint32_t table_size = TypeLookupTable::RawDataLength(dex_file->NumClassDefs());
1090 os << StringPrintf("type-table: 0x%08x..0x%08x\n",
1091 table_offset,
1092 table_offset + table_size - 1);
1093 }
1094
1095 VariableIndentationOutputStream vios(&os);
1096 ScopedIndentation indent1(&vios);
1097 for (size_t class_def_index = 0;
1098 class_def_index < dex_file->NumClassDefs();
1099 class_def_index++) {
1100 const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
1101 const char* descriptor = dex_file->GetClassDescriptor(class_def);
1102
1103 // TODO: Support regex
1104 if (DescriptorToDot(descriptor).find(options_.class_filter_) == std::string::npos) {
1105 continue;
1106 }
1107
1108 uint32_t oat_class_offset = oat_dex_file.GetOatClassOffset(class_def_index);
1109 const OatFile::OatClass oat_class = oat_dex_file.GetOatClass(class_def_index);
1110 os << StringPrintf("%zd: %s (offset=0x%08x) (type_idx=%d)",
1111 class_def_index, descriptor, oat_class_offset, class_def.class_idx_.index_)
1112 << " (" << oat_class.GetStatus() << ")"
1113 << " (" << oat_class.GetType() << ")\n";
1114 // TODO: include bitmap here if type is kOatClassSomeCompiled?
1115 if (options_.list_classes_) {
1116 continue;
1117 }
1118 if (!DumpOatClass(&vios, oat_class, *dex_file, class_def, &stop_analysis)) {
1119 success = false;
1120 }
1121 if (stop_analysis) {
1122 os << std::flush;
1123 return success;
1124 }
1125 }
1126 os << "\n";
1127 os << std::flush;
1128 return success;
1129 }
1130
1131 // Backwards compatible Dex file export. If dex_file is nullptr (valid Vdex file not present) the
1132 // Dex resource is extracted from the oat_dex_file and its checksum is repaired since it's not
1133 // unquickened. Otherwise the dex_file has been fully unquickened and is expected to verify the
1134 // original checksum.
ExportDexFile(std::ostream & os,const OatFile::OatDexFile & oat_dex_file,const DexFile * dex_file)1135 bool ExportDexFile(std::ostream& os,
1136 const OatFile::OatDexFile& oat_dex_file,
1137 const DexFile* dex_file) {
1138 std::string error_msg;
1139 std::string dex_file_location = oat_dex_file.GetDexFileLocation();
1140 size_t fsize = oat_dex_file.FileSize();
1141
1142 // Some quick checks just in case
1143 if (fsize == 0 || fsize < sizeof(DexFile::Header)) {
1144 os << "Invalid dex file\n";
1145 return false;
1146 }
1147
1148 if (dex_file == nullptr) {
1149 // Exported bytecode is quickened (dex-to-dex transformations present)
1150 dex_file = OpenDexFile(&oat_dex_file, &error_msg);
1151 if (dex_file == nullptr) {
1152 os << "Failed to open dex file '" << dex_file_location << "': " << error_msg;
1153 return false;
1154 }
1155
1156 // Recompute checksum
1157 reinterpret_cast<DexFile::Header*>(const_cast<uint8_t*>(dex_file->Begin()))->checksum_ =
1158 dex_file->CalculateChecksum();
1159 } else {
1160 // Vdex unquicken output should match original input bytecode
1161 uint32_t orig_checksum =
1162 reinterpret_cast<DexFile::Header*>(const_cast<uint8_t*>(dex_file->Begin()))->checksum_;
1163 CHECK_EQ(orig_checksum, dex_file->CalculateChecksum());
1164 if (orig_checksum != dex_file->CalculateChecksum()) {
1165 os << "Unexpected checksum from unquicken dex file '" << dex_file_location << "'\n";
1166 return false;
1167 }
1168 }
1169
1170 // Update header for shared section.
1171 uint32_t shared_section_offset = 0u;
1172 uint32_t shared_section_size = 0u;
1173 if (dex_file->IsCompactDexFile()) {
1174 CompactDexFile::Header* const header =
1175 reinterpret_cast<CompactDexFile::Header*>(const_cast<uint8_t*>(dex_file->Begin()));
1176 shared_section_offset = header->data_off_;
1177 shared_section_size = header->data_size_;
1178 // The shared section will be serialized right after the dex file.
1179 header->data_off_ = header->file_size_;
1180 }
1181 // Verify output directory exists
1182 if (!OS::DirectoryExists(options_.export_dex_location_)) {
1183 // TODO: Extend OS::DirectoryExists if symlink support is required
1184 os << options_.export_dex_location_ << " output directory not found or symlink\n";
1185 return false;
1186 }
1187
1188 // Beautify path names
1189 if (dex_file_location.size() > PATH_MAX || dex_file_location.size() <= 0) {
1190 return false;
1191 }
1192
1193 std::string dex_orig_name;
1194 size_t dex_orig_pos = dex_file_location.rfind('/');
1195 if (dex_orig_pos == std::string::npos)
1196 dex_orig_name = dex_file_location;
1197 else
1198 dex_orig_name = dex_file_location.substr(dex_orig_pos + 1);
1199
1200 // A more elegant approach to efficiently name user installed apps is welcome
1201 if (dex_orig_name.size() == 8 &&
1202 dex_orig_name.compare("base.apk") == 0 &&
1203 dex_orig_pos != std::string::npos) {
1204 dex_file_location.erase(dex_orig_pos, strlen("base.apk") + 1);
1205 size_t apk_orig_pos = dex_file_location.rfind('/');
1206 if (apk_orig_pos != std::string::npos) {
1207 dex_orig_name = dex_file_location.substr(++apk_orig_pos);
1208 }
1209 }
1210
1211 std::string out_dex_path(options_.export_dex_location_);
1212 if (out_dex_path.back() != '/') {
1213 out_dex_path.append("/");
1214 }
1215 out_dex_path.append(dex_orig_name);
1216 out_dex_path.append("_export.dex");
1217 if (out_dex_path.length() > PATH_MAX) {
1218 return false;
1219 }
1220
1221 std::unique_ptr<File> file(OS::CreateEmptyFile(out_dex_path.c_str()));
1222 if (file.get() == nullptr) {
1223 os << "Failed to open output dex file " << out_dex_path;
1224 return false;
1225 }
1226
1227 bool success = file->WriteFully(dex_file->Begin(), fsize);
1228 if (!success) {
1229 os << "Failed to write dex file";
1230 file->Erase();
1231 return false;
1232 }
1233
1234 if (shared_section_size != 0) {
1235 success = file->WriteFully(dex_file->Begin() + shared_section_offset, shared_section_size);
1236 if (!success) {
1237 os << "Failed to write shared data section";
1238 file->Erase();
1239 return false;
1240 }
1241 }
1242
1243 if (file->FlushCloseOrErase() != 0) {
1244 os << "Flush and close failed";
1245 return false;
1246 }
1247
1248 os << StringPrintf("Dex file exported at %s (%zd bytes)\n", out_dex_path.c_str(), fsize);
1249 os << std::flush;
1250
1251 return true;
1252 }
1253
DumpOatClass(VariableIndentationOutputStream * vios,const OatFile::OatClass & oat_class,const DexFile & dex_file,const DexFile::ClassDef & class_def,bool * stop_analysis)1254 bool DumpOatClass(VariableIndentationOutputStream* vios,
1255 const OatFile::OatClass& oat_class, const DexFile& dex_file,
1256 const DexFile::ClassDef& class_def, bool* stop_analysis) {
1257 bool success = true;
1258 bool addr_found = false;
1259 const uint8_t* class_data = dex_file.GetClassData(class_def);
1260 if (class_data == nullptr) { // empty class such as a marker interface?
1261 vios->Stream() << std::flush;
1262 return success;
1263 }
1264 ClassDataItemIterator it(dex_file, class_data);
1265 it.SkipAllFields();
1266 uint32_t class_method_index = 0;
1267 while (it.HasNextMethod()) {
1268 if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file,
1269 it.GetMemberIndex(), it.GetMethodCodeItem(),
1270 it.GetRawMemberAccessFlags(), &addr_found)) {
1271 success = false;
1272 }
1273 if (addr_found) {
1274 *stop_analysis = true;
1275 return success;
1276 }
1277 class_method_index++;
1278 it.Next();
1279 }
1280 DCHECK(!it.HasNext());
1281 vios->Stream() << std::flush;
1282 return success;
1283 }
1284
1285 static constexpr uint32_t kPrologueBytes = 16;
1286
1287 // When this was picked, the largest arm method was 55,256 bytes and arm64 was 50,412 bytes.
1288 static constexpr uint32_t kMaxCodeSize = 100 * 1000;
1289
DumpOatMethod(VariableIndentationOutputStream * vios,const DexFile::ClassDef & class_def,uint32_t class_method_index,const OatFile::OatClass & oat_class,const DexFile & dex_file,uint32_t dex_method_idx,const DexFile::CodeItem * code_item,uint32_t method_access_flags,bool * addr_found)1290 bool DumpOatMethod(VariableIndentationOutputStream* vios,
1291 const DexFile::ClassDef& class_def,
1292 uint32_t class_method_index,
1293 const OatFile::OatClass& oat_class,
1294 const DexFile& dex_file,
1295 uint32_t dex_method_idx,
1296 const DexFile::CodeItem* code_item,
1297 uint32_t method_access_flags,
1298 bool* addr_found) {
1299 bool success = true;
1300
1301 CodeItemDataAccessor code_item_accessor(dex_file, code_item);
1302
1303 // TODO: Support regex
1304 std::string method_name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx));
1305 if (method_name.find(options_.method_filter_) == std::string::npos) {
1306 return success;
1307 }
1308
1309 std::string pretty_method = dex_file.PrettyMethod(dex_method_idx, true);
1310 vios->Stream() << StringPrintf("%d: %s (dex_method_idx=%d)\n",
1311 class_method_index, pretty_method.c_str(),
1312 dex_method_idx);
1313 if (options_.list_methods_) {
1314 return success;
1315 }
1316
1317 uint32_t oat_method_offsets_offset = oat_class.GetOatMethodOffsetsOffset(class_method_index);
1318 const OatMethodOffsets* oat_method_offsets = oat_class.GetOatMethodOffsets(class_method_index);
1319 const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
1320 uint32_t code_offset = oat_method.GetCodeOffset();
1321 uint32_t code_size = oat_method.GetQuickCodeSize();
1322 if (resolved_addr2instr_ != 0) {
1323 if (resolved_addr2instr_ > code_offset + code_size) {
1324 return success;
1325 } else {
1326 *addr_found = true; // stop analyzing file at next iteration
1327 }
1328 }
1329
1330 // Everything below is indented at least once.
1331 ScopedIndentation indent1(vios);
1332
1333 {
1334 vios->Stream() << "DEX CODE:\n";
1335 ScopedIndentation indent2(vios);
1336 if (code_item_accessor.HasCodeItem()) {
1337 for (const DexInstructionPcPair& inst : code_item_accessor) {
1338 vios->Stream() << StringPrintf("0x%04x: ", inst.DexPc()) << inst->DumpHexLE(5)
1339 << StringPrintf("\t| %s\n", inst->DumpString(&dex_file).c_str());
1340 }
1341 }
1342 }
1343
1344 std::unique_ptr<StackHandleScope<1>> hs;
1345 std::unique_ptr<verifier::MethodVerifier> verifier;
1346 if (Runtime::Current() != nullptr) {
1347 // We need to have the handle scope stay live until after the verifier since the verifier has
1348 // a handle to the dex cache from hs.
1349 hs.reset(new StackHandleScope<1>(Thread::Current()));
1350 vios->Stream() << "VERIFIER TYPE ANALYSIS:\n";
1351 ScopedIndentation indent2(vios);
1352 verifier.reset(DumpVerifier(vios, hs.get(),
1353 dex_method_idx, &dex_file, class_def, code_item,
1354 method_access_flags));
1355 }
1356 {
1357 vios->Stream() << "OatMethodOffsets ";
1358 if (options_.absolute_addresses_) {
1359 vios->Stream() << StringPrintf("%p ", oat_method_offsets);
1360 }
1361 vios->Stream() << StringPrintf("(offset=0x%08x)\n", oat_method_offsets_offset);
1362 if (oat_method_offsets_offset > oat_file_.Size()) {
1363 vios->Stream() << StringPrintf(
1364 "WARNING: oat method offsets offset 0x%08x is past end of file 0x%08zx.\n",
1365 oat_method_offsets_offset, oat_file_.Size());
1366 // If we can't read OatMethodOffsets, the rest of the data is dangerous to read.
1367 vios->Stream() << std::flush;
1368 return false;
1369 }
1370
1371 ScopedIndentation indent2(vios);
1372 vios->Stream() << StringPrintf("code_offset: 0x%08x ", code_offset);
1373 uint32_t aligned_code_begin = AlignCodeOffset(oat_method.GetCodeOffset());
1374 if (aligned_code_begin > oat_file_.Size()) {
1375 vios->Stream() << StringPrintf("WARNING: "
1376 "code offset 0x%08x is past end of file 0x%08zx.\n",
1377 aligned_code_begin, oat_file_.Size());
1378 success = false;
1379 }
1380 vios->Stream() << "\n";
1381 }
1382 {
1383 vios->Stream() << "OatQuickMethodHeader ";
1384 uint32_t method_header_offset = oat_method.GetOatQuickMethodHeaderOffset();
1385 const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader();
1386 stats_.AddBitsIfUnique(Stats::kByteKindQuickMethodHeader,
1387 sizeof(*method_header) * kBitsPerByte,
1388 method_header);
1389 if (options_.absolute_addresses_) {
1390 vios->Stream() << StringPrintf("%p ", method_header);
1391 }
1392 vios->Stream() << StringPrintf("(offset=0x%08x)\n", method_header_offset);
1393 if (method_header_offset > oat_file_.Size()) {
1394 vios->Stream() << StringPrintf(
1395 "WARNING: oat quick method header offset 0x%08x is past end of file 0x%08zx.\n",
1396 method_header_offset, oat_file_.Size());
1397 // If we can't read the OatQuickMethodHeader, the rest of the data is dangerous to read.
1398 vios->Stream() << std::flush;
1399 return false;
1400 }
1401
1402 ScopedIndentation indent2(vios);
1403 vios->Stream() << "vmap_table: ";
1404 if (options_.absolute_addresses_) {
1405 vios->Stream() << StringPrintf("%p ", oat_method.GetVmapTable());
1406 }
1407 uint32_t vmap_table_offset = method_header ==
1408 nullptr ? 0 : method_header->GetVmapTableOffset();
1409 vios->Stream() << StringPrintf("(offset=0x%08x)\n", vmap_table_offset);
1410
1411 size_t vmap_table_offset_limit =
1412 IsMethodGeneratedByDexToDexCompiler(oat_method, code_item_accessor)
1413 ? oat_file_.GetVdexFile()->Size()
1414 : method_header->GetCode() - oat_file_.Begin();
1415 if (vmap_table_offset >= vmap_table_offset_limit) {
1416 vios->Stream() << StringPrintf("WARNING: "
1417 "vmap table offset 0x%08x is past end of file 0x%08zx. "
1418 "vmap table offset was loaded from offset 0x%08x.\n",
1419 vmap_table_offset,
1420 vmap_table_offset_limit,
1421 oat_method.GetVmapTableOffsetOffset());
1422 success = false;
1423 } else if (options_.dump_vmap_) {
1424 DumpVmapData(vios, oat_method, code_item_accessor);
1425 }
1426 }
1427 {
1428 vios->Stream() << "QuickMethodFrameInfo\n";
1429
1430 ScopedIndentation indent2(vios);
1431 vios->Stream()
1432 << StringPrintf("frame_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes());
1433 vios->Stream() << StringPrintf("core_spill_mask: 0x%08x ", oat_method.GetCoreSpillMask());
1434 DumpSpillMask(vios->Stream(), oat_method.GetCoreSpillMask(), false);
1435 vios->Stream() << "\n";
1436 vios->Stream() << StringPrintf("fp_spill_mask: 0x%08x ", oat_method.GetFpSpillMask());
1437 DumpSpillMask(vios->Stream(), oat_method.GetFpSpillMask(), true);
1438 vios->Stream() << "\n";
1439 }
1440 {
1441 // Based on spill masks from QuickMethodFrameInfo so placed
1442 // after it is dumped, but useful for understanding quick
1443 // code, so dumped here.
1444 ScopedIndentation indent2(vios);
1445 DumpVregLocations(vios->Stream(), oat_method, code_item_accessor);
1446 }
1447 {
1448 vios->Stream() << "CODE: ";
1449 uint32_t code_size_offset = oat_method.GetQuickCodeSizeOffset();
1450 if (code_size_offset > oat_file_.Size()) {
1451 ScopedIndentation indent2(vios);
1452 vios->Stream() << StringPrintf("WARNING: "
1453 "code size offset 0x%08x is past end of file 0x%08zx.",
1454 code_size_offset, oat_file_.Size());
1455 success = false;
1456 } else {
1457 const void* code = oat_method.GetQuickCode();
1458 uint32_t aligned_code_begin = AlignCodeOffset(code_offset);
1459 uint64_t aligned_code_end = aligned_code_begin + code_size;
1460 stats_.AddBitsIfUnique(Stats::kByteKindCode, code_size * kBitsPerByte, code);
1461
1462 if (options_.absolute_addresses_) {
1463 vios->Stream() << StringPrintf("%p ", code);
1464 }
1465 vios->Stream() << StringPrintf("(code_offset=0x%08x size_offset=0x%08x size=%u)%s\n",
1466 code_offset,
1467 code_size_offset,
1468 code_size,
1469 code != nullptr ? "..." : "");
1470
1471 ScopedIndentation indent2(vios);
1472 if (aligned_code_begin > oat_file_.Size()) {
1473 vios->Stream() << StringPrintf("WARNING: "
1474 "start of code at 0x%08x is past end of file 0x%08zx.",
1475 aligned_code_begin, oat_file_.Size());
1476 success = false;
1477 } else if (aligned_code_end > oat_file_.Size()) {
1478 vios->Stream() << StringPrintf(
1479 "WARNING: "
1480 "end of code at 0x%08" PRIx64 " is past end of file 0x%08zx. "
1481 "code size is 0x%08x loaded from offset 0x%08x.\n",
1482 aligned_code_end, oat_file_.Size(),
1483 code_size, code_size_offset);
1484 success = false;
1485 if (options_.disassemble_code_) {
1486 if (code_size_offset + kPrologueBytes <= oat_file_.Size()) {
1487 DumpCode(vios, oat_method, code_item_accessor, true, kPrologueBytes);
1488 }
1489 }
1490 } else if (code_size > kMaxCodeSize) {
1491 vios->Stream() << StringPrintf(
1492 "WARNING: "
1493 "code size %d is bigger than max expected threshold of %d. "
1494 "code size is 0x%08x loaded from offset 0x%08x.\n",
1495 code_size, kMaxCodeSize,
1496 code_size, code_size_offset);
1497 success = false;
1498 if (options_.disassemble_code_) {
1499 if (code_size_offset + kPrologueBytes <= oat_file_.Size()) {
1500 DumpCode(vios, oat_method, code_item_accessor, true, kPrologueBytes);
1501 }
1502 }
1503 } else if (options_.disassemble_code_) {
1504 DumpCode(vios, oat_method, code_item_accessor, !success, 0);
1505 }
1506 }
1507 }
1508 vios->Stream() << std::flush;
1509 return success;
1510 }
1511
DumpSpillMask(std::ostream & os,uint32_t spill_mask,bool is_float)1512 void DumpSpillMask(std::ostream& os, uint32_t spill_mask, bool is_float) {
1513 if (spill_mask == 0) {
1514 return;
1515 }
1516 os << "(";
1517 for (size_t i = 0; i < 32; i++) {
1518 if ((spill_mask & (1 << i)) != 0) {
1519 if (is_float) {
1520 os << "fr" << i;
1521 } else {
1522 os << "r" << i;
1523 }
1524 spill_mask ^= 1 << i; // clear bit
1525 if (spill_mask != 0) {
1526 os << ", ";
1527 } else {
1528 break;
1529 }
1530 }
1531 }
1532 os << ")";
1533 }
1534
1535 // Display data stored at the the vmap offset of an oat method.
DumpVmapData(VariableIndentationOutputStream * vios,const OatFile::OatMethod & oat_method,const CodeItemDataAccessor & code_item_accessor)1536 void DumpVmapData(VariableIndentationOutputStream* vios,
1537 const OatFile::OatMethod& oat_method,
1538 const CodeItemDataAccessor& code_item_accessor) {
1539 if (IsMethodGeneratedByOptimizingCompiler(oat_method, code_item_accessor)) {
1540 // The optimizing compiler outputs its CodeInfo data in the vmap table.
1541 const void* raw_code_info = oat_method.GetVmapTable();
1542 if (raw_code_info != nullptr) {
1543 CodeInfo code_info(raw_code_info);
1544 DCHECK(code_item_accessor.HasCodeItem());
1545 ScopedIndentation indent1(vios);
1546 MethodInfo method_info = oat_method.GetOatQuickMethodHeader()->GetOptimizedMethodInfo();
1547 DumpCodeInfo(vios, code_info, oat_method, code_item_accessor, method_info);
1548 }
1549 } else if (IsMethodGeneratedByDexToDexCompiler(oat_method, code_item_accessor)) {
1550 // We don't encode the size in the table, so just emit that we have quickened
1551 // information.
1552 ScopedIndentation indent(vios);
1553 vios->Stream() << "quickened data\n";
1554 } else {
1555 // Otherwise, there is nothing to display.
1556 }
1557 }
1558
1559 // Display a CodeInfo object emitted by the optimizing compiler.
DumpCodeInfo(VariableIndentationOutputStream * vios,const CodeInfo & code_info,const OatFile::OatMethod & oat_method,const CodeItemDataAccessor & code_item_accessor,const MethodInfo & method_info)1560 void DumpCodeInfo(VariableIndentationOutputStream* vios,
1561 const CodeInfo& code_info,
1562 const OatFile::OatMethod& oat_method,
1563 const CodeItemDataAccessor& code_item_accessor,
1564 const MethodInfo& method_info) {
1565 code_info.Dump(vios,
1566 oat_method.GetCodeOffset(),
1567 code_item_accessor.RegistersSize(),
1568 options_.dump_code_info_stack_maps_,
1569 instruction_set_,
1570 method_info);
1571 }
1572
GetOutVROffset(uint16_t out_num,InstructionSet isa)1573 static int GetOutVROffset(uint16_t out_num, InstructionSet isa) {
1574 // According to stack model, the first out is above the Method referernce.
1575 return static_cast<size_t>(InstructionSetPointerSize(isa)) + out_num * sizeof(uint32_t);
1576 }
1577
GetVRegOffsetFromQuickCode(const CodeItemDataAccessor & code_item_accessor,uint32_t core_spills,uint32_t fp_spills,size_t frame_size,int reg,InstructionSet isa)1578 static uint32_t GetVRegOffsetFromQuickCode(const CodeItemDataAccessor& code_item_accessor,
1579 uint32_t core_spills,
1580 uint32_t fp_spills,
1581 size_t frame_size,
1582 int reg,
1583 InstructionSet isa) {
1584 PointerSize pointer_size = InstructionSetPointerSize(isa);
1585 if (kIsDebugBuild) {
1586 auto* runtime = Runtime::Current();
1587 if (runtime != nullptr) {
1588 CHECK_EQ(runtime->GetClassLinker()->GetImagePointerSize(), pointer_size);
1589 }
1590 }
1591 DCHECK_ALIGNED(frame_size, kStackAlignment);
1592 DCHECK_NE(reg, -1);
1593 int spill_size = POPCOUNT(core_spills) * GetBytesPerGprSpillLocation(isa)
1594 + POPCOUNT(fp_spills) * GetBytesPerFprSpillLocation(isa)
1595 + sizeof(uint32_t); // Filler.
1596 int num_regs = code_item_accessor.RegistersSize() - code_item_accessor.InsSize();
1597 int temp_threshold = code_item_accessor.RegistersSize();
1598 const int max_num_special_temps = 1;
1599 if (reg == temp_threshold) {
1600 // The current method pointer corresponds to special location on stack.
1601 return 0;
1602 } else if (reg >= temp_threshold + max_num_special_temps) {
1603 /*
1604 * Special temporaries may have custom locations and the logic above deals with that.
1605 * However, non-special temporaries are placed relative to the outs.
1606 */
1607 int temps_start = code_item_accessor.OutsSize() * sizeof(uint32_t)
1608 + static_cast<size_t>(pointer_size) /* art method */;
1609 int relative_offset = (reg - (temp_threshold + max_num_special_temps)) * sizeof(uint32_t);
1610 return temps_start + relative_offset;
1611 } else if (reg < num_regs) {
1612 int locals_start = frame_size - spill_size - num_regs * sizeof(uint32_t);
1613 return locals_start + (reg * sizeof(uint32_t));
1614 } else {
1615 // Handle ins.
1616 return frame_size + ((reg - num_regs) * sizeof(uint32_t))
1617 + static_cast<size_t>(pointer_size) /* art method */;
1618 }
1619 }
1620
DumpVregLocations(std::ostream & os,const OatFile::OatMethod & oat_method,const CodeItemDataAccessor & code_item_accessor)1621 void DumpVregLocations(std::ostream& os, const OatFile::OatMethod& oat_method,
1622 const CodeItemDataAccessor& code_item_accessor) {
1623 if (code_item_accessor.HasCodeItem()) {
1624 size_t num_locals_ins = code_item_accessor.RegistersSize();
1625 size_t num_ins = code_item_accessor.InsSize();
1626 size_t num_locals = num_locals_ins - num_ins;
1627 size_t num_outs = code_item_accessor.OutsSize();
1628
1629 os << "vr_stack_locations:";
1630 for (size_t reg = 0; reg <= num_locals_ins; reg++) {
1631 // For readability, delimit the different kinds of VRs.
1632 if (reg == num_locals_ins) {
1633 os << "\n\tmethod*:";
1634 } else if (reg == num_locals && num_ins > 0) {
1635 os << "\n\tins:";
1636 } else if (reg == 0 && num_locals > 0) {
1637 os << "\n\tlocals:";
1638 }
1639
1640 uint32_t offset = GetVRegOffsetFromQuickCode(code_item_accessor,
1641 oat_method.GetCoreSpillMask(),
1642 oat_method.GetFpSpillMask(),
1643 oat_method.GetFrameSizeInBytes(),
1644 reg,
1645 GetInstructionSet());
1646 os << " v" << reg << "[sp + #" << offset << "]";
1647 }
1648
1649 for (size_t out_reg = 0; out_reg < num_outs; out_reg++) {
1650 if (out_reg == 0) {
1651 os << "\n\touts:";
1652 }
1653
1654 uint32_t offset = GetOutVROffset(out_reg, GetInstructionSet());
1655 os << " v" << out_reg << "[sp + #" << offset << "]";
1656 }
1657
1658 os << "\n";
1659 }
1660 }
1661
1662 // Has `oat_method` -- corresponding to the Dex `code_item` -- been compiled by
1663 // the optimizing compiler?
IsMethodGeneratedByOptimizingCompiler(const OatFile::OatMethod & oat_method,const CodeItemDataAccessor & code_item_accessor)1664 static bool IsMethodGeneratedByOptimizingCompiler(
1665 const OatFile::OatMethod& oat_method,
1666 const CodeItemDataAccessor& code_item_accessor) {
1667 // If the native GC map is null and the Dex `code_item` is not
1668 // null, then this method has been compiled with the optimizing
1669 // compiler.
1670 return oat_method.GetQuickCode() != nullptr &&
1671 oat_method.GetVmapTable() != nullptr &&
1672 code_item_accessor.HasCodeItem();
1673 }
1674
1675 // Has `oat_method` -- corresponding to the Dex `code_item` -- been compiled by
1676 // the dextodex compiler?
IsMethodGeneratedByDexToDexCompiler(const OatFile::OatMethod & oat_method,const CodeItemDataAccessor & code_item_accessor)1677 static bool IsMethodGeneratedByDexToDexCompiler(
1678 const OatFile::OatMethod& oat_method,
1679 const CodeItemDataAccessor& code_item_accessor) {
1680 // If the quick code is null, the Dex `code_item` is not
1681 // null, and the vmap table is not null, then this method has been compiled
1682 // with the dextodex compiler.
1683 return oat_method.GetQuickCode() == nullptr &&
1684 oat_method.GetVmapTable() != nullptr &&
1685 code_item_accessor.HasCodeItem();
1686 }
1687
DumpVerifier(VariableIndentationOutputStream * vios,StackHandleScope<1> * hs,uint32_t dex_method_idx,const DexFile * dex_file,const DexFile::ClassDef & class_def,const DexFile::CodeItem * code_item,uint32_t method_access_flags)1688 verifier::MethodVerifier* DumpVerifier(VariableIndentationOutputStream* vios,
1689 StackHandleScope<1>* hs,
1690 uint32_t dex_method_idx,
1691 const DexFile* dex_file,
1692 const DexFile::ClassDef& class_def,
1693 const DexFile::CodeItem* code_item,
1694 uint32_t method_access_flags) {
1695 if ((method_access_flags & kAccNative) == 0) {
1696 ScopedObjectAccess soa(Thread::Current());
1697 Runtime* const runtime = Runtime::Current();
1698 DCHECK(options_.class_loader_ != nullptr);
1699 Handle<mirror::DexCache> dex_cache = hs->NewHandle(
1700 runtime->GetClassLinker()->RegisterDexFile(*dex_file, options_.class_loader_->Get()));
1701 CHECK(dex_cache != nullptr);
1702 ArtMethod* method = runtime->GetClassLinker()->ResolveMethodWithoutInvokeType(
1703 dex_method_idx, dex_cache, *options_.class_loader_);
1704 CHECK(method != nullptr);
1705 return verifier::MethodVerifier::VerifyMethodAndDump(
1706 soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_,
1707 class_def, code_item, method, method_access_flags);
1708 }
1709
1710 return nullptr;
1711 }
1712
1713 // The StackMapsHelper provides the stack maps in the native PC order.
1714 // For identical native PCs, the order from the CodeInfo is preserved.
1715 class StackMapsHelper {
1716 public:
StackMapsHelper(const uint8_t * raw_code_info,InstructionSet instruction_set)1717 explicit StackMapsHelper(const uint8_t* raw_code_info, InstructionSet instruction_set)
1718 : code_info_(raw_code_info),
1719 encoding_(code_info_.ExtractEncoding()),
1720 number_of_stack_maps_(code_info_.GetNumberOfStackMaps(encoding_)),
1721 indexes_(),
1722 offset_(static_cast<uint32_t>(-1)),
1723 stack_map_index_(0u),
1724 instruction_set_(instruction_set) {
1725 if (number_of_stack_maps_ != 0u) {
1726 // Check if native PCs are ordered.
1727 bool ordered = true;
1728 StackMap last = code_info_.GetStackMapAt(0u, encoding_);
1729 for (size_t i = 1; i != number_of_stack_maps_; ++i) {
1730 StackMap current = code_info_.GetStackMapAt(i, encoding_);
1731 if (last.GetNativePcOffset(encoding_.stack_map.encoding, instruction_set) >
1732 current.GetNativePcOffset(encoding_.stack_map.encoding, instruction_set)) {
1733 ordered = false;
1734 break;
1735 }
1736 last = current;
1737 }
1738 if (!ordered) {
1739 // Create indirection indexes for access in native PC order. We do not optimize
1740 // for the fact that there can currently be only two separately ordered ranges,
1741 // namely normal stack maps and catch-point stack maps.
1742 indexes_.resize(number_of_stack_maps_);
1743 std::iota(indexes_.begin(), indexes_.end(), 0u);
1744 std::sort(indexes_.begin(),
1745 indexes_.end(),
1746 [this](size_t lhs, size_t rhs) {
1747 StackMap left = code_info_.GetStackMapAt(lhs, encoding_);
1748 uint32_t left_pc = left.GetNativePcOffset(encoding_.stack_map.encoding,
1749 instruction_set_);
1750 StackMap right = code_info_.GetStackMapAt(rhs, encoding_);
1751 uint32_t right_pc = right.GetNativePcOffset(encoding_.stack_map.encoding,
1752 instruction_set_);
1753 // If the PCs are the same, compare indexes to preserve the original order.
1754 return (left_pc < right_pc) || (left_pc == right_pc && lhs < rhs);
1755 });
1756 }
1757 offset_ = GetStackMapAt(0).GetNativePcOffset(encoding_.stack_map.encoding,
1758 instruction_set_);
1759 }
1760 }
1761
GetCodeInfo() const1762 const CodeInfo& GetCodeInfo() const {
1763 return code_info_;
1764 }
1765
GetEncoding() const1766 const CodeInfoEncoding& GetEncoding() const {
1767 return encoding_;
1768 }
1769
GetOffset() const1770 uint32_t GetOffset() const {
1771 return offset_;
1772 }
1773
GetStackMap() const1774 StackMap GetStackMap() const {
1775 return GetStackMapAt(stack_map_index_);
1776 }
1777
Next()1778 void Next() {
1779 ++stack_map_index_;
1780 offset_ = (stack_map_index_ == number_of_stack_maps_)
1781 ? static_cast<uint32_t>(-1)
1782 : GetStackMapAt(stack_map_index_).GetNativePcOffset(encoding_.stack_map.encoding,
1783 instruction_set_);
1784 }
1785
1786 private:
GetStackMapAt(size_t i) const1787 StackMap GetStackMapAt(size_t i) const {
1788 if (!indexes_.empty()) {
1789 i = indexes_[i];
1790 }
1791 DCHECK_LT(i, number_of_stack_maps_);
1792 return code_info_.GetStackMapAt(i, encoding_);
1793 }
1794
1795 const CodeInfo code_info_;
1796 const CodeInfoEncoding encoding_;
1797 const size_t number_of_stack_maps_;
1798 dchecked_vector<size_t> indexes_; // Used if stack map native PCs are not ordered.
1799 uint32_t offset_;
1800 size_t stack_map_index_;
1801 const InstructionSet instruction_set_;
1802 };
1803
DumpCode(VariableIndentationOutputStream * vios,const OatFile::OatMethod & oat_method,const CodeItemDataAccessor & code_item_accessor,bool bad_input,size_t code_size)1804 void DumpCode(VariableIndentationOutputStream* vios,
1805 const OatFile::OatMethod& oat_method,
1806 const CodeItemDataAccessor& code_item_accessor,
1807 bool bad_input, size_t code_size) {
1808 const void* quick_code = oat_method.GetQuickCode();
1809
1810 if (code_size == 0) {
1811 code_size = oat_method.GetQuickCodeSize();
1812 }
1813 if (code_size == 0 || quick_code == nullptr) {
1814 vios->Stream() << "NO CODE!\n";
1815 return;
1816 } else if (!bad_input && IsMethodGeneratedByOptimizingCompiler(oat_method,
1817 code_item_accessor)) {
1818 // The optimizing compiler outputs its CodeInfo data in the vmap table.
1819 StackMapsHelper helper(oat_method.GetVmapTable(), instruction_set_);
1820 MethodInfo method_info(oat_method.GetOatQuickMethodHeader()->GetOptimizedMethodInfo());
1821 {
1822 CodeInfoEncoding encoding(helper.GetEncoding());
1823 StackMapEncoding stack_map_encoding(encoding.stack_map.encoding);
1824 const size_t num_stack_maps = encoding.stack_map.num_entries;
1825 if (stats_.AddBitsIfUnique(Stats::kByteKindCodeInfoEncoding,
1826 encoding.HeaderSize() * kBitsPerByte,
1827 oat_method.GetVmapTable())) {
1828 // Stack maps
1829 stats_.AddBits(
1830 Stats::kByteKindStackMapNativePc,
1831 stack_map_encoding.GetNativePcEncoding().BitSize() * num_stack_maps);
1832 stats_.AddBits(
1833 Stats::kByteKindStackMapDexPc,
1834 stack_map_encoding.GetDexPcEncoding().BitSize() * num_stack_maps);
1835 stats_.AddBits(
1836 Stats::kByteKindStackMapDexRegisterMap,
1837 stack_map_encoding.GetDexRegisterMapEncoding().BitSize() * num_stack_maps);
1838 stats_.AddBits(
1839 Stats::kByteKindStackMapInlineInfoIndex,
1840 stack_map_encoding.GetInlineInfoEncoding().BitSize() * num_stack_maps);
1841 stats_.AddBits(
1842 Stats::kByteKindStackMapRegisterMaskIndex,
1843 stack_map_encoding.GetRegisterMaskIndexEncoding().BitSize() * num_stack_maps);
1844 stats_.AddBits(
1845 Stats::kByteKindStackMapStackMaskIndex,
1846 stack_map_encoding.GetStackMaskIndexEncoding().BitSize() * num_stack_maps);
1847
1848 // Stack masks
1849 stats_.AddBits(
1850 Stats::kByteKindCodeInfoStackMasks,
1851 encoding.stack_mask.encoding.BitSize() * encoding.stack_mask.num_entries);
1852
1853 // Register masks
1854 stats_.AddBits(
1855 Stats::kByteKindCodeInfoRegisterMasks,
1856 encoding.register_mask.encoding.BitSize() * encoding.register_mask.num_entries);
1857
1858 // Invoke infos
1859 if (encoding.invoke_info.num_entries > 0u) {
1860 stats_.AddBits(
1861 Stats::kByteKindCodeInfoInvokeInfo,
1862 encoding.invoke_info.encoding.BitSize() * encoding.invoke_info.num_entries);
1863 }
1864
1865 // Location catalog
1866 const size_t location_catalog_bytes =
1867 helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(encoding);
1868 stats_.AddBits(Stats::kByteKindCodeInfoLocationCatalog,
1869 kBitsPerByte * location_catalog_bytes);
1870 // Dex register bytes.
1871 const size_t dex_register_bytes =
1872 helper.GetCodeInfo().GetDexRegisterMapsSize(encoding,
1873 code_item_accessor.RegistersSize());
1874 stats_.AddBits(
1875 Stats::kByteKindCodeInfoDexRegisterMap,
1876 kBitsPerByte * dex_register_bytes);
1877
1878 // Inline infos.
1879 const size_t num_inline_infos = encoding.inline_info.num_entries;
1880 if (num_inline_infos > 0u) {
1881 stats_.AddBits(
1882 Stats::kByteKindInlineInfoMethodIndexIdx,
1883 encoding.inline_info.encoding.GetMethodIndexIdxEncoding().BitSize() *
1884 num_inline_infos);
1885 stats_.AddBits(
1886 Stats::kByteKindInlineInfoDexPc,
1887 encoding.inline_info.encoding.GetDexPcEncoding().BitSize() * num_inline_infos);
1888 stats_.AddBits(
1889 Stats::kByteKindInlineInfoExtraData,
1890 encoding.inline_info.encoding.GetExtraDataEncoding().BitSize() * num_inline_infos);
1891 stats_.AddBits(
1892 Stats::kByteKindInlineInfoDexRegisterMap,
1893 encoding.inline_info.encoding.GetDexRegisterMapEncoding().BitSize() *
1894 num_inline_infos);
1895 stats_.AddBits(Stats::kByteKindInlineInfoIsLast, num_inline_infos);
1896 }
1897 }
1898 }
1899 const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code);
1900 size_t offset = 0;
1901 while (offset < code_size) {
1902 offset += disassembler_->Dump(vios->Stream(), quick_native_pc + offset);
1903 if (offset == helper.GetOffset()) {
1904 ScopedIndentation indent1(vios);
1905 StackMap stack_map = helper.GetStackMap();
1906 DCHECK(stack_map.IsValid());
1907 stack_map.Dump(vios,
1908 helper.GetCodeInfo(),
1909 helper.GetEncoding(),
1910 method_info,
1911 oat_method.GetCodeOffset(),
1912 code_item_accessor.RegistersSize(),
1913 instruction_set_);
1914 do {
1915 helper.Next();
1916 // There may be multiple stack maps at a given PC. We display only the first one.
1917 } while (offset == helper.GetOffset());
1918 }
1919 DCHECK_LT(offset, helper.GetOffset());
1920 }
1921 } else {
1922 const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code);
1923 size_t offset = 0;
1924 while (offset < code_size) {
1925 offset += disassembler_->Dump(vios->Stream(), quick_native_pc + offset);
1926 }
1927 }
1928 }
1929
1930 template <typename NameGetter>
DumpBssEntries(std::ostream & os,const char * slot_type,const IndexBssMapping * mapping,uint32_t number_of_indexes,size_t slot_size,NameGetter name)1931 void DumpBssEntries(std::ostream& os,
1932 const char* slot_type,
1933 const IndexBssMapping* mapping,
1934 uint32_t number_of_indexes,
1935 size_t slot_size,
1936 NameGetter name) {
1937 os << ".bss mapping for " << slot_type << ": ";
1938 if (mapping == nullptr) {
1939 os << "empty.\n";
1940 return;
1941 }
1942 size_t index_bits = IndexBssMappingEntry::IndexBits(number_of_indexes);
1943 size_t num_valid_indexes = 0u;
1944 for (const IndexBssMappingEntry& entry : *mapping) {
1945 num_valid_indexes += 1u + POPCOUNT(entry.GetMask(index_bits));
1946 }
1947 os << mapping->size() << " entries for " << num_valid_indexes << " valid indexes.\n";
1948 os << std::hex;
1949 for (const IndexBssMappingEntry& entry : *mapping) {
1950 uint32_t index = entry.GetIndex(index_bits);
1951 uint32_t mask = entry.GetMask(index_bits);
1952 size_t bss_offset = entry.bss_offset - POPCOUNT(mask) * slot_size;
1953 for (uint32_t n : LowToHighBits(mask)) {
1954 size_t current_index = index - (32u - index_bits) + n;
1955 os << " 0x" << bss_offset << ": " << slot_type << ": " << name(current_index) << "\n";
1956 bss_offset += slot_size;
1957 }
1958 DCHECK_EQ(bss_offset, entry.bss_offset);
1959 os << " 0x" << bss_offset << ": " << slot_type << ": " << name(index) << "\n";
1960 }
1961 os << std::dec;
1962 }
1963
1964 const OatFile& oat_file_;
1965 const std::vector<const OatFile::OatDexFile*> oat_dex_files_;
1966 const OatDumperOptions& options_;
1967 uint32_t resolved_addr2instr_;
1968 const InstructionSet instruction_set_;
1969 std::set<uintptr_t> offsets_;
1970 Disassembler* disassembler_;
1971 Stats stats_;
1972 };
1973
1974 class ImageDumper {
1975 public:
ImageDumper(std::ostream * os,gc::space::ImageSpace & image_space,const ImageHeader & image_header,OatDumperOptions * oat_dumper_options)1976 ImageDumper(std::ostream* os,
1977 gc::space::ImageSpace& image_space,
1978 const ImageHeader& image_header,
1979 OatDumperOptions* oat_dumper_options)
1980 : os_(os),
1981 vios_(os),
1982 indent1_(&vios_),
1983 image_space_(image_space),
1984 image_header_(image_header),
1985 oat_dumper_options_(oat_dumper_options) {}
1986
Dump()1987 bool Dump() REQUIRES_SHARED(Locks::mutator_lock_) {
1988 std::ostream& os = *os_;
1989 std::ostream& indent_os = vios_.Stream();
1990
1991 os << "MAGIC: " << image_header_.GetMagic() << "\n\n";
1992
1993 os << "IMAGE LOCATION: " << image_space_.GetImageLocation() << "\n\n";
1994
1995 os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n";
1996
1997 os << "IMAGE SIZE: " << image_header_.GetImageSize() << "\n\n";
1998
1999 for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
2000 auto section = static_cast<ImageHeader::ImageSections>(i);
2001 os << "IMAGE SECTION " << section << ": " << image_header_.GetImageSection(section) << "\n\n";
2002 }
2003
2004 os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum());
2005
2006 os << "OAT FILE BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatFileBegin()) << "\n\n";
2007
2008 os << "OAT DATA BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatDataBegin()) << "\n\n";
2009
2010 os << "OAT DATA END:" << reinterpret_cast<void*>(image_header_.GetOatDataEnd()) << "\n\n";
2011
2012 os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n";
2013
2014 os << "PATCH DELTA:" << image_header_.GetPatchDelta() << "\n\n";
2015
2016 os << "COMPILE PIC: " << (image_header_.CompilePic() ? "yes" : "no") << "\n\n";
2017
2018 {
2019 os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
2020 static_assert(arraysize(image_roots_descriptions_) ==
2021 static_cast<size_t>(ImageHeader::kImageRootsMax), "sizes must match");
2022 DCHECK_LE(image_header_.GetImageRoots()->GetLength(), ImageHeader::kImageRootsMax);
2023 for (int32_t i = 0, size = image_header_.GetImageRoots()->GetLength(); i != size; ++i) {
2024 ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
2025 const char* image_root_description = image_roots_descriptions_[i];
2026 mirror::Object* image_root_object = image_header_.GetImageRoot(image_root);
2027 indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object);
2028 if (image_root_object != nullptr && image_root_object->IsObjectArray()) {
2029 mirror::ObjectArray<mirror::Object>* image_root_object_array
2030 = image_root_object->AsObjectArray<mirror::Object>();
2031 ScopedIndentation indent2(&vios_);
2032 for (int j = 0; j < image_root_object_array->GetLength(); j++) {
2033 mirror::Object* value = image_root_object_array->Get(j);
2034 size_t run = 0;
2035 for (int32_t k = j + 1; k < image_root_object_array->GetLength(); k++) {
2036 if (value == image_root_object_array->Get(k)) {
2037 run++;
2038 } else {
2039 break;
2040 }
2041 }
2042 if (run == 0) {
2043 indent_os << StringPrintf("%d: ", j);
2044 } else {
2045 indent_os << StringPrintf("%d to %zd: ", j, j + run);
2046 j = j + run;
2047 }
2048 if (value != nullptr) {
2049 PrettyObjectValue(indent_os, value->GetClass(), value);
2050 } else {
2051 indent_os << j << ": null\n";
2052 }
2053 }
2054 }
2055 }
2056 }
2057
2058 {
2059 os << "METHOD ROOTS\n";
2060 static_assert(arraysize(image_methods_descriptions_) ==
2061 static_cast<size_t>(ImageHeader::kImageMethodsCount), "sizes must match");
2062 for (int i = 0; i < ImageHeader::kImageMethodsCount; i++) {
2063 auto image_root = static_cast<ImageHeader::ImageMethod>(i);
2064 const char* description = image_methods_descriptions_[i];
2065 auto* image_method = image_header_.GetImageMethod(image_root);
2066 indent_os << StringPrintf("%s: %p\n", description, image_method);
2067 }
2068 }
2069 os << "\n";
2070
2071 Runtime* const runtime = Runtime::Current();
2072 ClassLinker* class_linker = runtime->GetClassLinker();
2073 std::string image_filename = image_space_.GetImageFilename();
2074 std::string oat_location = ImageHeader::GetOatLocationFromImageLocation(image_filename);
2075 os << "OAT LOCATION: " << oat_location;
2076 os << "\n";
2077 std::string error_msg;
2078 const OatFile* oat_file = image_space_.GetOatFile();
2079 if (oat_file == nullptr) {
2080 oat_file = runtime->GetOatFileManager().FindOpenedOatFileFromOatLocation(oat_location);
2081 }
2082 if (oat_file == nullptr) {
2083 oat_file = OatFile::Open(/* zip_fd */ -1,
2084 oat_location,
2085 oat_location,
2086 nullptr,
2087 nullptr,
2088 false,
2089 /*low_4gb*/false,
2090 nullptr,
2091 &error_msg);
2092 }
2093 if (oat_file == nullptr) {
2094 os << "OAT FILE NOT FOUND: " << error_msg << "\n";
2095 return EXIT_FAILURE;
2096 }
2097 os << "\n";
2098
2099 stats_.oat_file_bytes = oat_file->Size();
2100
2101 oat_dumper_.reset(new OatDumper(*oat_file, *oat_dumper_options_));
2102
2103 for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
2104 CHECK(oat_dex_file != nullptr);
2105 stats_.oat_dex_file_sizes.push_back(std::make_pair(oat_dex_file->GetDexFileLocation(),
2106 oat_dex_file->FileSize()));
2107 }
2108
2109 os << "OBJECTS:\n" << std::flush;
2110
2111 // Loop through the image space and dump its objects.
2112 gc::Heap* heap = runtime->GetHeap();
2113 Thread* self = Thread::Current();
2114 {
2115 {
2116 WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
2117 heap->FlushAllocStack();
2118 }
2119 // Since FlushAllocStack() above resets the (active) allocation
2120 // stack. Need to revoke the thread-local allocation stacks that
2121 // point into it.
2122 ScopedThreadSuspension sts(self, kNative);
2123 ScopedSuspendAll ssa(__FUNCTION__);
2124 heap->RevokeAllThreadLocalAllocationStacks(self);
2125 }
2126 {
2127 // Mark dex caches.
2128 dex_caches_.clear();
2129 {
2130 ReaderMutexLock mu(self, *Locks::dex_lock_);
2131 for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
2132 ObjPtr<mirror::DexCache> dex_cache =
2133 ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
2134 if (dex_cache != nullptr) {
2135 dex_caches_.insert(dex_cache.Ptr());
2136 }
2137 }
2138 }
2139 auto dump_visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
2140 DumpObject(obj);
2141 };
2142 ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
2143 // Dump the normal objects before ArtMethods.
2144 image_space_.GetLiveBitmap()->Walk(dump_visitor);
2145 indent_os << "\n";
2146 // TODO: Dump fields.
2147 // Dump methods after.
2148 DumpArtMethodVisitor visitor(this);
2149 image_header_.VisitPackedArtMethods(&visitor,
2150 image_space_.Begin(),
2151 image_header_.GetPointerSize());
2152 // Dump the large objects separately.
2153 heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(dump_visitor);
2154 indent_os << "\n";
2155 }
2156 os << "STATS:\n" << std::flush;
2157 std::unique_ptr<File> file(OS::OpenFileForReading(image_filename.c_str()));
2158 size_t data_size = image_header_.GetDataSize(); // stored size in file.
2159 if (file == nullptr) {
2160 LOG(WARNING) << "Failed to find image in " << image_filename;
2161 } else {
2162 stats_.file_bytes = file->GetLength();
2163 // If the image is compressed, adjust to decompressed size.
2164 size_t uncompressed_size = image_header_.GetImageSize() - sizeof(ImageHeader);
2165 if (image_header_.GetStorageMode() == ImageHeader::kStorageModeUncompressed) {
2166 DCHECK_EQ(uncompressed_size, data_size) << "Sizes should match for uncompressed image";
2167 }
2168 stats_.file_bytes += uncompressed_size - data_size;
2169 }
2170 size_t header_bytes = sizeof(ImageHeader);
2171 const auto& object_section = image_header_.GetObjectsSection();
2172 const auto& field_section = image_header_.GetFieldsSection();
2173 const auto& method_section = image_header_.GetMethodsSection();
2174 const auto& dex_cache_arrays_section = image_header_.GetDexCacheArraysSection();
2175 const auto& intern_section = image_header_.GetInternedStringsSection();
2176 const auto& class_table_section = image_header_.GetClassTableSection();
2177 const auto& bitmap_section = image_header_.GetImageBitmapSection();
2178
2179 stats_.header_bytes = header_bytes;
2180
2181 // Objects are kObjectAlignment-aligned.
2182 // CHECK_EQ(RoundUp(header_bytes, kObjectAlignment), object_section.Offset());
2183 if (object_section.Offset() > header_bytes) {
2184 stats_.alignment_bytes += object_section.Offset() - header_bytes;
2185 }
2186
2187 // Field section is 4-byte aligned.
2188 constexpr size_t kFieldSectionAlignment = 4U;
2189 uint32_t end_objects = object_section.Offset() + object_section.Size();
2190 CHECK_EQ(RoundUp(end_objects, kFieldSectionAlignment), field_section.Offset());
2191 stats_.alignment_bytes += field_section.Offset() - end_objects;
2192
2193 // Method section is 4/8 byte aligned depending on target. Just check for 4-byte alignment.
2194 uint32_t end_fields = field_section.Offset() + field_section.Size();
2195 CHECK_ALIGNED(method_section.Offset(), 4);
2196 stats_.alignment_bytes += method_section.Offset() - end_fields;
2197
2198 // Dex cache arrays section is aligned depending on the target. Just check for 4-byte alignment.
2199 uint32_t end_methods = method_section.Offset() + method_section.Size();
2200 CHECK_ALIGNED(dex_cache_arrays_section.Offset(), 4);
2201 stats_.alignment_bytes += dex_cache_arrays_section.Offset() - end_methods;
2202
2203 // Intern table is 8-byte aligned.
2204 uint32_t end_caches = dex_cache_arrays_section.Offset() + dex_cache_arrays_section.Size();
2205 CHECK_ALIGNED(intern_section.Offset(), sizeof(uint64_t));
2206 stats_.alignment_bytes += intern_section.Offset() - end_caches;
2207
2208 // Add space between intern table and class table.
2209 uint32_t end_intern = intern_section.Offset() + intern_section.Size();
2210 stats_.alignment_bytes += class_table_section.Offset() - end_intern;
2211
2212 // Add space between end of image data and bitmap. Expect the bitmap to be page-aligned.
2213 const size_t bitmap_offset = sizeof(ImageHeader) + data_size;
2214 CHECK_ALIGNED(bitmap_section.Offset(), kPageSize);
2215 stats_.alignment_bytes += RoundUp(bitmap_offset, kPageSize) - bitmap_offset;
2216
2217 stats_.bitmap_bytes += bitmap_section.Size();
2218 stats_.art_field_bytes += field_section.Size();
2219 stats_.art_method_bytes += method_section.Size();
2220 stats_.dex_cache_arrays_bytes += dex_cache_arrays_section.Size();
2221 stats_.interned_strings_bytes += intern_section.Size();
2222 stats_.class_table_bytes += class_table_section.Size();
2223 stats_.Dump(os, indent_os);
2224 os << "\n";
2225
2226 os << std::flush;
2227
2228 return oat_dumper_->Dump(os);
2229 }
2230
2231 private:
2232 class DumpArtMethodVisitor : public ArtMethodVisitor {
2233 public:
DumpArtMethodVisitor(ImageDumper * image_dumper)2234 explicit DumpArtMethodVisitor(ImageDumper* image_dumper) : image_dumper_(image_dumper) {}
2235
Visit(ArtMethod * method)2236 virtual void Visit(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
2237 std::ostream& indent_os = image_dumper_->vios_.Stream();
2238 indent_os << method << " " << " ArtMethod: " << ArtMethod::PrettyMethod(method) << "\n";
2239 image_dumper_->DumpMethod(method, indent_os);
2240 indent_os << "\n";
2241 }
2242
2243 private:
2244 ImageDumper* const image_dumper_;
2245 };
2246
PrettyObjectValue(std::ostream & os,ObjPtr<mirror::Class> type,ObjPtr<mirror::Object> value)2247 static void PrettyObjectValue(std::ostream& os,
2248 ObjPtr<mirror::Class> type,
2249 ObjPtr<mirror::Object> value)
2250 REQUIRES_SHARED(Locks::mutator_lock_) {
2251 CHECK(type != nullptr);
2252 if (value == nullptr) {
2253 os << StringPrintf("null %s\n", type->PrettyDescriptor().c_str());
2254 } else if (type->IsStringClass()) {
2255 mirror::String* string = value->AsString();
2256 os << StringPrintf("%p String: %s\n", string,
2257 PrintableString(string->ToModifiedUtf8().c_str()).c_str());
2258 } else if (type->IsClassClass()) {
2259 mirror::Class* klass = value->AsClass();
2260 os << StringPrintf("%p Class: %s\n", klass, mirror::Class::PrettyDescriptor(klass).c_str());
2261 } else {
2262 os << StringPrintf("%p %s\n", value.Ptr(), type->PrettyDescriptor().c_str());
2263 }
2264 }
2265
PrintField(std::ostream & os,ArtField * field,ObjPtr<mirror::Object> obj)2266 static void PrintField(std::ostream& os, ArtField* field, ObjPtr<mirror::Object> obj)
2267 REQUIRES_SHARED(Locks::mutator_lock_) {
2268 os << StringPrintf("%s: ", field->GetName());
2269 switch (field->GetTypeAsPrimitiveType()) {
2270 case Primitive::kPrimLong:
2271 os << StringPrintf("%" PRId64 " (0x%" PRIx64 ")\n", field->Get64(obj), field->Get64(obj));
2272 break;
2273 case Primitive::kPrimDouble:
2274 os << StringPrintf("%f (%a)\n", field->GetDouble(obj), field->GetDouble(obj));
2275 break;
2276 case Primitive::kPrimFloat:
2277 os << StringPrintf("%f (%a)\n", field->GetFloat(obj), field->GetFloat(obj));
2278 break;
2279 case Primitive::kPrimInt:
2280 os << StringPrintf("%d (0x%x)\n", field->Get32(obj), field->Get32(obj));
2281 break;
2282 case Primitive::kPrimChar:
2283 os << StringPrintf("%u (0x%x)\n", field->GetChar(obj), field->GetChar(obj));
2284 break;
2285 case Primitive::kPrimShort:
2286 os << StringPrintf("%d (0x%x)\n", field->GetShort(obj), field->GetShort(obj));
2287 break;
2288 case Primitive::kPrimBoolean:
2289 os << StringPrintf("%s (0x%x)\n", field->GetBoolean(obj) ? "true" : "false",
2290 field->GetBoolean(obj));
2291 break;
2292 case Primitive::kPrimByte:
2293 os << StringPrintf("%d (0x%x)\n", field->GetByte(obj), field->GetByte(obj));
2294 break;
2295 case Primitive::kPrimNot: {
2296 // Get the value, don't compute the type unless it is non-null as we don't want
2297 // to cause class loading.
2298 ObjPtr<mirror::Object> value = field->GetObj(obj);
2299 if (value == nullptr) {
2300 os << StringPrintf("null %s\n", PrettyDescriptor(field->GetTypeDescriptor()).c_str());
2301 } else {
2302 // Grab the field type without causing resolution.
2303 ObjPtr<mirror::Class> field_type = field->LookupResolvedType();
2304 if (field_type != nullptr) {
2305 PrettyObjectValue(os, field_type, value);
2306 } else {
2307 os << StringPrintf("%p %s\n",
2308 value.Ptr(),
2309 PrettyDescriptor(field->GetTypeDescriptor()).c_str());
2310 }
2311 }
2312 break;
2313 }
2314 default:
2315 os << "unexpected field type: " << field->GetTypeDescriptor() << "\n";
2316 break;
2317 }
2318 }
2319
DumpFields(std::ostream & os,mirror::Object * obj,mirror::Class * klass)2320 static void DumpFields(std::ostream& os, mirror::Object* obj, mirror::Class* klass)
2321 REQUIRES_SHARED(Locks::mutator_lock_) {
2322 mirror::Class* super = klass->GetSuperClass();
2323 if (super != nullptr) {
2324 DumpFields(os, obj, super);
2325 }
2326 for (ArtField& field : klass->GetIFields()) {
2327 PrintField(os, &field, obj);
2328 }
2329 }
2330
InDumpSpace(const mirror::Object * object)2331 bool InDumpSpace(const mirror::Object* object) {
2332 return image_space_.Contains(object);
2333 }
2334
GetQuickOatCodeBegin(ArtMethod * m)2335 const void* GetQuickOatCodeBegin(ArtMethod* m) REQUIRES_SHARED(Locks::mutator_lock_) {
2336 const void* quick_code = m->GetEntryPointFromQuickCompiledCodePtrSize(
2337 image_header_.GetPointerSize());
2338 if (Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(quick_code)) {
2339 quick_code = oat_dumper_->GetQuickOatCode(m);
2340 }
2341 if (oat_dumper_->GetInstructionSet() == InstructionSet::kThumb2) {
2342 quick_code = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(quick_code) & ~0x1);
2343 }
2344 return quick_code;
2345 }
2346
GetQuickOatCodeSize(ArtMethod * m)2347 uint32_t GetQuickOatCodeSize(ArtMethod* m)
2348 REQUIRES_SHARED(Locks::mutator_lock_) {
2349 const uint32_t* oat_code_begin = reinterpret_cast<const uint32_t*>(GetQuickOatCodeBegin(m));
2350 if (oat_code_begin == nullptr) {
2351 return 0;
2352 }
2353 return oat_code_begin[-1];
2354 }
2355
GetQuickOatCodeEnd(ArtMethod * m)2356 const void* GetQuickOatCodeEnd(ArtMethod* m)
2357 REQUIRES_SHARED(Locks::mutator_lock_) {
2358 const uint8_t* oat_code_begin = reinterpret_cast<const uint8_t*>(GetQuickOatCodeBegin(m));
2359 if (oat_code_begin == nullptr) {
2360 return nullptr;
2361 }
2362 return oat_code_begin + GetQuickOatCodeSize(m);
2363 }
2364
DumpObject(mirror::Object * obj)2365 void DumpObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
2366 DCHECK(obj != nullptr);
2367 if (!InDumpSpace(obj)) {
2368 return;
2369 }
2370
2371 size_t object_bytes = obj->SizeOf();
2372 size_t alignment_bytes = RoundUp(object_bytes, kObjectAlignment) - object_bytes;
2373 stats_.object_bytes += object_bytes;
2374 stats_.alignment_bytes += alignment_bytes;
2375
2376 std::ostream& os = vios_.Stream();
2377
2378 mirror::Class* obj_class = obj->GetClass();
2379 if (obj_class->IsArrayClass()) {
2380 os << StringPrintf("%p: %s length:%d\n", obj, obj_class->PrettyDescriptor().c_str(),
2381 obj->AsArray()->GetLength());
2382 } else if (obj->IsClass()) {
2383 mirror::Class* klass = obj->AsClass();
2384 os << StringPrintf("%p: java.lang.Class \"%s\" (", obj,
2385 mirror::Class::PrettyDescriptor(klass).c_str())
2386 << klass->GetStatus() << ")\n";
2387 } else if (obj_class->IsStringClass()) {
2388 os << StringPrintf("%p: java.lang.String %s\n", obj,
2389 PrintableString(obj->AsString()->ToModifiedUtf8().c_str()).c_str());
2390 } else {
2391 os << StringPrintf("%p: %s\n", obj, obj_class->PrettyDescriptor().c_str());
2392 }
2393 ScopedIndentation indent1(&vios_);
2394 DumpFields(os, obj, obj_class);
2395 const PointerSize image_pointer_size = image_header_.GetPointerSize();
2396 if (obj->IsObjectArray()) {
2397 auto* obj_array = obj->AsObjectArray<mirror::Object>();
2398 for (int32_t i = 0, length = obj_array->GetLength(); i < length; i++) {
2399 mirror::Object* value = obj_array->Get(i);
2400 size_t run = 0;
2401 for (int32_t j = i + 1; j < length; j++) {
2402 if (value == obj_array->Get(j)) {
2403 run++;
2404 } else {
2405 break;
2406 }
2407 }
2408 if (run == 0) {
2409 os << StringPrintf("%d: ", i);
2410 } else {
2411 os << StringPrintf("%d to %zd: ", i, i + run);
2412 i = i + run;
2413 }
2414 mirror::Class* value_class =
2415 (value == nullptr) ? obj_class->GetComponentType() : value->GetClass();
2416 PrettyObjectValue(os, value_class, value);
2417 }
2418 } else if (obj->IsClass()) {
2419 ObjPtr<mirror::Class> klass = obj->AsClass();
2420
2421 if (kBitstringSubtypeCheckEnabled) {
2422 os << "SUBTYPE_CHECK_BITS: ";
2423 SubtypeCheck<ObjPtr<mirror::Class>>::Dump(klass, os);
2424 os << "\n";
2425 }
2426
2427 if (klass->NumStaticFields() != 0) {
2428 os << "STATICS:\n";
2429 ScopedIndentation indent2(&vios_);
2430 for (ArtField& field : klass->GetSFields()) {
2431 PrintField(os, &field, field.GetDeclaringClass());
2432 }
2433 }
2434 } else {
2435 auto it = dex_caches_.find(obj);
2436 if (it != dex_caches_.end()) {
2437 auto* dex_cache = down_cast<mirror::DexCache*>(obj);
2438 const auto& field_section = image_header_.GetFieldsSection();
2439 const auto& method_section = image_header_.GetMethodsSection();
2440 size_t num_methods = dex_cache->NumResolvedMethods();
2441 if (num_methods != 0u) {
2442 os << "Methods (size=" << num_methods << "):\n";
2443 ScopedIndentation indent2(&vios_);
2444 mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods();
2445 for (size_t i = 0, length = dex_cache->NumResolvedMethods(); i < length; ++i) {
2446 ArtMethod* elem = mirror::DexCache::GetNativePairPtrSize(
2447 resolved_methods, i, image_pointer_size).object;
2448 size_t run = 0;
2449 for (size_t j = i + 1;
2450 j != length &&
2451 elem == mirror::DexCache::GetNativePairPtrSize(
2452 resolved_methods, j, image_pointer_size).object;
2453 ++j) {
2454 ++run;
2455 }
2456 if (run == 0) {
2457 os << StringPrintf("%zd: ", i);
2458 } else {
2459 os << StringPrintf("%zd to %zd: ", i, i + run);
2460 i = i + run;
2461 }
2462 std::string msg;
2463 if (elem == nullptr) {
2464 msg = "null";
2465 } else if (method_section.Contains(
2466 reinterpret_cast<uint8_t*>(elem) - image_space_.Begin())) {
2467 msg = reinterpret_cast<ArtMethod*>(elem)->PrettyMethod();
2468 } else {
2469 msg = "<not in method section>";
2470 }
2471 os << StringPrintf("%p %s\n", elem, msg.c_str());
2472 }
2473 }
2474 size_t num_fields = dex_cache->NumResolvedFields();
2475 if (num_fields != 0u) {
2476 os << "Fields (size=" << num_fields << "):\n";
2477 ScopedIndentation indent2(&vios_);
2478 auto* resolved_fields = dex_cache->GetResolvedFields();
2479 for (size_t i = 0, length = dex_cache->NumResolvedFields(); i < length; ++i) {
2480 ArtField* elem = mirror::DexCache::GetNativePairPtrSize(
2481 resolved_fields, i, image_pointer_size).object;
2482 size_t run = 0;
2483 for (size_t j = i + 1;
2484 j != length &&
2485 elem == mirror::DexCache::GetNativePairPtrSize(
2486 resolved_fields, j, image_pointer_size).object;
2487 ++j) {
2488 ++run;
2489 }
2490 if (run == 0) {
2491 os << StringPrintf("%zd: ", i);
2492 } else {
2493 os << StringPrintf("%zd to %zd: ", i, i + run);
2494 i = i + run;
2495 }
2496 std::string msg;
2497 if (elem == nullptr) {
2498 msg = "null";
2499 } else if (field_section.Contains(
2500 reinterpret_cast<uint8_t*>(elem) - image_space_.Begin())) {
2501 msg = reinterpret_cast<ArtField*>(elem)->PrettyField();
2502 } else {
2503 msg = "<not in field section>";
2504 }
2505 os << StringPrintf("%p %s\n", elem, msg.c_str());
2506 }
2507 }
2508 size_t num_types = dex_cache->NumResolvedTypes();
2509 if (num_types != 0u) {
2510 os << "Types (size=" << num_types << "):\n";
2511 ScopedIndentation indent2(&vios_);
2512 auto* resolved_types = dex_cache->GetResolvedTypes();
2513 for (size_t i = 0; i < num_types; ++i) {
2514 auto pair = resolved_types[i].load(std::memory_order_relaxed);
2515 size_t run = 0;
2516 for (size_t j = i + 1; j != num_types; ++j) {
2517 auto other_pair = resolved_types[j].load(std::memory_order_relaxed);
2518 if (pair.index != other_pair.index ||
2519 pair.object.Read() != other_pair.object.Read()) {
2520 break;
2521 }
2522 ++run;
2523 }
2524 if (run == 0) {
2525 os << StringPrintf("%zd: ", i);
2526 } else {
2527 os << StringPrintf("%zd to %zd: ", i, i + run);
2528 i = i + run;
2529 }
2530 std::string msg;
2531 auto* elem = pair.object.Read();
2532 if (elem == nullptr) {
2533 msg = "null";
2534 } else {
2535 msg = elem->PrettyClass();
2536 }
2537 os << StringPrintf("%p %u %s\n", elem, pair.index, msg.c_str());
2538 }
2539 }
2540 }
2541 }
2542 std::string temp;
2543 stats_.Update(obj_class->GetDescriptor(&temp), object_bytes);
2544 }
2545
DumpMethod(ArtMethod * method,std::ostream & indent_os)2546 void DumpMethod(ArtMethod* method, std::ostream& indent_os)
2547 REQUIRES_SHARED(Locks::mutator_lock_) {
2548 DCHECK(method != nullptr);
2549 const void* quick_oat_code_begin = GetQuickOatCodeBegin(method);
2550 const void* quick_oat_code_end = GetQuickOatCodeEnd(method);
2551 const PointerSize pointer_size = image_header_.GetPointerSize();
2552 OatQuickMethodHeader* method_header = reinterpret_cast<OatQuickMethodHeader*>(
2553 reinterpret_cast<uintptr_t>(quick_oat_code_begin) - sizeof(OatQuickMethodHeader));
2554 if (method->IsNative()) {
2555 bool first_occurrence;
2556 uint32_t quick_oat_code_size = GetQuickOatCodeSize(method);
2557 ComputeOatSize(quick_oat_code_begin, &first_occurrence);
2558 if (first_occurrence) {
2559 stats_.native_to_managed_code_bytes += quick_oat_code_size;
2560 }
2561 if (quick_oat_code_begin != method->GetEntryPointFromQuickCompiledCodePtrSize(
2562 image_header_.GetPointerSize())) {
2563 indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code_begin);
2564 }
2565 } else if (method->IsAbstract() || method->IsClassInitializer()) {
2566 // Don't print information for these.
2567 } else if (method->IsRuntimeMethod()) {
2568 ImtConflictTable* table = method->GetImtConflictTable(image_header_.GetPointerSize());
2569 if (table != nullptr) {
2570 indent_os << "IMT conflict table " << table << " method: ";
2571 for (size_t i = 0, count = table->NumEntries(pointer_size); i < count; ++i) {
2572 indent_os << ArtMethod::PrettyMethod(table->GetImplementationMethod(i, pointer_size))
2573 << " ";
2574 }
2575 }
2576 } else {
2577 CodeItemDataAccessor code_item_accessor(method->DexInstructionData());
2578 size_t dex_instruction_bytes = code_item_accessor.InsnsSizeInCodeUnits() * 2;
2579 stats_.dex_instruction_bytes += dex_instruction_bytes;
2580
2581 bool first_occurrence;
2582 size_t vmap_table_bytes = 0u;
2583 if (!method_header->IsOptimized()) {
2584 // Method compiled with the optimizing compiler have no vmap table.
2585 vmap_table_bytes = ComputeOatSize(method_header->GetVmapTable(), &first_occurrence);
2586 if (first_occurrence) {
2587 stats_.vmap_table_bytes += vmap_table_bytes;
2588 }
2589 }
2590
2591 uint32_t quick_oat_code_size = GetQuickOatCodeSize(method);
2592 ComputeOatSize(quick_oat_code_begin, &first_occurrence);
2593 if (first_occurrence) {
2594 stats_.managed_code_bytes += quick_oat_code_size;
2595 if (method->IsConstructor()) {
2596 if (method->IsStatic()) {
2597 stats_.class_initializer_code_bytes += quick_oat_code_size;
2598 } else if (dex_instruction_bytes > kLargeConstructorDexBytes) {
2599 stats_.large_initializer_code_bytes += quick_oat_code_size;
2600 }
2601 } else if (dex_instruction_bytes > kLargeMethodDexBytes) {
2602 stats_.large_method_code_bytes += quick_oat_code_size;
2603 }
2604 }
2605 stats_.managed_code_bytes_ignoring_deduplication += quick_oat_code_size;
2606
2607 uint32_t method_access_flags = method->GetAccessFlags();
2608
2609 indent_os << StringPrintf("OAT CODE: %p-%p\n", quick_oat_code_begin, quick_oat_code_end);
2610 indent_os << StringPrintf("SIZE: Dex Instructions=%zd StackMaps=%zd AccessFlags=0x%x\n",
2611 dex_instruction_bytes,
2612 vmap_table_bytes,
2613 method_access_flags);
2614
2615 size_t total_size = dex_instruction_bytes +
2616 vmap_table_bytes + quick_oat_code_size + ArtMethod::Size(image_header_.GetPointerSize());
2617
2618 double expansion =
2619 static_cast<double>(quick_oat_code_size) / static_cast<double>(dex_instruction_bytes);
2620 stats_.ComputeOutliers(total_size, expansion, method);
2621 }
2622 }
2623
2624 std::set<const void*> already_seen_;
2625 // Compute the size of the given data within the oat file and whether this is the first time
2626 // this data has been requested
ComputeOatSize(const void * oat_data,bool * first_occurrence)2627 size_t ComputeOatSize(const void* oat_data, bool* first_occurrence) {
2628 if (already_seen_.count(oat_data) == 0) {
2629 *first_occurrence = true;
2630 already_seen_.insert(oat_data);
2631 } else {
2632 *first_occurrence = false;
2633 }
2634 return oat_dumper_->ComputeSize(oat_data);
2635 }
2636
2637 public:
2638 struct Stats {
2639 size_t oat_file_bytes;
2640 size_t file_bytes;
2641
2642 size_t header_bytes;
2643 size_t object_bytes;
2644 size_t art_field_bytes;
2645 size_t art_method_bytes;
2646 size_t dex_cache_arrays_bytes;
2647 size_t interned_strings_bytes;
2648 size_t class_table_bytes;
2649 size_t bitmap_bytes;
2650 size_t alignment_bytes;
2651
2652 size_t managed_code_bytes;
2653 size_t managed_code_bytes_ignoring_deduplication;
2654 size_t native_to_managed_code_bytes;
2655 size_t class_initializer_code_bytes;
2656 size_t large_initializer_code_bytes;
2657 size_t large_method_code_bytes;
2658
2659 size_t vmap_table_bytes;
2660
2661 size_t dex_instruction_bytes;
2662
2663 std::vector<ArtMethod*> method_outlier;
2664 std::vector<size_t> method_outlier_size;
2665 std::vector<double> method_outlier_expansion;
2666 std::vector<std::pair<std::string, size_t>> oat_dex_file_sizes;
2667
Statsart::ImageDumper::Stats2668 Stats()
2669 : oat_file_bytes(0),
2670 file_bytes(0),
2671 header_bytes(0),
2672 object_bytes(0),
2673 art_field_bytes(0),
2674 art_method_bytes(0),
2675 dex_cache_arrays_bytes(0),
2676 interned_strings_bytes(0),
2677 class_table_bytes(0),
2678 bitmap_bytes(0),
2679 alignment_bytes(0),
2680 managed_code_bytes(0),
2681 managed_code_bytes_ignoring_deduplication(0),
2682 native_to_managed_code_bytes(0),
2683 class_initializer_code_bytes(0),
2684 large_initializer_code_bytes(0),
2685 large_method_code_bytes(0),
2686 vmap_table_bytes(0),
2687 dex_instruction_bytes(0) {}
2688
2689 struct SizeAndCount {
SizeAndCountart::ImageDumper::Stats::SizeAndCount2690 SizeAndCount(size_t bytes_in, size_t count_in) : bytes(bytes_in), count(count_in) {}
2691 size_t bytes;
2692 size_t count;
2693 };
2694 typedef SafeMap<std::string, SizeAndCount> SizeAndCountTable;
2695 SizeAndCountTable sizes_and_counts;
2696
Updateart::ImageDumper::Stats2697 void Update(const char* descriptor, size_t object_bytes_in) {
2698 SizeAndCountTable::iterator it = sizes_and_counts.find(descriptor);
2699 if (it != sizes_and_counts.end()) {
2700 it->second.bytes += object_bytes_in;
2701 it->second.count += 1;
2702 } else {
2703 sizes_and_counts.Put(descriptor, SizeAndCount(object_bytes_in, 1));
2704 }
2705 }
2706
PercentOfOatBytesart::ImageDumper::Stats2707 double PercentOfOatBytes(size_t size) {
2708 return (static_cast<double>(size) / static_cast<double>(oat_file_bytes)) * 100;
2709 }
2710
PercentOfFileBytesart::ImageDumper::Stats2711 double PercentOfFileBytes(size_t size) {
2712 return (static_cast<double>(size) / static_cast<double>(file_bytes)) * 100;
2713 }
2714
PercentOfObjectBytesart::ImageDumper::Stats2715 double PercentOfObjectBytes(size_t size) {
2716 return (static_cast<double>(size) / static_cast<double>(object_bytes)) * 100;
2717 }
2718
ComputeOutliersart::ImageDumper::Stats2719 void ComputeOutliers(size_t total_size, double expansion, ArtMethod* method) {
2720 method_outlier_size.push_back(total_size);
2721 method_outlier_expansion.push_back(expansion);
2722 method_outlier.push_back(method);
2723 }
2724
DumpOutliersart::ImageDumper::Stats2725 void DumpOutliers(std::ostream& os)
2726 REQUIRES_SHARED(Locks::mutator_lock_) {
2727 size_t sum_of_sizes = 0;
2728 size_t sum_of_sizes_squared = 0;
2729 size_t sum_of_expansion = 0;
2730 size_t sum_of_expansion_squared = 0;
2731 size_t n = method_outlier_size.size();
2732 if (n <= 1) {
2733 return;
2734 }
2735 for (size_t i = 0; i < n; i++) {
2736 size_t cur_size = method_outlier_size[i];
2737 sum_of_sizes += cur_size;
2738 sum_of_sizes_squared += cur_size * cur_size;
2739 double cur_expansion = method_outlier_expansion[i];
2740 sum_of_expansion += cur_expansion;
2741 sum_of_expansion_squared += cur_expansion * cur_expansion;
2742 }
2743 size_t size_mean = sum_of_sizes / n;
2744 size_t size_variance = (sum_of_sizes_squared - sum_of_sizes * size_mean) / (n - 1);
2745 double expansion_mean = sum_of_expansion / n;
2746 double expansion_variance =
2747 (sum_of_expansion_squared - sum_of_expansion * expansion_mean) / (n - 1);
2748
2749 // Dump methods whose size is a certain number of standard deviations from the mean
2750 size_t dumped_values = 0;
2751 size_t skipped_values = 0;
2752 for (size_t i = 100; i > 0; i--) { // i is the current number of standard deviations
2753 size_t cur_size_variance = i * i * size_variance;
2754 bool first = true;
2755 for (size_t j = 0; j < n; j++) {
2756 size_t cur_size = method_outlier_size[j];
2757 if (cur_size > size_mean) {
2758 size_t cur_var = cur_size - size_mean;
2759 cur_var = cur_var * cur_var;
2760 if (cur_var > cur_size_variance) {
2761 if (dumped_values > 20) {
2762 if (i == 1) {
2763 skipped_values++;
2764 } else {
2765 i = 2; // jump to counting for 1 standard deviation
2766 break;
2767 }
2768 } else {
2769 if (first) {
2770 os << "\nBig methods (size > " << i << " standard deviations the norm):\n";
2771 first = false;
2772 }
2773 os << ArtMethod::PrettyMethod(method_outlier[j]) << " requires storage of "
2774 << PrettySize(cur_size) << "\n";
2775 method_outlier_size[j] = 0; // don't consider this method again
2776 dumped_values++;
2777 }
2778 }
2779 }
2780 }
2781 }
2782 if (skipped_values > 0) {
2783 os << "... skipped " << skipped_values
2784 << " methods with size > 1 standard deviation from the norm\n";
2785 }
2786 os << std::flush;
2787
2788 // Dump methods whose expansion is a certain number of standard deviations from the mean
2789 dumped_values = 0;
2790 skipped_values = 0;
2791 for (size_t i = 10; i > 0; i--) { // i is the current number of standard deviations
2792 double cur_expansion_variance = i * i * expansion_variance;
2793 bool first = true;
2794 for (size_t j = 0; j < n; j++) {
2795 double cur_expansion = method_outlier_expansion[j];
2796 if (cur_expansion > expansion_mean) {
2797 size_t cur_var = cur_expansion - expansion_mean;
2798 cur_var = cur_var * cur_var;
2799 if (cur_var > cur_expansion_variance) {
2800 if (dumped_values > 20) {
2801 if (i == 1) {
2802 skipped_values++;
2803 } else {
2804 i = 2; // jump to counting for 1 standard deviation
2805 break;
2806 }
2807 } else {
2808 if (first) {
2809 os << "\nLarge expansion methods (size > " << i
2810 << " standard deviations the norm):\n";
2811 first = false;
2812 }
2813 os << ArtMethod::PrettyMethod(method_outlier[j]) << " expanded code by "
2814 << cur_expansion << "\n";
2815 method_outlier_expansion[j] = 0.0; // don't consider this method again
2816 dumped_values++;
2817 }
2818 }
2819 }
2820 }
2821 }
2822 if (skipped_values > 0) {
2823 os << "... skipped " << skipped_values
2824 << " methods with expansion > 1 standard deviation from the norm\n";
2825 }
2826 os << "\n" << std::flush;
2827 }
2828
Dumpart::ImageDumper::Stats2829 void Dump(std::ostream& os, std::ostream& indent_os)
2830 REQUIRES_SHARED(Locks::mutator_lock_) {
2831 {
2832 os << "art_file_bytes = " << PrettySize(file_bytes) << "\n\n"
2833 << "art_file_bytes = header_bytes + object_bytes + alignment_bytes\n";
2834 indent_os << StringPrintf("header_bytes = %8zd (%2.0f%% of art file bytes)\n"
2835 "object_bytes = %8zd (%2.0f%% of art file bytes)\n"
2836 "art_field_bytes = %8zd (%2.0f%% of art file bytes)\n"
2837 "art_method_bytes = %8zd (%2.0f%% of art file bytes)\n"
2838 "dex_cache_arrays_bytes = %8zd (%2.0f%% of art file bytes)\n"
2839 "interned_string_bytes = %8zd (%2.0f%% of art file bytes)\n"
2840 "class_table_bytes = %8zd (%2.0f%% of art file bytes)\n"
2841 "bitmap_bytes = %8zd (%2.0f%% of art file bytes)\n"
2842 "alignment_bytes = %8zd (%2.0f%% of art file bytes)\n\n",
2843 header_bytes, PercentOfFileBytes(header_bytes),
2844 object_bytes, PercentOfFileBytes(object_bytes),
2845 art_field_bytes, PercentOfFileBytes(art_field_bytes),
2846 art_method_bytes, PercentOfFileBytes(art_method_bytes),
2847 dex_cache_arrays_bytes,
2848 PercentOfFileBytes(dex_cache_arrays_bytes),
2849 interned_strings_bytes,
2850 PercentOfFileBytes(interned_strings_bytes),
2851 class_table_bytes, PercentOfFileBytes(class_table_bytes),
2852 bitmap_bytes, PercentOfFileBytes(bitmap_bytes),
2853 alignment_bytes, PercentOfFileBytes(alignment_bytes))
2854 << std::flush;
2855 CHECK_EQ(file_bytes,
2856 header_bytes + object_bytes + art_field_bytes + art_method_bytes +
2857 dex_cache_arrays_bytes + interned_strings_bytes + class_table_bytes +
2858 bitmap_bytes + alignment_bytes);
2859 }
2860
2861 os << "object_bytes breakdown:\n";
2862 size_t object_bytes_total = 0;
2863 for (const auto& sizes_and_count : sizes_and_counts) {
2864 const std::string& descriptor(sizes_and_count.first);
2865 double average = static_cast<double>(sizes_and_count.second.bytes) /
2866 static_cast<double>(sizes_and_count.second.count);
2867 double percent = PercentOfObjectBytes(sizes_and_count.second.bytes);
2868 os << StringPrintf("%32s %8zd bytes %6zd instances "
2869 "(%4.0f bytes/instance) %2.0f%% of object_bytes\n",
2870 descriptor.c_str(), sizes_and_count.second.bytes,
2871 sizes_and_count.second.count, average, percent);
2872 object_bytes_total += sizes_and_count.second.bytes;
2873 }
2874 os << "\n" << std::flush;
2875 CHECK_EQ(object_bytes, object_bytes_total);
2876
2877 os << StringPrintf("oat_file_bytes = %8zd\n"
2878 "managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
2879 "native_to_managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n"
2880 "class_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
2881 "large_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
2882 "large_method_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n",
2883 oat_file_bytes,
2884 managed_code_bytes,
2885 PercentOfOatBytes(managed_code_bytes),
2886 native_to_managed_code_bytes,
2887 PercentOfOatBytes(native_to_managed_code_bytes),
2888 class_initializer_code_bytes,
2889 PercentOfOatBytes(class_initializer_code_bytes),
2890 large_initializer_code_bytes,
2891 PercentOfOatBytes(large_initializer_code_bytes),
2892 large_method_code_bytes,
2893 PercentOfOatBytes(large_method_code_bytes))
2894 << "DexFile sizes:\n";
2895 for (const std::pair<std::string, size_t>& oat_dex_file_size : oat_dex_file_sizes) {
2896 os << StringPrintf("%s = %zd (%2.0f%% of oat file bytes)\n",
2897 oat_dex_file_size.first.c_str(), oat_dex_file_size.second,
2898 PercentOfOatBytes(oat_dex_file_size.second));
2899 }
2900
2901 os << "\n" << StringPrintf("vmap_table_bytes = %7zd (%2.0f%% of oat file bytes)\n\n",
2902 vmap_table_bytes, PercentOfOatBytes(vmap_table_bytes))
2903 << std::flush;
2904
2905 os << StringPrintf("dex_instruction_bytes = %zd\n", dex_instruction_bytes)
2906 << StringPrintf("managed_code_bytes expansion = %.2f (ignoring deduplication %.2f)\n\n",
2907 static_cast<double>(managed_code_bytes) /
2908 static_cast<double>(dex_instruction_bytes),
2909 static_cast<double>(managed_code_bytes_ignoring_deduplication) /
2910 static_cast<double>(dex_instruction_bytes))
2911 << std::flush;
2912
2913 DumpOutliers(os);
2914 }
2915 } stats_;
2916
2917 private:
2918 enum {
2919 // Number of bytes for a constructor to be considered large. Based on the 1000 basic block
2920 // threshold, we assume 2 bytes per instruction and 2 instructions per block.
2921 kLargeConstructorDexBytes = 4000,
2922 // Number of bytes for a method to be considered large. Based on the 4000 basic block
2923 // threshold, we assume 2 bytes per instruction and 2 instructions per block.
2924 kLargeMethodDexBytes = 16000
2925 };
2926
2927 // For performance, use the *os_ directly for anything that doesn't need indentation
2928 // and prepare an indentation stream with default indentation 1.
2929 std::ostream* os_;
2930 VariableIndentationOutputStream vios_;
2931 ScopedIndentation indent1_;
2932
2933 gc::space::ImageSpace& image_space_;
2934 const ImageHeader& image_header_;
2935 std::unique_ptr<OatDumper> oat_dumper_;
2936 OatDumperOptions* oat_dumper_options_;
2937 std::set<mirror::Object*> dex_caches_;
2938
2939 DISALLOW_COPY_AND_ASSIGN(ImageDumper);
2940 };
2941
DumpImage(gc::space::ImageSpace * image_space,OatDumperOptions * options,std::ostream * os)2942 static int DumpImage(gc::space::ImageSpace* image_space,
2943 OatDumperOptions* options,
2944 std::ostream* os) REQUIRES_SHARED(Locks::mutator_lock_) {
2945 const ImageHeader& image_header = image_space->GetImageHeader();
2946 if (!image_header.IsValid()) {
2947 LOG(ERROR) << "Invalid image header " << image_space->GetImageLocation();
2948 return EXIT_FAILURE;
2949 }
2950 ImageDumper image_dumper(os, *image_space, image_header, options);
2951 if (!image_dumper.Dump()) {
2952 return EXIT_FAILURE;
2953 }
2954 return EXIT_SUCCESS;
2955 }
2956
DumpImages(Runtime * runtime,OatDumperOptions * options,std::ostream * os)2957 static int DumpImages(Runtime* runtime, OatDumperOptions* options, std::ostream* os) {
2958 // Dumping the image, no explicit class loader.
2959 ScopedNullHandle<mirror::ClassLoader> null_class_loader;
2960 options->class_loader_ = &null_class_loader;
2961
2962 ScopedObjectAccess soa(Thread::Current());
2963 if (options->app_image_ != nullptr) {
2964 if (options->app_oat_ == nullptr) {
2965 LOG(ERROR) << "Can not dump app image without app oat file";
2966 return EXIT_FAILURE;
2967 }
2968 // We can't know if the app image is 32 bits yet, but it contains pointers into the oat file.
2969 // We need to map the oat file in the low 4gb or else the fixup wont be able to fit oat file
2970 // pointers into 32 bit pointer sized ArtMethods.
2971 std::string error_msg;
2972 std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
2973 options->app_oat_,
2974 options->app_oat_,
2975 nullptr,
2976 nullptr,
2977 false,
2978 /*low_4gb*/true,
2979 nullptr,
2980 &error_msg));
2981 if (oat_file == nullptr) {
2982 LOG(ERROR) << "Failed to open oat file " << options->app_oat_ << " with error " << error_msg;
2983 return EXIT_FAILURE;
2984 }
2985 std::unique_ptr<gc::space::ImageSpace> space(
2986 gc::space::ImageSpace::CreateFromAppImage(options->app_image_, oat_file.get(), &error_msg));
2987 if (space == nullptr) {
2988 LOG(ERROR) << "Failed to open app image " << options->app_image_ << " with error "
2989 << error_msg;
2990 }
2991 // Open dex files for the image.
2992 std::vector<std::unique_ptr<const DexFile>> dex_files;
2993 if (!runtime->GetClassLinker()->OpenImageDexFiles(space.get(), &dex_files, &error_msg)) {
2994 LOG(ERROR) << "Failed to open app image dex files " << options->app_image_ << " with error "
2995 << error_msg;
2996 }
2997 // Dump the actual image.
2998 int result = DumpImage(space.get(), options, os);
2999 if (result != EXIT_SUCCESS) {
3000 return result;
3001 }
3002 // Fall through to dump the boot images.
3003 }
3004
3005 gc::Heap* heap = runtime->GetHeap();
3006 CHECK(heap->HasBootImageSpace()) << "No image spaces";
3007 for (gc::space::ImageSpace* image_space : heap->GetBootImageSpaces()) {
3008 int result = DumpImage(image_space, options, os);
3009 if (result != EXIT_SUCCESS) {
3010 return result;
3011 }
3012 }
3013 return EXIT_SUCCESS;
3014 }
3015
InstallOatFile(Runtime * runtime,std::unique_ptr<OatFile> oat_file,std::vector<const DexFile * > * class_path)3016 static jobject InstallOatFile(Runtime* runtime,
3017 std::unique_ptr<OatFile> oat_file,
3018 std::vector<const DexFile*>* class_path)
3019 REQUIRES_SHARED(Locks::mutator_lock_) {
3020 Thread* self = Thread::Current();
3021 CHECK(self != nullptr);
3022 // Need well-known-classes.
3023 WellKnownClasses::Init(self->GetJniEnv());
3024
3025 // Open dex files.
3026 OatFile* oat_file_ptr = oat_file.get();
3027 ClassLinker* class_linker = runtime->GetClassLinker();
3028 runtime->GetOatFileManager().RegisterOatFile(std::move(oat_file));
3029 for (const OatFile::OatDexFile* odf : oat_file_ptr->GetOatDexFiles()) {
3030 std::string error_msg;
3031 const DexFile* const dex_file = OpenDexFile(odf, &error_msg);
3032 CHECK(dex_file != nullptr) << error_msg;
3033 class_path->push_back(dex_file);
3034 }
3035
3036 // Need a class loader. Fake that we're a compiler.
3037 // Note: this will run initializers through the unstarted runtime, so make sure it's
3038 // initialized.
3039 interpreter::UnstartedRuntime::Initialize();
3040
3041 jobject class_loader = class_linker->CreatePathClassLoader(self, *class_path);
3042
3043 // Need to register dex files to get a working dex cache.
3044 for (const DexFile* dex_file : *class_path) {
3045 ObjPtr<mirror::DexCache> dex_cache = class_linker->RegisterDexFile(
3046 *dex_file, self->DecodeJObject(class_loader)->AsClassLoader());
3047 CHECK(dex_cache != nullptr);
3048 }
3049
3050 return class_loader;
3051 }
3052
DumpOatWithRuntime(Runtime * runtime,std::unique_ptr<OatFile> oat_file,OatDumperOptions * options,std::ostream * os)3053 static int DumpOatWithRuntime(Runtime* runtime,
3054 std::unique_ptr<OatFile> oat_file,
3055 OatDumperOptions* options,
3056 std::ostream* os) {
3057 CHECK(runtime != nullptr && oat_file != nullptr && options != nullptr);
3058 ScopedObjectAccess soa(Thread::Current());
3059
3060 OatFile* oat_file_ptr = oat_file.get();
3061 std::vector<const DexFile*> class_path;
3062 jobject class_loader = InstallOatFile(runtime, std::move(oat_file), &class_path);
3063
3064 // Use the class loader while dumping.
3065 StackHandleScope<1> scope(soa.Self());
3066 Handle<mirror::ClassLoader> loader_handle = scope.NewHandle(
3067 soa.Decode<mirror::ClassLoader>(class_loader));
3068 options->class_loader_ = &loader_handle;
3069
3070 OatDumper oat_dumper(*oat_file_ptr, *options);
3071 bool success = oat_dumper.Dump(*os);
3072 return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
3073 }
3074
DumpOatWithoutRuntime(OatFile * oat_file,OatDumperOptions * options,std::ostream * os)3075 static int DumpOatWithoutRuntime(OatFile* oat_file, OatDumperOptions* options, std::ostream* os) {
3076 CHECK(oat_file != nullptr && options != nullptr);
3077 // No image = no class loader.
3078 ScopedNullHandle<mirror::ClassLoader> null_class_loader;
3079 options->class_loader_ = &null_class_loader;
3080
3081 OatDumper oat_dumper(*oat_file, *options);
3082 bool success = oat_dumper.Dump(*os);
3083 return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
3084 }
3085
DumpOat(Runtime * runtime,const char * oat_filename,const char * dex_filename,OatDumperOptions * options,std::ostream * os)3086 static int DumpOat(Runtime* runtime,
3087 const char* oat_filename,
3088 const char* dex_filename,
3089 OatDumperOptions* options,
3090 std::ostream* os) {
3091 if (dex_filename == nullptr) {
3092 LOG(WARNING) << "No dex filename provided, "
3093 << "oatdump might fail if the oat file does not contain the dex code.";
3094 }
3095 std::string error_msg;
3096 std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
3097 oat_filename,
3098 oat_filename,
3099 nullptr,
3100 nullptr,
3101 false,
3102 /*low_4gb*/false,
3103 dex_filename,
3104 &error_msg));
3105 if (oat_file == nullptr) {
3106 LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg;
3107 return EXIT_FAILURE;
3108 }
3109
3110 if (runtime != nullptr) {
3111 return DumpOatWithRuntime(runtime, std::move(oat_file), options, os);
3112 } else {
3113 return DumpOatWithoutRuntime(oat_file.get(), options, os);
3114 }
3115 }
3116
SymbolizeOat(const char * oat_filename,const char * dex_filename,std::string & output_name,bool no_bits)3117 static int SymbolizeOat(const char* oat_filename,
3118 const char* dex_filename,
3119 std::string& output_name,
3120 bool no_bits) {
3121 std::string error_msg;
3122 std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
3123 oat_filename,
3124 oat_filename,
3125 nullptr,
3126 nullptr,
3127 false,
3128 /*low_4gb*/false,
3129 dex_filename,
3130 &error_msg));
3131 if (oat_file == nullptr) {
3132 LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg;
3133 return EXIT_FAILURE;
3134 }
3135
3136 bool result;
3137 // Try to produce an ELF file of the same type. This is finicky, as we have used 32-bit ELF
3138 // files for 64-bit code in the past.
3139 if (Is64BitInstructionSet(oat_file->GetOatHeader().GetInstructionSet())) {
3140 OatSymbolizer<ElfTypes64> oat_symbolizer(oat_file.get(), output_name, no_bits);
3141 result = oat_symbolizer.Symbolize();
3142 } else {
3143 OatSymbolizer<ElfTypes32> oat_symbolizer(oat_file.get(), output_name, no_bits);
3144 result = oat_symbolizer.Symbolize();
3145 }
3146 if (!result) {
3147 LOG(ERROR) << "Failed to symbolize";
3148 return EXIT_FAILURE;
3149 }
3150
3151 return EXIT_SUCCESS;
3152 }
3153
3154 class IMTDumper {
3155 public:
Dump(Runtime * runtime,const std::string & imt_file,bool dump_imt_stats,const char * oat_filename,const char * dex_filename)3156 static bool Dump(Runtime* runtime,
3157 const std::string& imt_file,
3158 bool dump_imt_stats,
3159 const char* oat_filename,
3160 const char* dex_filename) {
3161 Thread* self = Thread::Current();
3162
3163 ScopedObjectAccess soa(self);
3164 StackHandleScope<1> scope(self);
3165 MutableHandle<mirror::ClassLoader> class_loader = scope.NewHandle<mirror::ClassLoader>(nullptr);
3166 std::vector<const DexFile*> class_path;
3167
3168 if (oat_filename != nullptr) {
3169 std::string error_msg;
3170 std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
3171 oat_filename,
3172 oat_filename,
3173 nullptr,
3174 nullptr,
3175 false,
3176 /*low_4gb*/false,
3177 dex_filename,
3178 &error_msg));
3179 if (oat_file == nullptr) {
3180 LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg;
3181 return false;
3182 }
3183
3184 class_loader.Assign(soa.Decode<mirror::ClassLoader>(
3185 InstallOatFile(runtime, std::move(oat_file), &class_path)));
3186 } else {
3187 class_loader.Assign(nullptr); // Boot classloader. Just here for explicit documentation.
3188 class_path = runtime->GetClassLinker()->GetBootClassPath();
3189 }
3190
3191 if (!imt_file.empty()) {
3192 return DumpImt(runtime, imt_file, class_loader);
3193 }
3194
3195 if (dump_imt_stats) {
3196 return DumpImtStats(runtime, class_path, class_loader);
3197 }
3198
3199 LOG(FATAL) << "Should not reach here";
3200 UNREACHABLE();
3201 }
3202
3203 private:
DumpImt(Runtime * runtime,const std::string & imt_file,Handle<mirror::ClassLoader> h_class_loader)3204 static bool DumpImt(Runtime* runtime,
3205 const std::string& imt_file,
3206 Handle<mirror::ClassLoader> h_class_loader)
3207 REQUIRES_SHARED(Locks::mutator_lock_) {
3208 std::vector<std::string> lines = ReadCommentedInputFromFile(imt_file);
3209 std::unordered_set<std::string> prepared;
3210
3211 for (const std::string& line : lines) {
3212 // A line should be either a class descriptor, in which case we will dump the complete IMT,
3213 // or a class descriptor and an interface method, in which case we will lookup the method,
3214 // determine its IMT slot, and check the class' IMT.
3215 size_t first_space = line.find(' ');
3216 if (first_space == std::string::npos) {
3217 DumpIMTForClass(runtime, line, h_class_loader, &prepared);
3218 } else {
3219 DumpIMTForMethod(runtime,
3220 line.substr(0, first_space),
3221 line.substr(first_space + 1, std::string::npos),
3222 h_class_loader,
3223 &prepared);
3224 }
3225 std::cerr << std::endl;
3226 }
3227
3228 return true;
3229 }
3230
DumpImtStats(Runtime * runtime,const std::vector<const DexFile * > & dex_files,Handle<mirror::ClassLoader> h_class_loader)3231 static bool DumpImtStats(Runtime* runtime,
3232 const std::vector<const DexFile*>& dex_files,
3233 Handle<mirror::ClassLoader> h_class_loader)
3234 REQUIRES_SHARED(Locks::mutator_lock_) {
3235 size_t without_imt = 0;
3236 size_t with_imt = 0;
3237 std::map<size_t, size_t> histogram;
3238
3239 ClassLinker* class_linker = runtime->GetClassLinker();
3240 const PointerSize pointer_size = class_linker->GetImagePointerSize();
3241 std::unordered_set<std::string> prepared;
3242
3243 Thread* self = Thread::Current();
3244 StackHandleScope<1> scope(self);
3245 MutableHandle<mirror::Class> h_klass(scope.NewHandle<mirror::Class>(nullptr));
3246
3247 for (const DexFile* dex_file : dex_files) {
3248 for (uint32_t class_def_index = 0;
3249 class_def_index != dex_file->NumClassDefs();
3250 ++class_def_index) {
3251 const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
3252 const char* descriptor = dex_file->GetClassDescriptor(class_def);
3253 h_klass.Assign(class_linker->FindClass(self, descriptor, h_class_loader));
3254 if (h_klass == nullptr) {
3255 std::cerr << "Warning: could not load " << descriptor << std::endl;
3256 continue;
3257 }
3258
3259 if (HasNoIMT(runtime, h_klass, pointer_size, &prepared)) {
3260 without_imt++;
3261 continue;
3262 }
3263
3264 ImTable* im_table = PrepareAndGetImTable(runtime, h_klass, pointer_size, &prepared);
3265 if (im_table == nullptr) {
3266 // Should not happen, but accept.
3267 without_imt++;
3268 continue;
3269 }
3270
3271 with_imt++;
3272 for (size_t imt_index = 0; imt_index != ImTable::kSize; ++imt_index) {
3273 ArtMethod* ptr = im_table->Get(imt_index, pointer_size);
3274 if (ptr->IsRuntimeMethod()) {
3275 if (ptr->IsImtUnimplementedMethod()) {
3276 histogram[0]++;
3277 } else {
3278 ImtConflictTable* current_table = ptr->GetImtConflictTable(pointer_size);
3279 histogram[current_table->NumEntries(pointer_size)]++;
3280 }
3281 } else {
3282 histogram[1]++;
3283 }
3284 }
3285 }
3286 }
3287
3288 std::cerr << "IMT stats:"
3289 << std::endl << std::endl;
3290
3291 std::cerr << " " << with_imt << " classes with IMT."
3292 << std::endl << std::endl;
3293 std::cerr << " " << without_imt << " classes without IMT (or copy from Object)."
3294 << std::endl << std::endl;
3295
3296 double sum_one = 0;
3297 size_t count_one = 0;
3298
3299 std::cerr << " " << "IMT histogram" << std::endl;
3300 for (auto& bucket : histogram) {
3301 std::cerr << " " << bucket.first << " " << bucket.second << std::endl;
3302 if (bucket.first > 0) {
3303 sum_one += bucket.second * bucket.first;
3304 count_one += bucket.second;
3305 }
3306 }
3307
3308 double count_zero = count_one + histogram[0];
3309 std::cerr << " Stats:" << std::endl;
3310 std::cerr << " Average depth (including empty): " << (sum_one / count_zero) << std::endl;
3311 std::cerr << " Average depth (excluding empty): " << (sum_one / count_one) << std::endl;
3312
3313 return true;
3314 }
3315
3316 // Return whether the given class has no IMT (or the one shared with java.lang.Object).
HasNoIMT(Runtime * runtime,Handle<mirror::Class> klass,const PointerSize pointer_size,std::unordered_set<std::string> * prepared)3317 static bool HasNoIMT(Runtime* runtime,
3318 Handle<mirror::Class> klass,
3319 const PointerSize pointer_size,
3320 std::unordered_set<std::string>* prepared)
3321 REQUIRES_SHARED(Locks::mutator_lock_) {
3322 if (klass->IsObjectClass() || !klass->ShouldHaveImt()) {
3323 return true;
3324 }
3325
3326 if (klass->GetImt(pointer_size) == nullptr) {
3327 PrepareClass(runtime, klass, prepared);
3328 }
3329
3330 mirror::Class* object_class = mirror::Class::GetJavaLangClass()->GetSuperClass();
3331 DCHECK(object_class->IsObjectClass());
3332
3333 bool result = klass->GetImt(pointer_size) == object_class->GetImt(pointer_size);
3334
3335 if (klass->GetIfTable()->Count() == 0) {
3336 DCHECK(result);
3337 }
3338
3339 return result;
3340 }
3341
PrintTable(ImtConflictTable * table,PointerSize pointer_size)3342 static void PrintTable(ImtConflictTable* table, PointerSize pointer_size)
3343 REQUIRES_SHARED(Locks::mutator_lock_) {
3344 if (table == nullptr) {
3345 std::cerr << " <No IMT?>" << std::endl;
3346 return;
3347 }
3348 size_t table_index = 0;
3349 for (;;) {
3350 ArtMethod* ptr = table->GetInterfaceMethod(table_index, pointer_size);
3351 if (ptr == nullptr) {
3352 return;
3353 }
3354 table_index++;
3355 std::cerr << " " << ptr->PrettyMethod(true) << std::endl;
3356 }
3357 }
3358
PrepareAndGetImTable(Runtime * runtime,Thread * self,Handle<mirror::ClassLoader> h_loader,const std::string & class_name,const PointerSize pointer_size,mirror::Class ** klass_out,std::unordered_set<std::string> * prepared)3359 static ImTable* PrepareAndGetImTable(Runtime* runtime,
3360 Thread* self,
3361 Handle<mirror::ClassLoader> h_loader,
3362 const std::string& class_name,
3363 const PointerSize pointer_size,
3364 mirror::Class** klass_out,
3365 std::unordered_set<std::string>* prepared)
3366 REQUIRES_SHARED(Locks::mutator_lock_) {
3367 if (class_name.empty()) {
3368 return nullptr;
3369 }
3370
3371 std::string descriptor;
3372 if (class_name[0] == 'L') {
3373 descriptor = class_name;
3374 } else {
3375 descriptor = DotToDescriptor(class_name.c_str());
3376 }
3377
3378 mirror::Class* klass = runtime->GetClassLinker()->FindClass(self, descriptor.c_str(), h_loader);
3379
3380 if (klass == nullptr) {
3381 self->ClearException();
3382 std::cerr << "Did not find " << class_name << std::endl;
3383 *klass_out = nullptr;
3384 return nullptr;
3385 }
3386
3387 StackHandleScope<1> scope(Thread::Current());
3388 Handle<mirror::Class> h_klass = scope.NewHandle<mirror::Class>(klass);
3389
3390 ImTable* ret = PrepareAndGetImTable(runtime, h_klass, pointer_size, prepared);
3391 *klass_out = h_klass.Get();
3392 return ret;
3393 }
3394
PrepareAndGetImTable(Runtime * runtime,Handle<mirror::Class> h_klass,const PointerSize pointer_size,std::unordered_set<std::string> * prepared)3395 static ImTable* PrepareAndGetImTable(Runtime* runtime,
3396 Handle<mirror::Class> h_klass,
3397 const PointerSize pointer_size,
3398 std::unordered_set<std::string>* prepared)
3399 REQUIRES_SHARED(Locks::mutator_lock_) {
3400 PrepareClass(runtime, h_klass, prepared);
3401 return h_klass->GetImt(pointer_size);
3402 }
3403
DumpIMTForClass(Runtime * runtime,const std::string & class_name,Handle<mirror::ClassLoader> h_loader,std::unordered_set<std::string> * prepared)3404 static void DumpIMTForClass(Runtime* runtime,
3405 const std::string& class_name,
3406 Handle<mirror::ClassLoader> h_loader,
3407 std::unordered_set<std::string>* prepared)
3408 REQUIRES_SHARED(Locks::mutator_lock_) {
3409 const PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
3410 mirror::Class* klass;
3411 ImTable* imt = PrepareAndGetImTable(runtime,
3412 Thread::Current(),
3413 h_loader,
3414 class_name,
3415 pointer_size,
3416 &klass,
3417 prepared);
3418 if (imt == nullptr) {
3419 return;
3420 }
3421
3422 std::cerr << class_name << std::endl << " IMT:" << std::endl;
3423 for (size_t index = 0; index < ImTable::kSize; ++index) {
3424 std::cerr << " " << index << ":" << std::endl;
3425 ArtMethod* ptr = imt->Get(index, pointer_size);
3426 if (ptr->IsRuntimeMethod()) {
3427 if (ptr->IsImtUnimplementedMethod()) {
3428 std::cerr << " <empty>" << std::endl;
3429 } else {
3430 ImtConflictTable* current_table = ptr->GetImtConflictTable(pointer_size);
3431 PrintTable(current_table, pointer_size);
3432 }
3433 } else {
3434 std::cerr << " " << ptr->PrettyMethod(true) << std::endl;
3435 }
3436 }
3437
3438 std::cerr << " Interfaces:" << std::endl;
3439 // Run through iftable, find methods that slot here, see if they fit.
3440 mirror::IfTable* if_table = klass->GetIfTable();
3441 for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
3442 mirror::Class* iface = if_table->GetInterface(i);
3443 std::string iface_name;
3444 std::cerr << " " << iface->GetDescriptor(&iface_name) << std::endl;
3445
3446 for (ArtMethod& iface_method : iface->GetVirtualMethods(pointer_size)) {
3447 uint32_t class_hash, name_hash, signature_hash;
3448 ImTable::GetImtHashComponents(&iface_method, &class_hash, &name_hash, &signature_hash);
3449 uint32_t imt_slot = ImTable::GetImtIndex(&iface_method);
3450 std::cerr << " " << iface_method.PrettyMethod(true)
3451 << " slot=" << imt_slot
3452 << std::hex
3453 << " class_hash=0x" << class_hash
3454 << " name_hash=0x" << name_hash
3455 << " signature_hash=0x" << signature_hash
3456 << std::dec
3457 << std::endl;
3458 }
3459 }
3460 }
3461
DumpIMTForMethod(Runtime * runtime,const std::string & class_name,const std::string & method,Handle<mirror::ClassLoader> h_loader,std::unordered_set<std::string> * prepared)3462 static void DumpIMTForMethod(Runtime* runtime,
3463 const std::string& class_name,
3464 const std::string& method,
3465 Handle<mirror::ClassLoader> h_loader,
3466 std::unordered_set<std::string>* prepared)
3467 REQUIRES_SHARED(Locks::mutator_lock_) {
3468 const PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
3469 mirror::Class* klass;
3470 ImTable* imt = PrepareAndGetImTable(runtime,
3471 Thread::Current(),
3472 h_loader,
3473 class_name,
3474 pointer_size,
3475 &klass,
3476 prepared);
3477 if (imt == nullptr) {
3478 return;
3479 }
3480
3481 std::cerr << class_name << " <" << method << ">" << std::endl;
3482 for (size_t index = 0; index < ImTable::kSize; ++index) {
3483 ArtMethod* ptr = imt->Get(index, pointer_size);
3484 if (ptr->IsRuntimeMethod()) {
3485 if (ptr->IsImtUnimplementedMethod()) {
3486 continue;
3487 }
3488
3489 ImtConflictTable* current_table = ptr->GetImtConflictTable(pointer_size);
3490 if (current_table == nullptr) {
3491 continue;
3492 }
3493
3494 size_t table_index = 0;
3495 for (;;) {
3496 ArtMethod* ptr2 = current_table->GetInterfaceMethod(table_index, pointer_size);
3497 if (ptr2 == nullptr) {
3498 break;
3499 }
3500 table_index++;
3501
3502 std::string p_name = ptr2->PrettyMethod(true);
3503 if (android::base::StartsWith(p_name, method.c_str())) {
3504 std::cerr << " Slot "
3505 << index
3506 << " ("
3507 << current_table->NumEntries(pointer_size)
3508 << ")"
3509 << std::endl;
3510 PrintTable(current_table, pointer_size);
3511 return;
3512 }
3513 }
3514 } else {
3515 std::string p_name = ptr->PrettyMethod(true);
3516 if (android::base::StartsWith(p_name, method.c_str())) {
3517 std::cerr << " Slot " << index << " (1)" << std::endl;
3518 std::cerr << " " << p_name << std::endl;
3519 } else {
3520 // Run through iftable, find methods that slot here, see if they fit.
3521 mirror::IfTable* if_table = klass->GetIfTable();
3522 for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
3523 mirror::Class* iface = if_table->GetInterface(i);
3524 size_t num_methods = iface->NumDeclaredVirtualMethods();
3525 if (num_methods > 0) {
3526 for (ArtMethod& iface_method : iface->GetMethods(pointer_size)) {
3527 if (ImTable::GetImtIndex(&iface_method) == index) {
3528 std::string i_name = iface_method.PrettyMethod(true);
3529 if (android::base::StartsWith(i_name, method.c_str())) {
3530 std::cerr << " Slot " << index << " (1)" << std::endl;
3531 std::cerr << " " << p_name << " (" << i_name << ")" << std::endl;
3532 }
3533 }
3534 }
3535 }
3536 }
3537 }
3538 }
3539 }
3540 }
3541
3542 // Read lines from the given stream, dropping comments and empty lines
ReadCommentedInputStream(std::istream & in_stream)3543 static std::vector<std::string> ReadCommentedInputStream(std::istream& in_stream) {
3544 std::vector<std::string> output;
3545 while (in_stream.good()) {
3546 std::string dot;
3547 std::getline(in_stream, dot);
3548 if (android::base::StartsWith(dot, "#") || dot.empty()) {
3549 continue;
3550 }
3551 output.push_back(dot);
3552 }
3553 return output;
3554 }
3555
3556 // Read lines from the given file, dropping comments and empty lines.
ReadCommentedInputFromFile(const std::string & input_filename)3557 static std::vector<std::string> ReadCommentedInputFromFile(const std::string& input_filename) {
3558 std::unique_ptr<std::ifstream> input_file(new std::ifstream(input_filename, std::ifstream::in));
3559 if (input_file.get() == nullptr) {
3560 LOG(ERROR) << "Failed to open input file " << input_filename;
3561 return std::vector<std::string>();
3562 }
3563 std::vector<std::string> result = ReadCommentedInputStream(*input_file);
3564 input_file->close();
3565 return result;
3566 }
3567
3568 // Prepare a class, i.e., ensure it has a filled IMT. Will do so recursively for superclasses,
3569 // and note in the given set that the work was done.
PrepareClass(Runtime * runtime,Handle<mirror::Class> h_klass,std::unordered_set<std::string> * done)3570 static void PrepareClass(Runtime* runtime,
3571 Handle<mirror::Class> h_klass,
3572 std::unordered_set<std::string>* done)
3573 REQUIRES_SHARED(Locks::mutator_lock_) {
3574 if (!h_klass->ShouldHaveImt()) {
3575 return;
3576 }
3577
3578 std::string name;
3579 name = h_klass->GetDescriptor(&name);
3580
3581 if (done->find(name) != done->end()) {
3582 return;
3583 }
3584 done->insert(name);
3585
3586 if (h_klass->HasSuperClass()) {
3587 StackHandleScope<1> h(Thread::Current());
3588 PrepareClass(runtime, h.NewHandle<mirror::Class>(h_klass->GetSuperClass()), done);
3589 }
3590
3591 if (!h_klass->IsTemp()) {
3592 runtime->GetClassLinker()->FillIMTAndConflictTables(h_klass.Get());
3593 }
3594 }
3595 };
3596
3597 struct OatdumpArgs : public CmdlineArgs {
3598 protected:
3599 using Base = CmdlineArgs;
3600
ParseCustomart::OatdumpArgs3601 virtual ParseStatus ParseCustom(const StringPiece& option,
3602 std::string* error_msg) OVERRIDE {
3603 {
3604 ParseStatus base_parse = Base::ParseCustom(option, error_msg);
3605 if (base_parse != kParseUnknownArgument) {
3606 return base_parse;
3607 }
3608 }
3609
3610 if (option.starts_with("--oat-file=")) {
3611 oat_filename_ = option.substr(strlen("--oat-file=")).data();
3612 } else if (option.starts_with("--dex-file=")) {
3613 dex_filename_ = option.substr(strlen("--dex-file=")).data();
3614 } else if (option.starts_with("--image=")) {
3615 image_location_ = option.substr(strlen("--image=")).data();
3616 } else if (option == "--no-dump:vmap") {
3617 dump_vmap_ = false;
3618 } else if (option =="--dump:code_info_stack_maps") {
3619 dump_code_info_stack_maps_ = true;
3620 } else if (option == "--no-disassemble") {
3621 disassemble_code_ = false;
3622 } else if (option =="--header-only") {
3623 dump_header_only_ = true;
3624 } else if (option.starts_with("--symbolize=")) {
3625 oat_filename_ = option.substr(strlen("--symbolize=")).data();
3626 symbolize_ = true;
3627 } else if (option.starts_with("--only-keep-debug")) {
3628 only_keep_debug_ = true;
3629 } else if (option.starts_with("--class-filter=")) {
3630 class_filter_ = option.substr(strlen("--class-filter=")).data();
3631 } else if (option.starts_with("--method-filter=")) {
3632 method_filter_ = option.substr(strlen("--method-filter=")).data();
3633 } else if (option.starts_with("--list-classes")) {
3634 list_classes_ = true;
3635 } else if (option.starts_with("--list-methods")) {
3636 list_methods_ = true;
3637 } else if (option.starts_with("--export-dex-to=")) {
3638 export_dex_location_ = option.substr(strlen("--export-dex-to=")).data();
3639 } else if (option.starts_with("--addr2instr=")) {
3640 if (!ParseUint(option.substr(strlen("--addr2instr=")).data(), &addr2instr_)) {
3641 *error_msg = "Address conversion failed";
3642 return kParseError;
3643 }
3644 } else if (option.starts_with("--app-image=")) {
3645 app_image_ = option.substr(strlen("--app-image=")).data();
3646 } else if (option.starts_with("--app-oat=")) {
3647 app_oat_ = option.substr(strlen("--app-oat=")).data();
3648 } else if (option.starts_with("--dump-imt=")) {
3649 imt_dump_ = option.substr(strlen("--dump-imt=")).data();
3650 } else if (option == "--dump-imt-stats") {
3651 imt_stat_dump_ = true;
3652 } else {
3653 return kParseUnknownArgument;
3654 }
3655
3656 return kParseOk;
3657 }
3658
ParseChecksart::OatdumpArgs3659 virtual ParseStatus ParseChecks(std::string* error_msg) OVERRIDE {
3660 // Infer boot image location from the image location if possible.
3661 if (boot_image_location_ == nullptr) {
3662 boot_image_location_ = image_location_;
3663 }
3664
3665 // Perform the parent checks.
3666 ParseStatus parent_checks = Base::ParseChecks(error_msg);
3667 if (parent_checks != kParseOk) {
3668 return parent_checks;
3669 }
3670
3671 // Perform our own checks.
3672 if (image_location_ == nullptr && oat_filename_ == nullptr) {
3673 *error_msg = "Either --image or --oat-file must be specified";
3674 return kParseError;
3675 } else if (image_location_ != nullptr && oat_filename_ != nullptr) {
3676 *error_msg = "Either --image or --oat-file must be specified but not both";
3677 return kParseError;
3678 }
3679
3680 return kParseOk;
3681 }
3682
GetUsageart::OatdumpArgs3683 virtual std::string GetUsage() const {
3684 std::string usage;
3685
3686 usage +=
3687 "Usage: oatdump [options] ...\n"
3688 " Example: oatdump --image=$ANDROID_PRODUCT_OUT/system/framework/boot.art\n"
3689 " Example: adb shell oatdump --image=/system/framework/boot.art\n"
3690 "\n"
3691 // Either oat-file or image is required.
3692 " --oat-file=<file.oat>: specifies an input oat filename.\n"
3693 " Example: --oat-file=/system/framework/boot.oat\n"
3694 "\n"
3695 " --image=<file.art>: specifies an input image location.\n"
3696 " Example: --image=/system/framework/boot.art\n"
3697 "\n"
3698 " --app-image=<file.art>: specifies an input app image. Must also have a specified\n"
3699 " boot image (with --image) and app oat file (with --app-oat).\n"
3700 " Example: --app-image=app.art\n"
3701 "\n"
3702 " --app-oat=<file.odex>: specifies an input app oat.\n"
3703 " Example: --app-oat=app.odex\n"
3704 "\n";
3705
3706 usage += Base::GetUsage();
3707
3708 usage += // Optional.
3709 " --no-dump:vmap may be used to disable vmap dumping.\n"
3710 " Example: --no-dump:vmap\n"
3711 "\n"
3712 " --dump:code_info_stack_maps enables dumping of stack maps in CodeInfo sections.\n"
3713 " Example: --dump:code_info_stack_maps\n"
3714 "\n"
3715 " --no-disassemble may be used to disable disassembly.\n"
3716 " Example: --no-disassemble\n"
3717 "\n"
3718 " --header-only may be used to print only the oat header.\n"
3719 " Example: --header-only\n"
3720 "\n"
3721 " --list-classes may be used to list target file classes (can be used with filters).\n"
3722 " Example: --list-classes\n"
3723 " Example: --list-classes --class-filter=com.example.foo\n"
3724 "\n"
3725 " --list-methods may be used to list target file methods (can be used with filters).\n"
3726 " Example: --list-methods\n"
3727 " Example: --list-methods --class-filter=com.example --method-filter=foo\n"
3728 "\n"
3729 " --symbolize=<file.oat>: output a copy of file.oat with elf symbols included.\n"
3730 " Example: --symbolize=/system/framework/boot.oat\n"
3731 "\n"
3732 " --only-keep-debug<file.oat>: Modifies the behaviour of --symbolize so that\n"
3733 " .rodata and .text sections are omitted in the output file to save space.\n"
3734 " Example: --symbolize=/system/framework/boot.oat --only-keep-debug\n"
3735 "\n"
3736 " --class-filter=<class name>: only dumps classes that contain the filter.\n"
3737 " Example: --class-filter=com.example.foo\n"
3738 "\n"
3739 " --method-filter=<method name>: only dumps methods that contain the filter.\n"
3740 " Example: --method-filter=foo\n"
3741 "\n"
3742 " --export-dex-to=<directory>: may be used to export oat embedded dex files.\n"
3743 " Example: --export-dex-to=/data/local/tmp\n"
3744 "\n"
3745 " --addr2instr=<address>: output matching method disassembled code from relative\n"
3746 " address (e.g. PC from crash dump)\n"
3747 " Example: --addr2instr=0x00001a3b\n"
3748 "\n"
3749 " --dump-imt=<file.txt>: output IMT collisions (if any) for the given receiver\n"
3750 " types and interface methods in the given file. The file\n"
3751 " is read line-wise, where each line should either be a class\n"
3752 " name or descriptor, or a class name/descriptor and a prefix\n"
3753 " of a complete method name (separated by a whitespace).\n"
3754 " Example: --dump-imt=imt.txt\n"
3755 "\n"
3756 " --dump-imt-stats: output IMT statistics for the given boot image\n"
3757 " Example: --dump-imt-stats"
3758 "\n";
3759
3760 return usage;
3761 }
3762
3763 public:
3764 const char* oat_filename_ = nullptr;
3765 const char* dex_filename_ = nullptr;
3766 const char* class_filter_ = "";
3767 const char* method_filter_ = "";
3768 const char* image_location_ = nullptr;
3769 std::string elf_filename_prefix_;
3770 std::string imt_dump_;
3771 bool dump_vmap_ = true;
3772 bool dump_code_info_stack_maps_ = false;
3773 bool disassemble_code_ = true;
3774 bool symbolize_ = false;
3775 bool only_keep_debug_ = false;
3776 bool list_classes_ = false;
3777 bool list_methods_ = false;
3778 bool dump_header_only_ = false;
3779 bool imt_stat_dump_ = false;
3780 uint32_t addr2instr_ = 0;
3781 const char* export_dex_location_ = nullptr;
3782 const char* app_image_ = nullptr;
3783 const char* app_oat_ = nullptr;
3784 };
3785
3786 struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
NeedsRuntimeart::OatdumpMain3787 virtual bool NeedsRuntime() OVERRIDE {
3788 CHECK(args_ != nullptr);
3789
3790 // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
3791 bool absolute_addresses = (args_->oat_filename_ == nullptr);
3792
3793 oat_dumper_options_.reset(new OatDumperOptions(
3794 args_->dump_vmap_,
3795 args_->dump_code_info_stack_maps_,
3796 args_->disassemble_code_,
3797 absolute_addresses,
3798 args_->class_filter_,
3799 args_->method_filter_,
3800 args_->list_classes_,
3801 args_->list_methods_,
3802 args_->dump_header_only_,
3803 args_->export_dex_location_,
3804 args_->app_image_,
3805 args_->app_oat_,
3806 args_->addr2instr_));
3807
3808 return (args_->boot_image_location_ != nullptr ||
3809 args_->image_location_ != nullptr ||
3810 !args_->imt_dump_.empty()) &&
3811 !args_->symbolize_;
3812 }
3813
ExecuteWithoutRuntimeart::OatdumpMain3814 virtual bool ExecuteWithoutRuntime() OVERRIDE {
3815 CHECK(args_ != nullptr);
3816 CHECK(args_->oat_filename_ != nullptr);
3817
3818 MemMap::Init();
3819
3820 if (args_->symbolize_) {
3821 // ELF has special kind of section called SHT_NOBITS which allows us to create
3822 // sections which exist but their data is omitted from the ELF file to save space.
3823 // This is what "strip --only-keep-debug" does when it creates separate ELF file
3824 // with only debug data. We use it in similar way to exclude .rodata and .text.
3825 bool no_bits = args_->only_keep_debug_;
3826 return SymbolizeOat(args_->oat_filename_, args_->dex_filename_, args_->output_name_, no_bits)
3827 == EXIT_SUCCESS;
3828 } else {
3829 return DumpOat(nullptr,
3830 args_->oat_filename_,
3831 args_->dex_filename_,
3832 oat_dumper_options_.get(),
3833 args_->os_) == EXIT_SUCCESS;
3834 }
3835 }
3836
ExecuteWithRuntimeart::OatdumpMain3837 virtual bool ExecuteWithRuntime(Runtime* runtime) {
3838 CHECK(args_ != nullptr);
3839
3840 if (!args_->imt_dump_.empty() || args_->imt_stat_dump_) {
3841 return IMTDumper::Dump(runtime,
3842 args_->imt_dump_,
3843 args_->imt_stat_dump_,
3844 args_->oat_filename_,
3845 args_->dex_filename_);
3846 }
3847
3848 if (args_->oat_filename_ != nullptr) {
3849 return DumpOat(runtime,
3850 args_->oat_filename_,
3851 args_->dex_filename_,
3852 oat_dumper_options_.get(),
3853 args_->os_) == EXIT_SUCCESS;
3854 }
3855
3856 return DumpImages(runtime, oat_dumper_options_.get(), args_->os_) == EXIT_SUCCESS;
3857 }
3858
3859 std::unique_ptr<OatDumperOptions> oat_dumper_options_;
3860 };
3861
3862 } // namespace art
3863
main(int argc,char ** argv)3864 int main(int argc, char** argv) {
3865 // Output all logging to stderr.
3866 android::base::SetLogger(android::base::StderrLogger);
3867
3868 art::OatdumpMain main;
3869 return main.Main(argc, argv);
3870 }
3871