oatdump.cc revision 81a9087f0df0518c39405b7d18ba5858a6d8b77b
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 <vector> 27 28#include "arch/instruction_set_features.h" 29#include "art_field-inl.h" 30#include "art_method-inl.h" 31#include "base/unix_file/fd_file.h" 32#include "class_linker.h" 33#include "class_linker-inl.h" 34#include "dex_file-inl.h" 35#include "dex_instruction.h" 36#include "disassembler.h" 37#include "elf_builder.h" 38#include "gc_map.h" 39#include "gc/space/image_space.h" 40#include "gc/space/large_object_space.h" 41#include "gc/space/space-inl.h" 42#include "image.h" 43#include "indenter.h" 44#include "mapping_table.h" 45#include "mirror/array-inl.h" 46#include "mirror/class-inl.h" 47#include "mirror/object-inl.h" 48#include "mirror/object_array-inl.h" 49#include "oat.h" 50#include "oat_file-inl.h" 51#include "os.h" 52#include "output_stream.h" 53#include "safe_map.h" 54#include "scoped_thread_state_change.h" 55#include "ScopedLocalRef.h" 56#include "thread_list.h" 57#include "verifier/dex_gc_map.h" 58#include "verifier/method_verifier.h" 59#include "vmap_table.h" 60#include "well_known_classes.h" 61 62#include <sys/stat.h> 63#include "cmdline.h" 64 65namespace art { 66 67const char* image_methods_descriptions_[] = { 68 "kResolutionMethod", 69 "kImtConflictMethod", 70 "kImtUnimplementedMethod", 71 "kCalleeSaveMethod", 72 "kRefsOnlySaveMethod", 73 "kRefsAndArgsSaveMethod", 74}; 75 76const char* image_roots_descriptions_[] = { 77 "kDexCaches", 78 "kClassRoots", 79}; 80 81// Map is so that we don't allocate multiple dex files for the same OatDexFile. 82static std::map<const OatFile::OatDexFile*, 83 std::unique_ptr<const DexFile>> opened_dex_files; 84 85const DexFile* OpenDexFile(const OatFile::OatDexFile* oat_dex_file, std::string* error_msg) { 86 DCHECK(oat_dex_file != nullptr); 87 auto it = opened_dex_files.find(oat_dex_file); 88 if (it != opened_dex_files.end()) { 89 return it->second.get(); 90 } 91 const DexFile* ret = oat_dex_file->OpenDexFile(error_msg).release(); 92 opened_dex_files.emplace(oat_dex_file, std::unique_ptr<const DexFile>(ret)); 93 return ret; 94} 95 96class OatSymbolizer FINAL { 97 public: 98 class RodataWriter FINAL : public CodeOutput { 99 public: 100 explicit RodataWriter(const OatFile* oat_file) : oat_file_(oat_file) {} 101 102 bool Write(OutputStream* out) OVERRIDE { 103 const size_t rodata_size = oat_file_->GetOatHeader().GetExecutableOffset(); 104 return out->WriteFully(oat_file_->Begin(), rodata_size); 105 } 106 107 private: 108 const OatFile* oat_file_; 109 }; 110 111 class TextWriter FINAL : public CodeOutput { 112 public: 113 explicit TextWriter(const OatFile* oat_file) : oat_file_(oat_file) {} 114 115 bool Write(OutputStream* out) OVERRIDE { 116 const size_t rodata_size = oat_file_->GetOatHeader().GetExecutableOffset(); 117 const uint8_t* text_begin = oat_file_->Begin() + rodata_size; 118 return out->WriteFully(text_begin, oat_file_->End() - text_begin); 119 } 120 121 private: 122 const OatFile* oat_file_; 123 }; 124 125 OatSymbolizer(const OatFile* oat_file, const std::string& output_name) : 126 oat_file_(oat_file), builder_(nullptr), 127 output_name_(output_name.empty() ? "symbolized.oat" : output_name) { 128 } 129 130 typedef void (OatSymbolizer::*Callback)(const DexFile::ClassDef&, 131 uint32_t, 132 const OatFile::OatMethod&, 133 const DexFile&, 134 uint32_t, 135 const DexFile::CodeItem*, 136 uint32_t); 137 138 bool Symbolize() { 139 Elf32_Word rodata_size = oat_file_->GetOatHeader().GetExecutableOffset(); 140 uint32_t size = static_cast<uint32_t>(oat_file_->End() - oat_file_->Begin()); 141 uint32_t text_size = size - rodata_size; 142 uint32_t bss_size = oat_file_->BssSize(); 143 RodataWriter rodata_writer(oat_file_); 144 TextWriter text_writer(oat_file_); 145 builder_.reset(new ElfBuilder<ElfTypes32>( 146 oat_file_->GetOatHeader().GetInstructionSet(), 147 rodata_size, &rodata_writer, 148 text_size, &text_writer, 149 bss_size)); 150 151 Walk(&art::OatSymbolizer::RegisterForDedup); 152 153 NormalizeState(); 154 155 Walk(&art::OatSymbolizer::AddSymbol); 156 157 File* elf_output = OS::CreateEmptyFile(output_name_.c_str()); 158 bool result = builder_->Write(elf_output); 159 160 // Ignore I/O errors. 161 UNUSED(elf_output->FlushClose()); 162 163 return result; 164 } 165 166 void Walk(Callback callback) { 167 std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles(); 168 for (size_t i = 0; i < oat_dex_files.size(); i++) { 169 const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i]; 170 CHECK(oat_dex_file != nullptr); 171 WalkOatDexFile(oat_dex_file, callback); 172 } 173 } 174 175 void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file, Callback callback) { 176 std::string error_msg; 177 const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg); 178 if (dex_file == nullptr) { 179 return; 180 } 181 for (size_t class_def_index = 0; 182 class_def_index < dex_file->NumClassDefs(); 183 class_def_index++) { 184 const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); 185 const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index); 186 OatClassType type = oat_class.GetType(); 187 switch (type) { 188 case kOatClassAllCompiled: 189 case kOatClassSomeCompiled: 190 WalkOatClass(oat_class, *dex_file, class_def, callback); 191 break; 192 193 case kOatClassNoneCompiled: 194 case kOatClassMax: 195 // Ignore. 196 break; 197 } 198 } 199 } 200 201 void WalkOatClass(const OatFile::OatClass& oat_class, const DexFile& dex_file, 202 const DexFile::ClassDef& class_def, Callback callback) { 203 const uint8_t* class_data = dex_file.GetClassData(class_def); 204 if (class_data == nullptr) { // empty class such as a marker interface? 205 return; 206 } 207 // Note: even if this is an interface or a native class, we still have to walk it, as there 208 // might be a static initializer. 209 ClassDataItemIterator it(dex_file, class_data); 210 SkipAllFields(&it); 211 uint32_t class_method_idx = 0; 212 while (it.HasNextDirectMethod()) { 213 const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx); 214 WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(), 215 it.GetMethodCodeItem(), it.GetMethodAccessFlags(), callback); 216 class_method_idx++; 217 it.Next(); 218 } 219 while (it.HasNextVirtualMethod()) { 220 const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx); 221 WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(), 222 it.GetMethodCodeItem(), it.GetMethodAccessFlags(), callback); 223 class_method_idx++; 224 it.Next(); 225 } 226 DCHECK(!it.HasNext()); 227 } 228 229 void WalkOatMethod(const DexFile::ClassDef& class_def, uint32_t class_method_index, 230 const OatFile::OatMethod& oat_method, const DexFile& dex_file, 231 uint32_t dex_method_idx, const DexFile::CodeItem* code_item, 232 uint32_t method_access_flags, Callback callback) { 233 if ((method_access_flags & kAccAbstract) != 0) { 234 // Abstract method, no code. 235 return; 236 } 237 if (oat_method.GetCodeOffset() == 0) { 238 // No code. 239 return; 240 } 241 242 (this->*callback)(class_def, class_method_index, oat_method, dex_file, dex_method_idx, code_item, 243 method_access_flags); 244 } 245 246 void RegisterForDedup(const DexFile::ClassDef& class_def ATTRIBUTE_UNUSED, 247 uint32_t class_method_index ATTRIBUTE_UNUSED, 248 const OatFile::OatMethod& oat_method, 249 const DexFile& dex_file ATTRIBUTE_UNUSED, 250 uint32_t dex_method_idx ATTRIBUTE_UNUSED, 251 const DexFile::CodeItem* code_item ATTRIBUTE_UNUSED, 252 uint32_t method_access_flags ATTRIBUTE_UNUSED) { 253 state_[oat_method.GetCodeOffset()]++; 254 } 255 256 void NormalizeState() { 257 for (auto& x : state_) { 258 if (x.second == 1) { 259 state_[x.first] = 0; 260 } 261 } 262 } 263 264 enum class DedupState { // private 265 kNotDeduplicated, 266 kDeduplicatedFirst, 267 kDeduplicatedOther 268 }; 269 DedupState IsDuplicated(uint32_t offset) { 270 if (state_[offset] == 0) { 271 return DedupState::kNotDeduplicated; 272 } 273 if (state_[offset] == 1) { 274 return DedupState::kDeduplicatedOther; 275 } 276 state_[offset] = 1; 277 return DedupState::kDeduplicatedFirst; 278 } 279 280 void AddSymbol(const DexFile::ClassDef& class_def ATTRIBUTE_UNUSED, 281 uint32_t class_method_index ATTRIBUTE_UNUSED, 282 const OatFile::OatMethod& oat_method, 283 const DexFile& dex_file, 284 uint32_t dex_method_idx, 285 const DexFile::CodeItem* code_item ATTRIBUTE_UNUSED, 286 uint32_t method_access_flags ATTRIBUTE_UNUSED) { 287 DedupState dedup = IsDuplicated(oat_method.GetCodeOffset()); 288 if (dedup != DedupState::kDeduplicatedOther) { 289 std::string pretty_name = PrettyMethod(dex_method_idx, dex_file, true); 290 291 if (dedup == DedupState::kDeduplicatedFirst) { 292 pretty_name = "[Dedup]" + pretty_name; 293 } 294 295 auto* symtab = builder_->GetSymtab(); 296 297 symtab->AddSymbol(pretty_name, builder_->GetText(), 298 oat_method.GetCodeOffset() - oat_file_->GetOatHeader().GetExecutableOffset(), 299 true, oat_method.GetQuickCodeSize(), STB_GLOBAL, STT_FUNC); 300 } 301 } 302 303 private: 304 static void SkipAllFields(ClassDataItemIterator* it) { 305 while (it->HasNextStaticField()) { 306 it->Next(); 307 } 308 while (it->HasNextInstanceField()) { 309 it->Next(); 310 } 311 } 312 313 const OatFile* oat_file_; 314 std::unique_ptr<ElfBuilder<ElfTypes32> > builder_; 315 std::unordered_map<uint32_t, uint32_t> state_; 316 const std::string output_name_; 317}; 318 319class OatDumperOptions { 320 public: 321 OatDumperOptions(bool dump_raw_mapping_table, 322 bool dump_raw_gc_map, 323 bool dump_vmap, 324 bool dump_code_info_stack_maps, 325 bool disassemble_code, 326 bool absolute_addresses, 327 const char* class_filter, 328 const char* method_filter, 329 bool list_classes, 330 bool list_methods, 331 const char* export_dex_location, 332 uint32_t addr2instr) 333 : dump_raw_mapping_table_(dump_raw_mapping_table), 334 dump_raw_gc_map_(dump_raw_gc_map), 335 dump_vmap_(dump_vmap), 336 dump_code_info_stack_maps_(dump_code_info_stack_maps), 337 disassemble_code_(disassemble_code), 338 absolute_addresses_(absolute_addresses), 339 class_filter_(class_filter), 340 method_filter_(method_filter), 341 list_classes_(list_classes), 342 list_methods_(list_methods), 343 export_dex_location_(export_dex_location), 344 addr2instr_(addr2instr), 345 class_loader_(nullptr) {} 346 347 const bool dump_raw_mapping_table_; 348 const bool dump_raw_gc_map_; 349 const bool dump_vmap_; 350 const bool dump_code_info_stack_maps_; 351 const bool disassemble_code_; 352 const bool absolute_addresses_; 353 const char* const class_filter_; 354 const char* const method_filter_; 355 const bool list_classes_; 356 const bool list_methods_; 357 const char* const export_dex_location_; 358 uint32_t addr2instr_; 359 Handle<mirror::ClassLoader>* class_loader_; 360}; 361 362class OatDumper { 363 public: 364 OatDumper(const OatFile& oat_file, const OatDumperOptions& options) 365 : oat_file_(oat_file), 366 oat_dex_files_(oat_file.GetOatDexFiles()), 367 options_(options), 368 resolved_addr2instr_(0), 369 instruction_set_(oat_file_.GetOatHeader().GetInstructionSet()), 370 disassembler_(Disassembler::Create(instruction_set_, 371 new DisassemblerOptions(options_.absolute_addresses_, 372 oat_file.Begin(), 373 true /* can_read_literals_ */))) { 374 CHECK(options_.class_loader_ != nullptr); 375 CHECK(options_.class_filter_ != nullptr); 376 CHECK(options_.method_filter_ != nullptr); 377 AddAllOffsets(); 378 } 379 380 ~OatDumper() { 381 delete disassembler_; 382 } 383 384 InstructionSet GetInstructionSet() { 385 return instruction_set_; 386 } 387 388 bool Dump(std::ostream& os) { 389 bool success = true; 390 const OatHeader& oat_header = oat_file_.GetOatHeader(); 391 392 os << "MAGIC:\n"; 393 os << oat_header.GetMagic() << "\n\n"; 394 395 os << "CHECKSUM:\n"; 396 os << StringPrintf("0x%08x\n\n", oat_header.GetChecksum()); 397 398 os << "INSTRUCTION SET:\n"; 399 os << oat_header.GetInstructionSet() << "\n\n"; 400 401 { 402 std::unique_ptr<const InstructionSetFeatures> features( 403 InstructionSetFeatures::FromBitmap(oat_header.GetInstructionSet(), 404 oat_header.GetInstructionSetFeaturesBitmap())); 405 os << "INSTRUCTION SET FEATURES:\n"; 406 os << features->GetFeatureString() << "\n\n"; 407 } 408 409 os << "DEX FILE COUNT:\n"; 410 os << oat_header.GetDexFileCount() << "\n\n"; 411 412#define DUMP_OAT_HEADER_OFFSET(label, offset) \ 413 os << label " OFFSET:\n"; \ 414 os << StringPrintf("0x%08x", oat_header.offset()); \ 415 if (oat_header.offset() != 0 && options_.absolute_addresses_) { \ 416 os << StringPrintf(" (%p)", oat_file_.Begin() + oat_header.offset()); \ 417 } \ 418 os << StringPrintf("\n\n"); 419 420 DUMP_OAT_HEADER_OFFSET("EXECUTABLE", GetExecutableOffset); 421 DUMP_OAT_HEADER_OFFSET("INTERPRETER TO INTERPRETER BRIDGE", 422 GetInterpreterToInterpreterBridgeOffset); 423 DUMP_OAT_HEADER_OFFSET("INTERPRETER TO COMPILED CODE BRIDGE", 424 GetInterpreterToCompiledCodeBridgeOffset); 425 DUMP_OAT_HEADER_OFFSET("JNI DLSYM LOOKUP", 426 GetJniDlsymLookupOffset); 427 DUMP_OAT_HEADER_OFFSET("QUICK GENERIC JNI TRAMPOLINE", 428 GetQuickGenericJniTrampolineOffset); 429 DUMP_OAT_HEADER_OFFSET("QUICK IMT CONFLICT TRAMPOLINE", 430 GetQuickImtConflictTrampolineOffset); 431 DUMP_OAT_HEADER_OFFSET("QUICK RESOLUTION TRAMPOLINE", 432 GetQuickResolutionTrampolineOffset); 433 DUMP_OAT_HEADER_OFFSET("QUICK TO INTERPRETER BRIDGE", 434 GetQuickToInterpreterBridgeOffset); 435#undef DUMP_OAT_HEADER_OFFSET 436 437 os << "IMAGE PATCH DELTA:\n"; 438 os << StringPrintf("%d (0x%08x)\n\n", 439 oat_header.GetImagePatchDelta(), 440 oat_header.GetImagePatchDelta()); 441 442 os << "IMAGE FILE LOCATION OAT CHECKSUM:\n"; 443 os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum()); 444 445 os << "IMAGE FILE LOCATION OAT BEGIN:\n"; 446 os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatDataBegin()); 447 448 // Print the key-value store. 449 { 450 os << "KEY VALUE STORE:\n"; 451 size_t index = 0; 452 const char* key; 453 const char* value; 454 while (oat_header.GetStoreKeyValuePairByIndex(index, &key, &value)) { 455 os << key << " = " << value << "\n"; 456 index++; 457 } 458 os << "\n"; 459 } 460 461 if (options_.absolute_addresses_) { 462 os << "BEGIN:\n"; 463 os << reinterpret_cast<const void*>(oat_file_.Begin()) << "\n\n"; 464 465 os << "END:\n"; 466 os << reinterpret_cast<const void*>(oat_file_.End()) << "\n\n"; 467 } 468 469 os << "SIZE:\n"; 470 os << oat_file_.Size() << "\n\n"; 471 472 os << std::flush; 473 474 // If set, adjust relative address to be searched 475 if (options_.addr2instr_ != 0) { 476 resolved_addr2instr_ = options_.addr2instr_ + oat_header.GetExecutableOffset(); 477 os << "SEARCH ADDRESS (executable offset + input):\n"; 478 os << StringPrintf("0x%08x\n\n", resolved_addr2instr_); 479 } 480 481 for (size_t i = 0; i < oat_dex_files_.size(); i++) { 482 const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; 483 CHECK(oat_dex_file != nullptr); 484 485 // If file export selected skip file analysis 486 if (options_.export_dex_location_) { 487 if (!ExportDexFile(os, *oat_dex_file)) { 488 success = false; 489 } 490 } else { 491 if (!DumpOatDexFile(os, *oat_dex_file)) { 492 success = false; 493 } 494 } 495 } 496 os << std::flush; 497 return success; 498 } 499 500 size_t ComputeSize(const void* oat_data) { 501 if (reinterpret_cast<const uint8_t*>(oat_data) < oat_file_.Begin() || 502 reinterpret_cast<const uint8_t*>(oat_data) > oat_file_.End()) { 503 return 0; // Address not in oat file 504 } 505 uintptr_t begin_offset = reinterpret_cast<uintptr_t>(oat_data) - 506 reinterpret_cast<uintptr_t>(oat_file_.Begin()); 507 auto it = offsets_.upper_bound(begin_offset); 508 CHECK(it != offsets_.end()); 509 uintptr_t end_offset = *it; 510 return end_offset - begin_offset; 511 } 512 513 InstructionSet GetOatInstructionSet() { 514 return oat_file_.GetOatHeader().GetInstructionSet(); 515 } 516 517 const void* GetQuickOatCode(ArtMethod* m) SHARED_REQUIRES(Locks::mutator_lock_) { 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 LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation() 525 << "': " << error_msg; 526 } else { 527 const char* descriptor = m->GetDeclaringClassDescriptor(); 528 const DexFile::ClassDef* class_def = 529 dex_file->FindClassDef(descriptor, ComputeModifiedUtf8Hash(descriptor)); 530 if (class_def != nullptr) { 531 uint16_t class_def_index = dex_file->GetIndexForClassDef(*class_def); 532 const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index); 533 size_t method_index = m->GetMethodIndex(); 534 return oat_class.GetOatMethod(method_index).GetQuickCode(); 535 } 536 } 537 } 538 return nullptr; 539 } 540 541 private: 542 void AddAllOffsets() { 543 // We don't know the length of the code for each method, but we need to know where to stop 544 // when disassembling. What we do know is that a region of code will be followed by some other 545 // region, so if we keep a sorted sequence of the start of each region, we can infer the length 546 // of a piece of code by using upper_bound to find the start of the next region. 547 for (size_t i = 0; i < oat_dex_files_.size(); i++) { 548 const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; 549 CHECK(oat_dex_file != nullptr); 550 std::string error_msg; 551 const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg); 552 if (dex_file == nullptr) { 553 LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation() 554 << "': " << error_msg; 555 continue; 556 } 557 offsets_.insert(reinterpret_cast<uintptr_t>(&dex_file->GetHeader())); 558 for (size_t class_def_index = 0; 559 class_def_index < dex_file->NumClassDefs(); 560 class_def_index++) { 561 const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); 562 const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index); 563 const uint8_t* class_data = dex_file->GetClassData(class_def); 564 if (class_data != nullptr) { 565 ClassDataItemIterator it(*dex_file, class_data); 566 SkipAllFields(it); 567 uint32_t class_method_index = 0; 568 while (it.HasNextDirectMethod()) { 569 AddOffsets(oat_class.GetOatMethod(class_method_index++)); 570 it.Next(); 571 } 572 while (it.HasNextVirtualMethod()) { 573 AddOffsets(oat_class.GetOatMethod(class_method_index++)); 574 it.Next(); 575 } 576 } 577 } 578 } 579 580 // If the last thing in the file is code for a method, there won't be an offset for the "next" 581 // thing. Instead of having a special case in the upper_bound code, let's just add an entry 582 // for the end of the file. 583 offsets_.insert(oat_file_.Size()); 584 } 585 586 static uint32_t AlignCodeOffset(uint32_t maybe_thumb_offset) { 587 return maybe_thumb_offset & ~0x1; // TODO: Make this Thumb2 specific. 588 } 589 590 void AddOffsets(const OatFile::OatMethod& oat_method) { 591 uint32_t code_offset = oat_method.GetCodeOffset(); 592 if (oat_file_.GetOatHeader().GetInstructionSet() == kThumb2) { 593 code_offset &= ~0x1; 594 } 595 offsets_.insert(code_offset); 596 offsets_.insert(oat_method.GetMappingTableOffset()); 597 offsets_.insert(oat_method.GetVmapTableOffset()); 598 offsets_.insert(oat_method.GetGcMapOffset()); 599 } 600 601 bool DumpOatDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) { 602 bool success = true; 603 bool stop_analysis = false; 604 os << "OatDexFile:\n"; 605 os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str()); 606 os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum()); 607 608 // Create the verifier early. 609 610 std::string error_msg; 611 const DexFile* const dex_file = OpenDexFile(&oat_dex_file, &error_msg); 612 if (dex_file == nullptr) { 613 os << "NOT FOUND: " << error_msg << "\n\n"; 614 os << std::flush; 615 return false; 616 } 617 618 VariableIndentationOutputStream vios(&os); 619 ScopedIndentation indent1(&vios); 620 for (size_t class_def_index = 0; 621 class_def_index < dex_file->NumClassDefs(); 622 class_def_index++) { 623 const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); 624 const char* descriptor = dex_file->GetClassDescriptor(class_def); 625 626 // TODO: Support regex 627 if (DescriptorToDot(descriptor).find(options_.class_filter_) == std::string::npos) { 628 continue; 629 } 630 631 uint32_t oat_class_offset = oat_dex_file.GetOatClassOffset(class_def_index); 632 const OatFile::OatClass oat_class = oat_dex_file.GetOatClass(class_def_index); 633 os << StringPrintf("%zd: %s (offset=0x%08x) (type_idx=%d)", 634 class_def_index, descriptor, oat_class_offset, class_def.class_idx_) 635 << " (" << oat_class.GetStatus() << ")" 636 << " (" << oat_class.GetType() << ")\n"; 637 // TODO: include bitmap here if type is kOatClassSomeCompiled? 638 if (options_.list_classes_) continue; 639 if (!DumpOatClass(&vios, oat_class, *dex_file, class_def, &stop_analysis)) { 640 success = false; 641 } 642 if (stop_analysis) { 643 os << std::flush; 644 return success; 645 } 646 } 647 648 os << std::flush; 649 return success; 650 } 651 652 bool ExportDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) { 653 std::string error_msg; 654 std::string dex_file_location = oat_dex_file.GetDexFileLocation(); 655 656 const DexFile* const dex_file = OpenDexFile(&oat_dex_file, &error_msg); 657 if (dex_file == nullptr) { 658 os << "Failed to open dex file '" << dex_file_location << "': " << error_msg; 659 return false; 660 } 661 size_t fsize = oat_dex_file.FileSize(); 662 663 // Some quick checks just in case 664 if (fsize == 0 || fsize < sizeof(DexFile::Header)) { 665 os << "Invalid dex file\n"; 666 return false; 667 } 668 669 // Verify output directory exists 670 if (!OS::DirectoryExists(options_.export_dex_location_)) { 671 // TODO: Extend OS::DirectoryExists if symlink support is required 672 os << options_.export_dex_location_ << " output directory not found or symlink\n"; 673 return false; 674 } 675 676 // Beautify path names 677 if (dex_file_location.size() > PATH_MAX || dex_file_location.size() <= 0) { 678 return false; 679 } 680 681 std::string dex_orig_name; 682 size_t dex_orig_pos = dex_file_location.rfind('/'); 683 if (dex_orig_pos == std::string::npos) 684 dex_orig_name = dex_file_location; 685 else 686 dex_orig_name = dex_file_location.substr(dex_orig_pos + 1); 687 688 // A more elegant approach to efficiently name user installed apps is welcome 689 if (dex_orig_name.size() == 8 && !dex_orig_name.compare("base.apk")) { 690 dex_file_location.erase(dex_orig_pos, strlen("base.apk") + 1); 691 size_t apk_orig_pos = dex_file_location.rfind('/'); 692 if (apk_orig_pos != std::string::npos) { 693 dex_orig_name = dex_file_location.substr(++apk_orig_pos); 694 } 695 } 696 697 std::string out_dex_path(options_.export_dex_location_); 698 if (out_dex_path.back() != '/') { 699 out_dex_path.append("/"); 700 } 701 out_dex_path.append(dex_orig_name); 702 out_dex_path.append("_export.dex"); 703 if (out_dex_path.length() > PATH_MAX) { 704 return false; 705 } 706 707 std::unique_ptr<File> file(OS::CreateEmptyFile(out_dex_path.c_str())); 708 if (file.get() == nullptr) { 709 os << "Failed to open output dex file " << out_dex_path; 710 return false; 711 } 712 713 if (!file->WriteFully(dex_file->Begin(), fsize)) { 714 os << "Failed to write dex file"; 715 file->Erase(); 716 return false; 717 } 718 719 if (file->FlushCloseOrErase() != 0) { 720 os << "Flush and close failed"; 721 return false; 722 } 723 724 os << StringPrintf("Dex file exported at %s (%zd bytes)\n", out_dex_path.c_str(), fsize); 725 os << std::flush; 726 727 return true; 728 } 729 730 static void SkipAllFields(ClassDataItemIterator& it) { 731 while (it.HasNextStaticField()) { 732 it.Next(); 733 } 734 while (it.HasNextInstanceField()) { 735 it.Next(); 736 } 737 } 738 739 bool DumpOatClass(VariableIndentationOutputStream* vios, 740 const OatFile::OatClass& oat_class, const DexFile& dex_file, 741 const DexFile::ClassDef& class_def, bool* stop_analysis) { 742 bool success = true; 743 bool addr_found = false; 744 const uint8_t* class_data = dex_file.GetClassData(class_def); 745 if (class_data == nullptr) { // empty class such as a marker interface? 746 vios->Stream() << std::flush; 747 return success; 748 } 749 ClassDataItemIterator it(dex_file, class_data); 750 SkipAllFields(it); 751 uint32_t class_method_index = 0; 752 while (it.HasNextDirectMethod()) { 753 if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file, 754 it.GetMemberIndex(), it.GetMethodCodeItem(), 755 it.GetRawMemberAccessFlags(), &addr_found)) { 756 success = false; 757 } 758 if (addr_found) { 759 *stop_analysis = true; 760 return success; 761 } 762 class_method_index++; 763 it.Next(); 764 } 765 while (it.HasNextVirtualMethod()) { 766 if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file, 767 it.GetMemberIndex(), it.GetMethodCodeItem(), 768 it.GetRawMemberAccessFlags(), &addr_found)) { 769 success = false; 770 } 771 if (addr_found) { 772 *stop_analysis = true; 773 return success; 774 } 775 class_method_index++; 776 it.Next(); 777 } 778 DCHECK(!it.HasNext()); 779 vios->Stream() << std::flush; 780 return success; 781 } 782 783 static constexpr uint32_t kPrologueBytes = 16; 784 785 // When this was picked, the largest arm method was 55,256 bytes and arm64 was 50,412 bytes. 786 static constexpr uint32_t kMaxCodeSize = 100 * 1000; 787 788 bool DumpOatMethod(VariableIndentationOutputStream* vios, 789 const DexFile::ClassDef& class_def, 790 uint32_t class_method_index, 791 const OatFile::OatClass& oat_class, const DexFile& dex_file, 792 uint32_t dex_method_idx, const DexFile::CodeItem* code_item, 793 uint32_t method_access_flags, bool* addr_found) { 794 bool success = true; 795 796 // TODO: Support regex 797 std::string method_name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx)); 798 if (method_name.find(options_.method_filter_) == std::string::npos) { 799 return success; 800 } 801 802 std::string pretty_method = PrettyMethod(dex_method_idx, dex_file, true); 803 vios->Stream() << StringPrintf("%d: %s (dex_method_idx=%d)\n", 804 class_method_index, pretty_method.c_str(), 805 dex_method_idx); 806 if (options_.list_methods_) return success; 807 808 uint32_t oat_method_offsets_offset = oat_class.GetOatMethodOffsetsOffset(class_method_index); 809 const OatMethodOffsets* oat_method_offsets = oat_class.GetOatMethodOffsets(class_method_index); 810 const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index); 811 uint32_t code_offset = oat_method.GetCodeOffset(); 812 uint32_t code_size = oat_method.GetQuickCodeSize(); 813 if (resolved_addr2instr_ != 0) { 814 if (resolved_addr2instr_ > code_offset + code_size) { 815 return success; 816 } else { 817 *addr_found = true; // stop analyzing file at next iteration 818 } 819 } 820 821 // Everything below is indented at least once. 822 ScopedIndentation indent1(vios); 823 824 { 825 vios->Stream() << "DEX CODE:\n"; 826 ScopedIndentation indent2(vios); 827 DumpDexCode(vios->Stream(), dex_file, code_item); 828 } 829 830 std::unique_ptr<verifier::MethodVerifier> verifier; 831 if (Runtime::Current() != nullptr) { 832 vios->Stream() << "VERIFIER TYPE ANALYSIS:\n"; 833 ScopedIndentation indent2(vios); 834 verifier.reset(DumpVerifier(vios, 835 dex_method_idx, &dex_file, class_def, code_item, 836 method_access_flags)); 837 } 838 { 839 vios->Stream() << "OatMethodOffsets "; 840 if (options_.absolute_addresses_) { 841 vios->Stream() << StringPrintf("%p ", oat_method_offsets); 842 } 843 vios->Stream() << StringPrintf("(offset=0x%08x)\n", oat_method_offsets_offset); 844 if (oat_method_offsets_offset > oat_file_.Size()) { 845 vios->Stream() << StringPrintf( 846 "WARNING: oat method offsets offset 0x%08x is past end of file 0x%08zx.\n", 847 oat_method_offsets_offset, oat_file_.Size()); 848 // If we can't read OatMethodOffsets, the rest of the data is dangerous to read. 849 vios->Stream() << std::flush; 850 return false; 851 } 852 853 ScopedIndentation indent2(vios); 854 vios->Stream() << StringPrintf("code_offset: 0x%08x ", code_offset); 855 uint32_t aligned_code_begin = AlignCodeOffset(oat_method.GetCodeOffset()); 856 if (aligned_code_begin > oat_file_.Size()) { 857 vios->Stream() << StringPrintf("WARNING: " 858 "code offset 0x%08x is past end of file 0x%08zx.\n", 859 aligned_code_begin, oat_file_.Size()); 860 success = false; 861 } 862 vios->Stream() << "\n"; 863 864 vios->Stream() << "gc_map: "; 865 if (options_.absolute_addresses_) { 866 vios->Stream() << StringPrintf("%p ", oat_method.GetGcMap()); 867 } 868 uint32_t gc_map_offset = oat_method.GetGcMapOffset(); 869 vios->Stream() << StringPrintf("(offset=0x%08x)\n", gc_map_offset); 870 if (gc_map_offset > oat_file_.Size()) { 871 vios->Stream() << StringPrintf("WARNING: " 872 "gc map table offset 0x%08x is past end of file 0x%08zx.\n", 873 gc_map_offset, oat_file_.Size()); 874 success = false; 875 } else if (options_.dump_raw_gc_map_) { 876 ScopedIndentation indent3(vios); 877 DumpGcMap(vios->Stream(), oat_method, code_item); 878 } 879 } 880 { 881 vios->Stream() << "OatQuickMethodHeader "; 882 uint32_t method_header_offset = oat_method.GetOatQuickMethodHeaderOffset(); 883 const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader(); 884 885 if (options_.absolute_addresses_) { 886 vios->Stream() << StringPrintf("%p ", method_header); 887 } 888 vios->Stream() << StringPrintf("(offset=0x%08x)\n", method_header_offset); 889 if (method_header_offset > oat_file_.Size()) { 890 vios->Stream() << StringPrintf( 891 "WARNING: oat quick method header offset 0x%08x is past end of file 0x%08zx.\n", 892 method_header_offset, oat_file_.Size()); 893 // If we can't read the OatQuickMethodHeader, the rest of the data is dangerous to read. 894 vios->Stream() << std::flush; 895 return false; 896 } 897 898 ScopedIndentation indent2(vios); 899 vios->Stream() << "mapping_table: "; 900 if (options_.absolute_addresses_) { 901 vios->Stream() << StringPrintf("%p ", oat_method.GetMappingTable()); 902 } 903 uint32_t mapping_table_offset = oat_method.GetMappingTableOffset(); 904 vios->Stream() << StringPrintf("(offset=0x%08x)\n", oat_method.GetMappingTableOffset()); 905 if (mapping_table_offset > oat_file_.Size()) { 906 vios->Stream() << StringPrintf("WARNING: " 907 "mapping table offset 0x%08x is past end of file 0x%08zx. " 908 "mapping table offset was loaded from offset 0x%08x.\n", 909 mapping_table_offset, oat_file_.Size(), 910 oat_method.GetMappingTableOffsetOffset()); 911 success = false; 912 } else if (options_.dump_raw_mapping_table_) { 913 ScopedIndentation indent3(vios); 914 DumpMappingTable(vios, oat_method); 915 } 916 917 vios->Stream() << "vmap_table: "; 918 if (options_.absolute_addresses_) { 919 vios->Stream() << StringPrintf("%p ", oat_method.GetVmapTable()); 920 } 921 uint32_t vmap_table_offset = oat_method.GetVmapTableOffset(); 922 vios->Stream() << StringPrintf("(offset=0x%08x)\n", vmap_table_offset); 923 if (vmap_table_offset > oat_file_.Size()) { 924 vios->Stream() << StringPrintf("WARNING: " 925 "vmap table offset 0x%08x is past end of file 0x%08zx. " 926 "vmap table offset was loaded from offset 0x%08x.\n", 927 vmap_table_offset, oat_file_.Size(), 928 oat_method.GetVmapTableOffsetOffset()); 929 success = false; 930 } else if (options_.dump_vmap_) { 931 DumpVmapData(vios, oat_method, code_item); 932 } 933 } 934 { 935 vios->Stream() << "QuickMethodFrameInfo\n"; 936 937 ScopedIndentation indent2(vios); 938 vios->Stream() 939 << StringPrintf("frame_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes()); 940 vios->Stream() << StringPrintf("core_spill_mask: 0x%08x ", oat_method.GetCoreSpillMask()); 941 DumpSpillMask(vios->Stream(), oat_method.GetCoreSpillMask(), false); 942 vios->Stream() << "\n"; 943 vios->Stream() << StringPrintf("fp_spill_mask: 0x%08x ", oat_method.GetFpSpillMask()); 944 DumpSpillMask(vios->Stream(), oat_method.GetFpSpillMask(), true); 945 vios->Stream() << "\n"; 946 } 947 { 948 // Based on spill masks from QuickMethodFrameInfo so placed 949 // after it is dumped, but useful for understanding quick 950 // code, so dumped here. 951 ScopedIndentation indent2(vios); 952 DumpVregLocations(vios->Stream(), oat_method, code_item); 953 } 954 { 955 vios->Stream() << "CODE: "; 956 uint32_t code_size_offset = oat_method.GetQuickCodeSizeOffset(); 957 if (code_size_offset > oat_file_.Size()) { 958 ScopedIndentation indent2(vios); 959 vios->Stream() << StringPrintf("WARNING: " 960 "code size offset 0x%08x is past end of file 0x%08zx.", 961 code_size_offset, oat_file_.Size()); 962 success = false; 963 } else { 964 const void* code = oat_method.GetQuickCode(); 965 uint32_t aligned_code_begin = AlignCodeOffset(code_offset); 966 uint64_t aligned_code_end = aligned_code_begin + code_size; 967 968 if (options_.absolute_addresses_) { 969 vios->Stream() << StringPrintf("%p ", code); 970 } 971 vios->Stream() << StringPrintf("(code_offset=0x%08x size_offset=0x%08x size=%u)%s\n", 972 code_offset, 973 code_size_offset, 974 code_size, 975 code != nullptr ? "..." : ""); 976 977 ScopedIndentation indent2(vios); 978 if (aligned_code_begin > oat_file_.Size()) { 979 vios->Stream() << StringPrintf("WARNING: " 980 "start of code at 0x%08x is past end of file 0x%08zx.", 981 aligned_code_begin, oat_file_.Size()); 982 success = false; 983 } else if (aligned_code_end > oat_file_.Size()) { 984 vios->Stream() << StringPrintf( 985 "WARNING: " 986 "end of code at 0x%08" PRIx64 " is past end of file 0x%08zx. " 987 "code size is 0x%08x loaded from offset 0x%08x.\n", 988 aligned_code_end, oat_file_.Size(), 989 code_size, code_size_offset); 990 success = false; 991 if (options_.disassemble_code_) { 992 if (code_size_offset + kPrologueBytes <= oat_file_.Size()) { 993 DumpCode(vios, verifier.get(), oat_method, code_item, true, kPrologueBytes); 994 } 995 } 996 } else if (code_size > kMaxCodeSize) { 997 vios->Stream() << StringPrintf( 998 "WARNING: " 999 "code size %d is bigger than max expected threshold of %d. " 1000 "code size is 0x%08x loaded from offset 0x%08x.\n", 1001 code_size, kMaxCodeSize, 1002 code_size, code_size_offset); 1003 success = false; 1004 if (options_.disassemble_code_) { 1005 if (code_size_offset + kPrologueBytes <= oat_file_.Size()) { 1006 DumpCode(vios, verifier.get(), oat_method, code_item, true, kPrologueBytes); 1007 } 1008 } 1009 } else if (options_.disassemble_code_) { 1010 DumpCode(vios, verifier.get(), oat_method, code_item, !success, 0); 1011 } 1012 } 1013 } 1014 vios->Stream() << std::flush; 1015 return success; 1016 } 1017 1018 void DumpSpillMask(std::ostream& os, uint32_t spill_mask, bool is_float) { 1019 if (spill_mask == 0) { 1020 return; 1021 } 1022 os << "("; 1023 for (size_t i = 0; i < 32; i++) { 1024 if ((spill_mask & (1 << i)) != 0) { 1025 if (is_float) { 1026 os << "fr" << i; 1027 } else { 1028 os << "r" << i; 1029 } 1030 spill_mask ^= 1 << i; // clear bit 1031 if (spill_mask != 0) { 1032 os << ", "; 1033 } else { 1034 break; 1035 } 1036 } 1037 } 1038 os << ")"; 1039 } 1040 1041 // Display data stored at the the vmap offset of an oat method. 1042 void DumpVmapData(VariableIndentationOutputStream* vios, 1043 const OatFile::OatMethod& oat_method, 1044 const DexFile::CodeItem* code_item) { 1045 if (IsMethodGeneratedByOptimizingCompiler(oat_method, code_item)) { 1046 // The optimizing compiler outputs its CodeInfo data in the vmap table. 1047 const void* raw_code_info = oat_method.GetVmapTable(); 1048 if (raw_code_info != nullptr) { 1049 CodeInfo code_info(raw_code_info); 1050 DCHECK(code_item != nullptr); 1051 ScopedIndentation indent1(vios); 1052 DumpCodeInfo(vios, code_info, oat_method, *code_item); 1053 } 1054 } else if (IsMethodGeneratedByDexToDexCompiler(oat_method, code_item)) { 1055 // We don't encode the size in the table, so just emit that we have quickened 1056 // information. 1057 ScopedIndentation indent(vios); 1058 vios->Stream() << "quickened data\n"; 1059 } else { 1060 // Otherwise, display the vmap table. 1061 const uint8_t* raw_table = oat_method.GetVmapTable(); 1062 if (raw_table != nullptr) { 1063 VmapTable vmap_table(raw_table); 1064 DumpVmapTable(vios->Stream(), oat_method, vmap_table); 1065 } 1066 } 1067 } 1068 1069 // Display a CodeInfo object emitted by the optimizing compiler. 1070 void DumpCodeInfo(VariableIndentationOutputStream* vios, 1071 const CodeInfo& code_info, 1072 const OatFile::OatMethod& oat_method, 1073 const DexFile::CodeItem& code_item) { 1074 code_info.Dump(vios, 1075 oat_method.GetCodeOffset(), 1076 code_item.registers_size_, 1077 options_.dump_code_info_stack_maps_); 1078 } 1079 1080 // Display a vmap table. 1081 void DumpVmapTable(std::ostream& os, 1082 const OatFile::OatMethod& oat_method, 1083 const VmapTable& vmap_table) { 1084 bool first = true; 1085 bool processing_fp = false; 1086 uint32_t spill_mask = oat_method.GetCoreSpillMask(); 1087 for (size_t i = 0; i < vmap_table.Size(); i++) { 1088 uint16_t dex_reg = vmap_table[i]; 1089 uint32_t cpu_reg = vmap_table.ComputeRegister(spill_mask, i, 1090 processing_fp ? kFloatVReg : kIntVReg); 1091 os << (first ? "v" : ", v") << dex_reg; 1092 if (!processing_fp) { 1093 os << "/r" << cpu_reg; 1094 } else { 1095 os << "/fr" << cpu_reg; 1096 } 1097 first = false; 1098 if (!processing_fp && dex_reg == 0xFFFF) { 1099 processing_fp = true; 1100 spill_mask = oat_method.GetFpSpillMask(); 1101 } 1102 } 1103 os << "\n"; 1104 } 1105 1106 void DumpVregLocations(std::ostream& os, const OatFile::OatMethod& oat_method, 1107 const DexFile::CodeItem* code_item) { 1108 if (code_item != nullptr) { 1109 size_t num_locals_ins = code_item->registers_size_; 1110 size_t num_ins = code_item->ins_size_; 1111 size_t num_locals = num_locals_ins - num_ins; 1112 size_t num_outs = code_item->outs_size_; 1113 1114 os << "vr_stack_locations:"; 1115 for (size_t reg = 0; reg <= num_locals_ins; reg++) { 1116 // For readability, delimit the different kinds of VRs. 1117 if (reg == num_locals_ins) { 1118 os << "\n\tmethod*:"; 1119 } else if (reg == num_locals && num_ins > 0) { 1120 os << "\n\tins:"; 1121 } else if (reg == 0 && num_locals > 0) { 1122 os << "\n\tlocals:"; 1123 } 1124 1125 uint32_t offset = StackVisitor::GetVRegOffsetFromQuickCode( 1126 code_item, 1127 oat_method.GetCoreSpillMask(), 1128 oat_method.GetFpSpillMask(), 1129 oat_method.GetFrameSizeInBytes(), 1130 reg, 1131 GetInstructionSet()); 1132 os << " v" << reg << "[sp + #" << offset << "]"; 1133 } 1134 1135 for (size_t out_reg = 0; out_reg < num_outs; out_reg++) { 1136 if (out_reg == 0) { 1137 os << "\n\touts:"; 1138 } 1139 1140 uint32_t offset = StackVisitor::GetOutVROffset(out_reg, GetInstructionSet()); 1141 os << " v" << out_reg << "[sp + #" << offset << "]"; 1142 } 1143 1144 os << "\n"; 1145 } 1146 } 1147 1148 void DescribeVReg(std::ostream& os, const OatFile::OatMethod& oat_method, 1149 const DexFile::CodeItem* code_item, size_t reg, VRegKind kind) { 1150 const uint8_t* raw_table = oat_method.GetVmapTable(); 1151 if (raw_table != nullptr) { 1152 const VmapTable vmap_table(raw_table); 1153 uint32_t vmap_offset; 1154 if (vmap_table.IsInContext(reg, kind, &vmap_offset)) { 1155 bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg); 1156 uint32_t spill_mask = is_float ? oat_method.GetFpSpillMask() 1157 : oat_method.GetCoreSpillMask(); 1158 os << (is_float ? "fr" : "r") << vmap_table.ComputeRegister(spill_mask, vmap_offset, kind); 1159 } else { 1160 uint32_t offset = StackVisitor::GetVRegOffsetFromQuickCode( 1161 code_item, 1162 oat_method.GetCoreSpillMask(), 1163 oat_method.GetFpSpillMask(), 1164 oat_method.GetFrameSizeInBytes(), 1165 reg, 1166 GetInstructionSet()); 1167 os << "[sp + #" << offset << "]"; 1168 } 1169 } 1170 } 1171 1172 void DumpGcMapRegisters(std::ostream& os, const OatFile::OatMethod& oat_method, 1173 const DexFile::CodeItem* code_item, 1174 size_t num_regs, const uint8_t* reg_bitmap) { 1175 bool first = true; 1176 for (size_t reg = 0; reg < num_regs; reg++) { 1177 if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) { 1178 if (first) { 1179 os << " v" << reg << " ("; 1180 DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg); 1181 os << ")"; 1182 first = false; 1183 } else { 1184 os << ", v" << reg << " ("; 1185 DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg); 1186 os << ")"; 1187 } 1188 } 1189 } 1190 if (first) { 1191 os << "No registers in GC map\n"; 1192 } else { 1193 os << "\n"; 1194 } 1195 } 1196 void DumpGcMap(std::ostream& os, const OatFile::OatMethod& oat_method, 1197 const DexFile::CodeItem* code_item) { 1198 const uint8_t* gc_map_raw = oat_method.GetGcMap(); 1199 if (gc_map_raw == nullptr) { 1200 return; // No GC map. 1201 } 1202 const void* quick_code = oat_method.GetQuickCode(); 1203 NativePcOffsetToReferenceMap map(gc_map_raw); 1204 for (size_t entry = 0; entry < map.NumEntries(); entry++) { 1205 const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(quick_code) + 1206 map.GetNativePcOffset(entry); 1207 os << StringPrintf("%p", native_pc); 1208 DumpGcMapRegisters(os, oat_method, code_item, map.RegWidth() * 8, map.GetBitMap(entry)); 1209 } 1210 } 1211 1212 void DumpMappingTable(VariableIndentationOutputStream* vios, 1213 const OatFile::OatMethod& oat_method) { 1214 const void* quick_code = oat_method.GetQuickCode(); 1215 if (quick_code == nullptr) { 1216 return; 1217 } 1218 MappingTable table(oat_method.GetMappingTable()); 1219 if (table.TotalSize() != 0) { 1220 if (table.PcToDexSize() != 0) { 1221 typedef MappingTable::PcToDexIterator It; 1222 vios->Stream() << "suspend point mappings {\n"; 1223 for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) { 1224 ScopedIndentation indent1(vios); 1225 vios->Stream() << StringPrintf("0x%04x -> 0x%04x\n", cur.NativePcOffset(), cur.DexPc()); 1226 } 1227 vios->Stream() << "}\n"; 1228 } 1229 if (table.DexToPcSize() != 0) { 1230 typedef MappingTable::DexToPcIterator It; 1231 vios->Stream() << "catch entry mappings {\n"; 1232 for (It cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) { 1233 ScopedIndentation indent1(vios); 1234 vios->Stream() << StringPrintf("0x%04x -> 0x%04x\n", cur.NativePcOffset(), cur.DexPc()); 1235 } 1236 vios->Stream() << "}\n"; 1237 } 1238 } 1239 } 1240 1241 uint32_t DumpInformationAtOffset(VariableIndentationOutputStream* vios, 1242 const OatFile::OatMethod& oat_method, 1243 const DexFile::CodeItem* code_item, 1244 size_t offset, 1245 bool suspend_point_mapping) { 1246 if (IsMethodGeneratedByOptimizingCompiler(oat_method, code_item)) { 1247 if (suspend_point_mapping) { 1248 ScopedIndentation indent1(vios); 1249 DumpDexRegisterMapAtOffset(vios, oat_method, code_item, offset); 1250 } 1251 // The return value is not used in the case of a method compiled 1252 // with the optimizing compiler. 1253 return DexFile::kDexNoIndex; 1254 } else { 1255 return DumpMappingAtOffset(vios->Stream(), oat_method, offset, suspend_point_mapping); 1256 } 1257 } 1258 1259 uint32_t DumpMappingAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method, 1260 size_t offset, bool suspend_point_mapping) { 1261 MappingTable table(oat_method.GetMappingTable()); 1262 if (suspend_point_mapping && table.PcToDexSize() > 0) { 1263 typedef MappingTable::PcToDexIterator It; 1264 for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) { 1265 if (offset == cur.NativePcOffset()) { 1266 os << StringPrintf("suspend point dex PC: 0x%04x\n", cur.DexPc()); 1267 return cur.DexPc(); 1268 } 1269 } 1270 } else if (!suspend_point_mapping && table.DexToPcSize() > 0) { 1271 typedef MappingTable::DexToPcIterator It; 1272 for (It cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) { 1273 if (offset == cur.NativePcOffset()) { 1274 os << StringPrintf("catch entry dex PC: 0x%04x\n", cur.DexPc()); 1275 return cur.DexPc(); 1276 } 1277 } 1278 } 1279 return DexFile::kDexNoIndex; 1280 } 1281 1282 void DumpGcMapAtNativePcOffset(std::ostream& os, const OatFile::OatMethod& oat_method, 1283 const DexFile::CodeItem* code_item, size_t native_pc_offset) { 1284 const uint8_t* gc_map_raw = oat_method.GetGcMap(); 1285 if (gc_map_raw != nullptr) { 1286 NativePcOffsetToReferenceMap map(gc_map_raw); 1287 if (map.HasEntry(native_pc_offset)) { 1288 size_t num_regs = map.RegWidth() * 8; 1289 const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset); 1290 bool first = true; 1291 for (size_t reg = 0; reg < num_regs; reg++) { 1292 if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) { 1293 if (first) { 1294 os << "GC map objects: v" << reg << " ("; 1295 DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg); 1296 os << ")"; 1297 first = false; 1298 } else { 1299 os << ", v" << reg << " ("; 1300 DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg); 1301 os << ")"; 1302 } 1303 } 1304 } 1305 if (!first) { 1306 os << "\n"; 1307 } 1308 } 1309 } 1310 } 1311 1312 void DumpVRegsAtDexPc(std::ostream& os, verifier::MethodVerifier* verifier, 1313 const OatFile::OatMethod& oat_method, 1314 const DexFile::CodeItem* code_item, uint32_t dex_pc) { 1315 DCHECK(verifier != nullptr); 1316 std::vector<int32_t> kinds = verifier->DescribeVRegs(dex_pc); 1317 bool first = true; 1318 for (size_t reg = 0; reg < code_item->registers_size_; reg++) { 1319 VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2)); 1320 if (kind != kUndefined) { 1321 if (first) { 1322 os << "VRegs: v"; 1323 first = false; 1324 } else { 1325 os << ", v"; 1326 } 1327 os << reg << " ("; 1328 switch (kind) { 1329 case kImpreciseConstant: 1330 os << "Imprecise Constant: " << kinds.at((reg * 2) + 1) << ", "; 1331 DescribeVReg(os, oat_method, code_item, reg, kind); 1332 break; 1333 case kConstant: 1334 os << "Constant: " << kinds.at((reg * 2) + 1); 1335 break; 1336 default: 1337 DescribeVReg(os, oat_method, code_item, reg, kind); 1338 break; 1339 } 1340 os << ")"; 1341 } 1342 } 1343 if (!first) { 1344 os << "\n"; 1345 } 1346 } 1347 1348 1349 void DumpDexCode(std::ostream& os, const DexFile& dex_file, const DexFile::CodeItem* code_item) { 1350 if (code_item != nullptr) { 1351 size_t i = 0; 1352 while (i < code_item->insns_size_in_code_units_) { 1353 const Instruction* instruction = Instruction::At(&code_item->insns_[i]); 1354 os << StringPrintf("0x%04zx: ", i) << instruction->DumpHexLE(5) 1355 << StringPrintf("\t| %s\n", instruction->DumpString(&dex_file).c_str()); 1356 i += instruction->SizeInCodeUnits(); 1357 } 1358 } 1359 } 1360 1361 // Has `oat_method` -- corresponding to the Dex `code_item` -- been compiled by 1362 // the optimizing compiler? 1363 static bool IsMethodGeneratedByOptimizingCompiler(const OatFile::OatMethod& oat_method, 1364 const DexFile::CodeItem* code_item) { 1365 // If the native GC map is null and the Dex `code_item` is not 1366 // null, then this method has been compiled with the optimizing 1367 // compiler. 1368 return oat_method.GetQuickCode() != nullptr && 1369 oat_method.GetGcMap() == nullptr && 1370 code_item != nullptr; 1371 } 1372 1373 // Has `oat_method` -- corresponding to the Dex `code_item` -- been compiled by 1374 // the dextodex compiler? 1375 static bool IsMethodGeneratedByDexToDexCompiler(const OatFile::OatMethod& oat_method, 1376 const DexFile::CodeItem* code_item) { 1377 // If the quick code is null, the Dex `code_item` is not 1378 // null, and the vmap table is not null, then this method has been compiled 1379 // with the dextodex compiler. 1380 return oat_method.GetQuickCode() == nullptr && 1381 oat_method.GetVmapTable() != nullptr && 1382 code_item != nullptr; 1383 } 1384 1385 void DumpDexRegisterMapAtOffset(VariableIndentationOutputStream* vios, 1386 const OatFile::OatMethod& oat_method, 1387 const DexFile::CodeItem* code_item, 1388 size_t offset) { 1389 // This method is only relevant for oat methods compiled with the 1390 // optimizing compiler. 1391 DCHECK(IsMethodGeneratedByOptimizingCompiler(oat_method, code_item)); 1392 1393 // The optimizing compiler outputs its CodeInfo data in the vmap table. 1394 const void* raw_code_info = oat_method.GetVmapTable(); 1395 if (raw_code_info != nullptr) { 1396 CodeInfo code_info(raw_code_info); 1397 StackMapEncoding encoding = code_info.ExtractEncoding(); 1398 StackMap stack_map = code_info.GetStackMapForNativePcOffset(offset, encoding); 1399 if (stack_map.IsValid()) { 1400 stack_map.Dump(vios, code_info, encoding, oat_method.GetCodeOffset(), 1401 code_item->registers_size_); 1402 } 1403 } 1404 } 1405 1406 verifier::MethodVerifier* DumpVerifier(VariableIndentationOutputStream* vios, 1407 uint32_t dex_method_idx, 1408 const DexFile* dex_file, 1409 const DexFile::ClassDef& class_def, 1410 const DexFile::CodeItem* code_item, 1411 uint32_t method_access_flags) { 1412 if ((method_access_flags & kAccNative) == 0) { 1413 ScopedObjectAccess soa(Thread::Current()); 1414 StackHandleScope<1> hs(soa.Self()); 1415 Handle<mirror::DexCache> dex_cache( 1416 hs.NewHandle(Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file))); 1417 DCHECK(options_.class_loader_ != nullptr); 1418 return verifier::MethodVerifier::VerifyMethodAndDump( 1419 soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_, 1420 &class_def, code_item, nullptr, method_access_flags); 1421 } 1422 1423 return nullptr; 1424 } 1425 1426 void DumpCode(VariableIndentationOutputStream* vios, 1427 verifier::MethodVerifier* verifier, 1428 const OatFile::OatMethod& oat_method, const DexFile::CodeItem* code_item, 1429 bool bad_input, size_t code_size) { 1430 const void* quick_code = oat_method.GetQuickCode(); 1431 1432 if (code_size == 0) { 1433 code_size = oat_method.GetQuickCodeSize(); 1434 } 1435 if (code_size == 0 || quick_code == nullptr) { 1436 vios->Stream() << "NO CODE!\n"; 1437 return; 1438 } else { 1439 const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code); 1440 size_t offset = 0; 1441 while (offset < code_size) { 1442 if (!bad_input) { 1443 DumpInformationAtOffset(vios, oat_method, code_item, offset, false); 1444 } 1445 offset += disassembler_->Dump(vios->Stream(), quick_native_pc + offset); 1446 if (!bad_input) { 1447 uint32_t dex_pc = 1448 DumpInformationAtOffset(vios, oat_method, code_item, offset, true); 1449 if (dex_pc != DexFile::kDexNoIndex) { 1450 DumpGcMapAtNativePcOffset(vios->Stream(), oat_method, code_item, offset); 1451 if (verifier != nullptr) { 1452 DumpVRegsAtDexPc(vios->Stream(), verifier, oat_method, code_item, dex_pc); 1453 } 1454 } 1455 } 1456 } 1457 } 1458 } 1459 1460 const OatFile& oat_file_; 1461 const std::vector<const OatFile::OatDexFile*> oat_dex_files_; 1462 const OatDumperOptions& options_; 1463 uint32_t resolved_addr2instr_; 1464 InstructionSet instruction_set_; 1465 std::set<uintptr_t> offsets_; 1466 Disassembler* disassembler_; 1467}; 1468 1469class ImageDumper { 1470 public: 1471 ImageDumper(std::ostream* os, gc::space::ImageSpace& image_space, 1472 const ImageHeader& image_header, OatDumperOptions* oat_dumper_options) 1473 : os_(os), 1474 vios_(os), 1475 indent1_(&vios_), 1476 image_space_(image_space), 1477 image_header_(image_header), 1478 oat_dumper_options_(oat_dumper_options) {} 1479 1480 bool Dump() SHARED_REQUIRES(Locks::mutator_lock_) { 1481 std::ostream& os = *os_; 1482 std::ostream& indent_os = vios_.Stream(); 1483 1484 os << "MAGIC: " << image_header_.GetMagic() << "\n\n"; 1485 1486 os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n"; 1487 1488 os << "IMAGE SIZE: " << image_header_.GetImageSize() << "\n\n"; 1489 1490 for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) { 1491 auto section = static_cast<ImageHeader::ImageSections>(i); 1492 os << "IMAGE SECTION " << section << ": " << image_header_.GetImageSection(section) << "\n\n"; 1493 } 1494 1495 os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum()); 1496 1497 os << "OAT FILE BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatFileBegin()) << "\n\n"; 1498 1499 os << "OAT DATA BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatDataBegin()) << "\n\n"; 1500 1501 os << "OAT DATA END:" << reinterpret_cast<void*>(image_header_.GetOatDataEnd()) << "\n\n"; 1502 1503 os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n"; 1504 1505 os << "PATCH DELTA:" << image_header_.GetPatchDelta() << "\n\n"; 1506 1507 os << "COMPILE PIC: " << (image_header_.CompilePic() ? "yes" : "no") << "\n\n"; 1508 1509 { 1510 os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n"; 1511 static_assert(arraysize(image_roots_descriptions_) == 1512 static_cast<size_t>(ImageHeader::kImageRootsMax), "sizes must match"); 1513 for (int i = 0; i < ImageHeader::kImageRootsMax; i++) { 1514 ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i); 1515 const char* image_root_description = image_roots_descriptions_[i]; 1516 mirror::Object* image_root_object = image_header_.GetImageRoot(image_root); 1517 indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object); 1518 if (image_root_object->IsObjectArray()) { 1519 mirror::ObjectArray<mirror::Object>* image_root_object_array 1520 = image_root_object->AsObjectArray<mirror::Object>(); 1521 ScopedIndentation indent2(&vios_); 1522 for (int j = 0; j < image_root_object_array->GetLength(); j++) { 1523 mirror::Object* value = image_root_object_array->Get(j); 1524 size_t run = 0; 1525 for (int32_t k = j + 1; k < image_root_object_array->GetLength(); k++) { 1526 if (value == image_root_object_array->Get(k)) { 1527 run++; 1528 } else { 1529 break; 1530 } 1531 } 1532 if (run == 0) { 1533 indent_os << StringPrintf("%d: ", j); 1534 } else { 1535 indent_os << StringPrintf("%d to %zd: ", j, j + run); 1536 j = j + run; 1537 } 1538 if (value != nullptr) { 1539 PrettyObjectValue(indent_os, value->GetClass(), value); 1540 } else { 1541 indent_os << j << ": null\n"; 1542 } 1543 } 1544 } 1545 } 1546 } 1547 1548 { 1549 os << "METHOD ROOTS\n"; 1550 static_assert(arraysize(image_methods_descriptions_) == 1551 static_cast<size_t>(ImageHeader::kImageMethodsCount), "sizes must match"); 1552 for (int i = 0; i < ImageHeader::kImageMethodsCount; i++) { 1553 auto image_root = static_cast<ImageHeader::ImageMethod>(i); 1554 const char* description = image_methods_descriptions_[i]; 1555 auto* image_method = image_header_.GetImageMethod(image_root); 1556 indent_os << StringPrintf("%s: %p\n", description, image_method); 1557 } 1558 } 1559 os << "\n"; 1560 1561 ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); 1562 std::string image_filename = image_space_.GetImageFilename(); 1563 std::string oat_location = ImageHeader::GetOatLocationFromImageLocation(image_filename); 1564 os << "OAT LOCATION: " << oat_location; 1565 os << "\n"; 1566 std::string error_msg; 1567 const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location); 1568 if (oat_file == nullptr) { 1569 oat_file = OatFile::Open(oat_location, oat_location, 1570 nullptr, nullptr, false, nullptr, 1571 &error_msg); 1572 if (oat_file == nullptr) { 1573 os << "NOT FOUND: " << error_msg << "\n"; 1574 return false; 1575 } 1576 } 1577 os << "\n"; 1578 1579 stats_.oat_file_bytes = oat_file->Size(); 1580 1581 oat_dumper_.reset(new OatDumper(*oat_file, *oat_dumper_options_)); 1582 1583 for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { 1584 CHECK(oat_dex_file != nullptr); 1585 stats_.oat_dex_file_sizes.push_back(std::make_pair(oat_dex_file->GetDexFileLocation(), 1586 oat_dex_file->FileSize())); 1587 } 1588 1589 os << "OBJECTS:\n" << std::flush; 1590 1591 // Loop through all the image spaces and dump their objects. 1592 gc::Heap* heap = Runtime::Current()->GetHeap(); 1593 const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); 1594 Thread* self = Thread::Current(); 1595 { 1596 { 1597 WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); 1598 heap->FlushAllocStack(); 1599 } 1600 // Since FlushAllocStack() above resets the (active) allocation 1601 // stack. Need to revoke the thread-local allocation stacks that 1602 // point into it. 1603 { 1604 self->TransitionFromRunnableToSuspended(kNative); 1605 ThreadList* thread_list = Runtime::Current()->GetThreadList(); 1606 thread_list->SuspendAll(__FUNCTION__); 1607 heap->RevokeAllThreadLocalAllocationStacks(self); 1608 thread_list->ResumeAll(); 1609 self->TransitionFromSuspendedToRunnable(); 1610 } 1611 } 1612 { 1613 // Mark dex caches. 1614 dex_cache_arrays_.clear(); 1615 { 1616 ReaderMutexLock mu(self, *class_linker->DexLock()); 1617 for (size_t i = 0; i < class_linker->GetDexCacheCount(); ++i) { 1618 auto* dex_cache = class_linker->GetDexCache(i); 1619 dex_cache_arrays_.insert(dex_cache->GetResolvedFields()); 1620 dex_cache_arrays_.insert(dex_cache->GetResolvedMethods()); 1621 } 1622 } 1623 ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); 1624 for (const auto& space : spaces) { 1625 if (space->IsImageSpace()) { 1626 auto* image_space = space->AsImageSpace(); 1627 // Dump the normal objects before ArtMethods. 1628 image_space->GetLiveBitmap()->Walk(ImageDumper::Callback, this); 1629 indent_os << "\n"; 1630 // TODO: Dump fields. 1631 // Dump methods after. 1632 const auto& methods_section = image_header_.GetMethodsSection(); 1633 const size_t pointer_size = 1634 InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet()); 1635 DumpArtMethodVisitor visitor(this); 1636 methods_section.VisitPackedArtMethods(&visitor, image_space->Begin(), pointer_size); 1637 } 1638 } 1639 // Dump the large objects separately. 1640 heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(ImageDumper::Callback, this); 1641 indent_os << "\n"; 1642 } 1643 os << "STATS:\n" << std::flush; 1644 std::unique_ptr<File> file(OS::OpenFileForReading(image_filename.c_str())); 1645 if (file.get() == nullptr) { 1646 LOG(WARNING) << "Failed to find image in " << image_filename; 1647 } 1648 if (file.get() != nullptr) { 1649 stats_.file_bytes = file->GetLength(); 1650 } 1651 size_t header_bytes = sizeof(ImageHeader); 1652 const auto& bitmap_section = image_header_.GetImageSection(ImageHeader::kSectionImageBitmap); 1653 const auto& field_section = image_header_.GetImageSection(ImageHeader::kSectionArtFields); 1654 const auto& method_section = image_header_.GetMethodsSection(); 1655 const auto& intern_section = image_header_.GetImageSection( 1656 ImageHeader::kSectionInternedStrings); 1657 stats_.header_bytes = header_bytes; 1658 stats_.alignment_bytes += RoundUp(header_bytes, kObjectAlignment) - header_bytes; 1659 // Add padding between the field and method section. 1660 // (Field section is 4-byte aligned, method section is 8-byte aligned on 64-bit targets.) 1661 stats_.alignment_bytes += 1662 method_section.Offset() - (field_section.Offset() + field_section.Size()); 1663 // Add padding between the method section and the intern table. 1664 // (Method section is 4-byte aligned on 32-bit targets, intern table is 8-byte aligned.) 1665 stats_.alignment_bytes += 1666 intern_section.Offset() - (method_section.Offset() + method_section.Size()); 1667 stats_.alignment_bytes += bitmap_section.Offset() - image_header_.GetImageSize(); 1668 stats_.bitmap_bytes += bitmap_section.Size(); 1669 stats_.art_field_bytes += field_section.Size(); 1670 stats_.art_method_bytes += method_section.Size(); 1671 stats_.interned_strings_bytes += intern_section.Size(); 1672 stats_.Dump(os, indent_os); 1673 os << "\n"; 1674 1675 os << std::flush; 1676 1677 return oat_dumper_->Dump(os); 1678 } 1679 1680 private: 1681 class DumpArtMethodVisitor : public ArtMethodVisitor { 1682 public: 1683 explicit DumpArtMethodVisitor(ImageDumper* image_dumper) : image_dumper_(image_dumper) {} 1684 1685 virtual void Visit(ArtMethod* method) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { 1686 std::ostream& indent_os = image_dumper_->vios_.Stream(); 1687 indent_os << method << " " << " ArtMethod: " << PrettyMethod(method) << "\n"; 1688 image_dumper_->DumpMethod(method, image_dumper_, indent_os); 1689 indent_os << "\n"; 1690 } 1691 1692 private: 1693 ImageDumper* const image_dumper_; 1694 }; 1695 1696 static void PrettyObjectValue(std::ostream& os, mirror::Class* type, mirror::Object* value) 1697 SHARED_REQUIRES(Locks::mutator_lock_) { 1698 CHECK(type != nullptr); 1699 if (value == nullptr) { 1700 os << StringPrintf("null %s\n", PrettyDescriptor(type).c_str()); 1701 } else if (type->IsStringClass()) { 1702 mirror::String* string = value->AsString(); 1703 os << StringPrintf("%p String: %s\n", string, 1704 PrintableString(string->ToModifiedUtf8().c_str()).c_str()); 1705 } else if (type->IsClassClass()) { 1706 mirror::Class* klass = value->AsClass(); 1707 os << StringPrintf("%p Class: %s\n", klass, PrettyDescriptor(klass).c_str()); 1708 } else { 1709 os << StringPrintf("%p %s\n", value, PrettyDescriptor(type).c_str()); 1710 } 1711 } 1712 1713 static void PrintField(std::ostream& os, ArtField* field, mirror::Object* obj) 1714 SHARED_REQUIRES(Locks::mutator_lock_) { 1715 os << StringPrintf("%s: ", field->GetName()); 1716 switch (field->GetTypeAsPrimitiveType()) { 1717 case Primitive::kPrimLong: 1718 os << StringPrintf("%" PRId64 " (0x%" PRIx64 ")\n", field->Get64(obj), field->Get64(obj)); 1719 break; 1720 case Primitive::kPrimDouble: 1721 os << StringPrintf("%f (%a)\n", field->GetDouble(obj), field->GetDouble(obj)); 1722 break; 1723 case Primitive::kPrimFloat: 1724 os << StringPrintf("%f (%a)\n", field->GetFloat(obj), field->GetFloat(obj)); 1725 break; 1726 case Primitive::kPrimInt: 1727 os << StringPrintf("%d (0x%x)\n", field->Get32(obj), field->Get32(obj)); 1728 break; 1729 case Primitive::kPrimChar: 1730 os << StringPrintf("%u (0x%x)\n", field->GetChar(obj), field->GetChar(obj)); 1731 break; 1732 case Primitive::kPrimShort: 1733 os << StringPrintf("%d (0x%x)\n", field->GetShort(obj), field->GetShort(obj)); 1734 break; 1735 case Primitive::kPrimBoolean: 1736 os << StringPrintf("%s (0x%x)\n", field->GetBoolean(obj)? "true" : "false", 1737 field->GetBoolean(obj)); 1738 break; 1739 case Primitive::kPrimByte: 1740 os << StringPrintf("%d (0x%x)\n", field->GetByte(obj), field->GetByte(obj)); 1741 break; 1742 case Primitive::kPrimNot: { 1743 // Get the value, don't compute the type unless it is non-null as we don't want 1744 // to cause class loading. 1745 mirror::Object* value = field->GetObj(obj); 1746 if (value == nullptr) { 1747 os << StringPrintf("null %s\n", PrettyDescriptor(field->GetTypeDescriptor()).c_str()); 1748 } else { 1749 // Grab the field type without causing resolution. 1750 mirror::Class* field_type = field->GetType<false>(); 1751 if (field_type != nullptr) { 1752 PrettyObjectValue(os, field_type, value); 1753 } else { 1754 os << StringPrintf("%p %s\n", value, 1755 PrettyDescriptor(field->GetTypeDescriptor()).c_str()); 1756 } 1757 } 1758 break; 1759 } 1760 default: 1761 os << "unexpected field type: " << field->GetTypeDescriptor() << "\n"; 1762 break; 1763 } 1764 } 1765 1766 static void DumpFields(std::ostream& os, mirror::Object* obj, mirror::Class* klass) 1767 SHARED_REQUIRES(Locks::mutator_lock_) { 1768 mirror::Class* super = klass->GetSuperClass(); 1769 if (super != nullptr) { 1770 DumpFields(os, obj, super); 1771 } 1772 for (ArtField& field : klass->GetIFields()) { 1773 PrintField(os, &field, obj); 1774 } 1775 } 1776 1777 bool InDumpSpace(const mirror::Object* object) { 1778 return image_space_.Contains(object); 1779 } 1780 1781 const void* GetQuickOatCodeBegin(ArtMethod* m) 1782 SHARED_REQUIRES(Locks::mutator_lock_) { 1783 const void* quick_code = m->GetEntryPointFromQuickCompiledCodePtrSize( 1784 InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet())); 1785 if (Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(quick_code)) { 1786 quick_code = oat_dumper_->GetQuickOatCode(m); 1787 } 1788 if (oat_dumper_->GetInstructionSet() == kThumb2) { 1789 quick_code = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(quick_code) & ~0x1); 1790 } 1791 return quick_code; 1792 } 1793 1794 uint32_t GetQuickOatCodeSize(ArtMethod* m) 1795 SHARED_REQUIRES(Locks::mutator_lock_) { 1796 const uint32_t* oat_code_begin = reinterpret_cast<const uint32_t*>(GetQuickOatCodeBegin(m)); 1797 if (oat_code_begin == nullptr) { 1798 return 0; 1799 } 1800 return oat_code_begin[-1]; 1801 } 1802 1803 const void* GetQuickOatCodeEnd(ArtMethod* m) 1804 SHARED_REQUIRES(Locks::mutator_lock_) { 1805 const uint8_t* oat_code_begin = reinterpret_cast<const uint8_t*>(GetQuickOatCodeBegin(m)); 1806 if (oat_code_begin == nullptr) { 1807 return nullptr; 1808 } 1809 return oat_code_begin + GetQuickOatCodeSize(m); 1810 } 1811 1812 static void Callback(mirror::Object* obj, void* arg) SHARED_REQUIRES(Locks::mutator_lock_) { 1813 DCHECK(obj != nullptr); 1814 DCHECK(arg != nullptr); 1815 ImageDumper* state = reinterpret_cast<ImageDumper*>(arg); 1816 if (!state->InDumpSpace(obj)) { 1817 return; 1818 } 1819 1820 size_t object_bytes = obj->SizeOf(); 1821 size_t alignment_bytes = RoundUp(object_bytes, kObjectAlignment) - object_bytes; 1822 state->stats_.object_bytes += object_bytes; 1823 state->stats_.alignment_bytes += alignment_bytes; 1824 1825 std::ostream& os = state->vios_.Stream(); 1826 1827 mirror::Class* obj_class = obj->GetClass(); 1828 if (obj_class->IsArrayClass()) { 1829 os << StringPrintf("%p: %s length:%d\n", obj, PrettyDescriptor(obj_class).c_str(), 1830 obj->AsArray()->GetLength()); 1831 } else if (obj->IsClass()) { 1832 mirror::Class* klass = obj->AsClass(); 1833 os << StringPrintf("%p: java.lang.Class \"%s\" (", obj, PrettyDescriptor(klass).c_str()) 1834 << klass->GetStatus() << ")\n"; 1835 } else if (obj_class->IsStringClass()) { 1836 os << StringPrintf("%p: java.lang.String %s\n", obj, 1837 PrintableString(obj->AsString()->ToModifiedUtf8().c_str()).c_str()); 1838 } else { 1839 os << StringPrintf("%p: %s\n", obj, PrettyDescriptor(obj_class).c_str()); 1840 } 1841 ScopedIndentation indent1(&state->vios_); 1842 DumpFields(os, obj, obj_class); 1843 const auto image_pointer_size = 1844 InstructionSetPointerSize(state->oat_dumper_->GetOatInstructionSet()); 1845 if (obj->IsObjectArray()) { 1846 auto* obj_array = obj->AsObjectArray<mirror::Object>(); 1847 for (int32_t i = 0, length = obj_array->GetLength(); i < length; i++) { 1848 mirror::Object* value = obj_array->Get(i); 1849 size_t run = 0; 1850 for (int32_t j = i + 1; j < length; j++) { 1851 if (value == obj_array->Get(j)) { 1852 run++; 1853 } else { 1854 break; 1855 } 1856 } 1857 if (run == 0) { 1858 os << StringPrintf("%d: ", i); 1859 } else { 1860 os << StringPrintf("%d to %zd: ", i, i + run); 1861 i = i + run; 1862 } 1863 mirror::Class* value_class = 1864 (value == nullptr) ? obj_class->GetComponentType() : value->GetClass(); 1865 PrettyObjectValue(os, value_class, value); 1866 } 1867 } else if (obj->IsClass()) { 1868 mirror::Class* klass = obj->AsClass(); 1869 if (klass->NumStaticFields() != 0) { 1870 os << "STATICS:\n"; 1871 ScopedIndentation indent2(&state->vios_); 1872 for (ArtField& field : klass->GetSFields()) { 1873 PrintField(os, &field, field.GetDeclaringClass()); 1874 } 1875 } 1876 } else { 1877 auto it = state->dex_cache_arrays_.find(obj); 1878 if (it != state->dex_cache_arrays_.end()) { 1879 const auto& field_section = state->image_header_.GetImageSection( 1880 ImageHeader::kSectionArtFields); 1881 const auto& method_section = state->image_header_.GetMethodsSection(); 1882 auto* arr = down_cast<mirror::PointerArray*>(obj); 1883 for (int32_t i = 0, length = arr->GetLength(); i < length; i++) { 1884 void* elem = arr->GetElementPtrSize<void*>(i, image_pointer_size); 1885 size_t run = 0; 1886 for (int32_t j = i + 1; j < length && 1887 elem == arr->GetElementPtrSize<void*>(j, image_pointer_size); j++, run++) { } 1888 if (run == 0) { 1889 os << StringPrintf("%d: ", i); 1890 } else { 1891 os << StringPrintf("%d to %zd: ", i, i + run); 1892 i = i + run; 1893 } 1894 auto offset = reinterpret_cast<uint8_t*>(elem) - state->image_space_.Begin(); 1895 std::string msg; 1896 if (field_section.Contains(offset)) { 1897 msg = PrettyField(reinterpret_cast<ArtField*>(elem)); 1898 } else if (method_section.Contains(offset)) { 1899 msg = PrettyMethod(reinterpret_cast<ArtMethod*>(elem)); 1900 } else { 1901 msg = "Unknown type"; 1902 } 1903 os << StringPrintf("%p %s\n", elem, msg.c_str()); 1904 } 1905 } 1906 } 1907 std::string temp; 1908 state->stats_.Update(obj_class->GetDescriptor(&temp), object_bytes); 1909 } 1910 1911 void DumpMethod(ArtMethod* method, ImageDumper* state, std::ostream& indent_os) 1912 SHARED_REQUIRES(Locks::mutator_lock_) { 1913 DCHECK(method != nullptr); 1914 const auto image_pointer_size = 1915 InstructionSetPointerSize(state->oat_dumper_->GetOatInstructionSet()); 1916 if (method->IsNative()) { 1917 DCHECK(method->GetNativeGcMap(image_pointer_size) == nullptr) << PrettyMethod(method); 1918 DCHECK(method->GetMappingTable(image_pointer_size) == nullptr) << PrettyMethod(method); 1919 bool first_occurrence; 1920 const void* quick_oat_code = state->GetQuickOatCodeBegin(method); 1921 uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method); 1922 state->ComputeOatSize(quick_oat_code, &first_occurrence); 1923 if (first_occurrence) { 1924 state->stats_.native_to_managed_code_bytes += quick_oat_code_size; 1925 } 1926 if (quick_oat_code != method->GetEntryPointFromQuickCompiledCodePtrSize(image_pointer_size)) { 1927 indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code); 1928 } 1929 } else if (method->IsAbstract() || method->IsCalleeSaveMethod() || 1930 method->IsResolutionMethod() || method->IsImtConflictMethod() || 1931 method->IsImtUnimplementedMethod() || method->IsClassInitializer()) { 1932 DCHECK(method->GetNativeGcMap(image_pointer_size) == nullptr) << PrettyMethod(method); 1933 DCHECK(method->GetMappingTable(image_pointer_size) == nullptr) << PrettyMethod(method); 1934 } else { 1935 const DexFile::CodeItem* code_item = method->GetCodeItem(); 1936 size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2; 1937 state->stats_.dex_instruction_bytes += dex_instruction_bytes; 1938 1939 bool first_occurrence; 1940 size_t gc_map_bytes = state->ComputeOatSize( 1941 method->GetNativeGcMap(image_pointer_size), &first_occurrence); 1942 if (first_occurrence) { 1943 state->stats_.gc_map_bytes += gc_map_bytes; 1944 } 1945 1946 size_t pc_mapping_table_bytes = state->ComputeOatSize( 1947 method->GetMappingTable(image_pointer_size), &first_occurrence); 1948 if (first_occurrence) { 1949 state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes; 1950 } 1951 1952 size_t vmap_table_bytes = 0u; 1953 if (!method->IsOptimized(image_pointer_size)) { 1954 // Method compiled with the optimizing compiler have no vmap table. 1955 vmap_table_bytes = state->ComputeOatSize( 1956 method->GetVmapTable(image_pointer_size), &first_occurrence); 1957 if (first_occurrence) { 1958 state->stats_.vmap_table_bytes += vmap_table_bytes; 1959 } 1960 } 1961 1962 const void* quick_oat_code_begin = state->GetQuickOatCodeBegin(method); 1963 const void* quick_oat_code_end = state->GetQuickOatCodeEnd(method); 1964 uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method); 1965 state->ComputeOatSize(quick_oat_code_begin, &first_occurrence); 1966 if (first_occurrence) { 1967 state->stats_.managed_code_bytes += quick_oat_code_size; 1968 if (method->IsConstructor()) { 1969 if (method->IsStatic()) { 1970 state->stats_.class_initializer_code_bytes += quick_oat_code_size; 1971 } else if (dex_instruction_bytes > kLargeConstructorDexBytes) { 1972 state->stats_.large_initializer_code_bytes += quick_oat_code_size; 1973 } 1974 } else if (dex_instruction_bytes > kLargeMethodDexBytes) { 1975 state->stats_.large_method_code_bytes += quick_oat_code_size; 1976 } 1977 } 1978 state->stats_.managed_code_bytes_ignoring_deduplication += quick_oat_code_size; 1979 1980 uint32_t method_access_flags = method->GetAccessFlags(); 1981 1982 indent_os << StringPrintf("OAT CODE: %p-%p\n", quick_oat_code_begin, quick_oat_code_end); 1983 indent_os << StringPrintf("SIZE: Dex Instructions=%zd GC=%zd Mapping=%zd AccessFlags=0x%x\n", 1984 dex_instruction_bytes, gc_map_bytes, pc_mapping_table_bytes, 1985 method_access_flags); 1986 1987 size_t total_size = dex_instruction_bytes + gc_map_bytes + pc_mapping_table_bytes + 1988 vmap_table_bytes + quick_oat_code_size + ArtMethod::Size(image_pointer_size); 1989 1990 double expansion = 1991 static_cast<double>(quick_oat_code_size) / static_cast<double>(dex_instruction_bytes); 1992 state->stats_.ComputeOutliers(total_size, expansion, method); 1993 } 1994 } 1995 1996 std::set<const void*> already_seen_; 1997 // Compute the size of the given data within the oat file and whether this is the first time 1998 // this data has been requested 1999 size_t ComputeOatSize(const void* oat_data, bool* first_occurrence) { 2000 if (already_seen_.count(oat_data) == 0) { 2001 *first_occurrence = true; 2002 already_seen_.insert(oat_data); 2003 } else { 2004 *first_occurrence = false; 2005 } 2006 return oat_dumper_->ComputeSize(oat_data); 2007 } 2008 2009 public: 2010 struct Stats { 2011 size_t oat_file_bytes; 2012 size_t file_bytes; 2013 2014 size_t header_bytes; 2015 size_t object_bytes; 2016 size_t art_field_bytes; 2017 size_t art_method_bytes; 2018 size_t interned_strings_bytes; 2019 size_t bitmap_bytes; 2020 size_t alignment_bytes; 2021 2022 size_t managed_code_bytes; 2023 size_t managed_code_bytes_ignoring_deduplication; 2024 size_t managed_to_native_code_bytes; 2025 size_t native_to_managed_code_bytes; 2026 size_t class_initializer_code_bytes; 2027 size_t large_initializer_code_bytes; 2028 size_t large_method_code_bytes; 2029 2030 size_t gc_map_bytes; 2031 size_t pc_mapping_table_bytes; 2032 size_t vmap_table_bytes; 2033 2034 size_t dex_instruction_bytes; 2035 2036 std::vector<ArtMethod*> method_outlier; 2037 std::vector<size_t> method_outlier_size; 2038 std::vector<double> method_outlier_expansion; 2039 std::vector<std::pair<std::string, size_t>> oat_dex_file_sizes; 2040 2041 Stats() 2042 : oat_file_bytes(0), 2043 file_bytes(0), 2044 header_bytes(0), 2045 object_bytes(0), 2046 art_field_bytes(0), 2047 art_method_bytes(0), 2048 interned_strings_bytes(0), 2049 bitmap_bytes(0), 2050 alignment_bytes(0), 2051 managed_code_bytes(0), 2052 managed_code_bytes_ignoring_deduplication(0), 2053 managed_to_native_code_bytes(0), 2054 native_to_managed_code_bytes(0), 2055 class_initializer_code_bytes(0), 2056 large_initializer_code_bytes(0), 2057 large_method_code_bytes(0), 2058 gc_map_bytes(0), 2059 pc_mapping_table_bytes(0), 2060 vmap_table_bytes(0), 2061 dex_instruction_bytes(0) {} 2062 2063 struct SizeAndCount { 2064 SizeAndCount(size_t bytes_in, size_t count_in) : bytes(bytes_in), count(count_in) {} 2065 size_t bytes; 2066 size_t count; 2067 }; 2068 typedef SafeMap<std::string, SizeAndCount> SizeAndCountTable; 2069 SizeAndCountTable sizes_and_counts; 2070 2071 void Update(const char* descriptor, size_t object_bytes_in) { 2072 SizeAndCountTable::iterator it = sizes_and_counts.find(descriptor); 2073 if (it != sizes_and_counts.end()) { 2074 it->second.bytes += object_bytes_in; 2075 it->second.count += 1; 2076 } else { 2077 sizes_and_counts.Put(descriptor, SizeAndCount(object_bytes_in, 1)); 2078 } 2079 } 2080 2081 double PercentOfOatBytes(size_t size) { 2082 return (static_cast<double>(size) / static_cast<double>(oat_file_bytes)) * 100; 2083 } 2084 2085 double PercentOfFileBytes(size_t size) { 2086 return (static_cast<double>(size) / static_cast<double>(file_bytes)) * 100; 2087 } 2088 2089 double PercentOfObjectBytes(size_t size) { 2090 return (static_cast<double>(size) / static_cast<double>(object_bytes)) * 100; 2091 } 2092 2093 void ComputeOutliers(size_t total_size, double expansion, ArtMethod* method) { 2094 method_outlier_size.push_back(total_size); 2095 method_outlier_expansion.push_back(expansion); 2096 method_outlier.push_back(method); 2097 } 2098 2099 void DumpOutliers(std::ostream& os) 2100 SHARED_REQUIRES(Locks::mutator_lock_) { 2101 size_t sum_of_sizes = 0; 2102 size_t sum_of_sizes_squared = 0; 2103 size_t sum_of_expansion = 0; 2104 size_t sum_of_expansion_squared = 0; 2105 size_t n = method_outlier_size.size(); 2106 for (size_t i = 0; i < n; i++) { 2107 size_t cur_size = method_outlier_size[i]; 2108 sum_of_sizes += cur_size; 2109 sum_of_sizes_squared += cur_size * cur_size; 2110 double cur_expansion = method_outlier_expansion[i]; 2111 sum_of_expansion += cur_expansion; 2112 sum_of_expansion_squared += cur_expansion * cur_expansion; 2113 } 2114 size_t size_mean = sum_of_sizes / n; 2115 size_t size_variance = (sum_of_sizes_squared - sum_of_sizes * size_mean) / (n - 1); 2116 double expansion_mean = sum_of_expansion / n; 2117 double expansion_variance = 2118 (sum_of_expansion_squared - sum_of_expansion * expansion_mean) / (n - 1); 2119 2120 // Dump methods whose size is a certain number of standard deviations from the mean 2121 size_t dumped_values = 0; 2122 size_t skipped_values = 0; 2123 for (size_t i = 100; i > 0; i--) { // i is the current number of standard deviations 2124 size_t cur_size_variance = i * i * size_variance; 2125 bool first = true; 2126 for (size_t j = 0; j < n; j++) { 2127 size_t cur_size = method_outlier_size[j]; 2128 if (cur_size > size_mean) { 2129 size_t cur_var = cur_size - size_mean; 2130 cur_var = cur_var * cur_var; 2131 if (cur_var > cur_size_variance) { 2132 if (dumped_values > 20) { 2133 if (i == 1) { 2134 skipped_values++; 2135 } else { 2136 i = 2; // jump to counting for 1 standard deviation 2137 break; 2138 } 2139 } else { 2140 if (first) { 2141 os << "\nBig methods (size > " << i << " standard deviations the norm):\n"; 2142 first = false; 2143 } 2144 os << PrettyMethod(method_outlier[j]) << " requires storage of " 2145 << PrettySize(cur_size) << "\n"; 2146 method_outlier_size[j] = 0; // don't consider this method again 2147 dumped_values++; 2148 } 2149 } 2150 } 2151 } 2152 } 2153 if (skipped_values > 0) { 2154 os << "... skipped " << skipped_values 2155 << " methods with size > 1 standard deviation from the norm\n"; 2156 } 2157 os << std::flush; 2158 2159 // Dump methods whose expansion is a certain number of standard deviations from the mean 2160 dumped_values = 0; 2161 skipped_values = 0; 2162 for (size_t i = 10; i > 0; i--) { // i is the current number of standard deviations 2163 double cur_expansion_variance = i * i * expansion_variance; 2164 bool first = true; 2165 for (size_t j = 0; j < n; j++) { 2166 double cur_expansion = method_outlier_expansion[j]; 2167 if (cur_expansion > expansion_mean) { 2168 size_t cur_var = cur_expansion - expansion_mean; 2169 cur_var = cur_var * cur_var; 2170 if (cur_var > cur_expansion_variance) { 2171 if (dumped_values > 20) { 2172 if (i == 1) { 2173 skipped_values++; 2174 } else { 2175 i = 2; // jump to counting for 1 standard deviation 2176 break; 2177 } 2178 } else { 2179 if (first) { 2180 os << "\nLarge expansion methods (size > " << i 2181 << " standard deviations the norm):\n"; 2182 first = false; 2183 } 2184 os << PrettyMethod(method_outlier[j]) << " expanded code by " 2185 << cur_expansion << "\n"; 2186 method_outlier_expansion[j] = 0.0; // don't consider this method again 2187 dumped_values++; 2188 } 2189 } 2190 } 2191 } 2192 } 2193 if (skipped_values > 0) { 2194 os << "... skipped " << skipped_values 2195 << " methods with expansion > 1 standard deviation from the norm\n"; 2196 } 2197 os << "\n" << std::flush; 2198 } 2199 2200 void Dump(std::ostream& os, std::ostream& indent_os) 2201 SHARED_REQUIRES(Locks::mutator_lock_) { 2202 { 2203 os << "art_file_bytes = " << PrettySize(file_bytes) << "\n\n" 2204 << "art_file_bytes = header_bytes + object_bytes + alignment_bytes\n"; 2205 indent_os << StringPrintf("header_bytes = %8zd (%2.0f%% of art file bytes)\n" 2206 "object_bytes = %8zd (%2.0f%% of art file bytes)\n" 2207 "art_field_bytes = %8zd (%2.0f%% of art file bytes)\n" 2208 "art_method_bytes = %8zd (%2.0f%% of art file bytes)\n" 2209 "interned_string_bytes = %8zd (%2.0f%% of art file bytes)\n" 2210 "bitmap_bytes = %8zd (%2.0f%% of art file bytes)\n" 2211 "alignment_bytes = %8zd (%2.0f%% of art file bytes)\n\n", 2212 header_bytes, PercentOfFileBytes(header_bytes), 2213 object_bytes, PercentOfFileBytes(object_bytes), 2214 art_field_bytes, PercentOfFileBytes(art_field_bytes), 2215 art_method_bytes, PercentOfFileBytes(art_method_bytes), 2216 interned_strings_bytes, 2217 PercentOfFileBytes(interned_strings_bytes), 2218 bitmap_bytes, PercentOfFileBytes(bitmap_bytes), 2219 alignment_bytes, PercentOfFileBytes(alignment_bytes)) 2220 << std::flush; 2221 CHECK_EQ(file_bytes, header_bytes + object_bytes + art_field_bytes + art_method_bytes + 2222 interned_strings_bytes + bitmap_bytes + alignment_bytes); 2223 } 2224 2225 os << "object_bytes breakdown:\n"; 2226 size_t object_bytes_total = 0; 2227 for (const auto& sizes_and_count : sizes_and_counts) { 2228 const std::string& descriptor(sizes_and_count.first); 2229 double average = static_cast<double>(sizes_and_count.second.bytes) / 2230 static_cast<double>(sizes_and_count.second.count); 2231 double percent = PercentOfObjectBytes(sizes_and_count.second.bytes); 2232 os << StringPrintf("%32s %8zd bytes %6zd instances " 2233 "(%4.0f bytes/instance) %2.0f%% of object_bytes\n", 2234 descriptor.c_str(), sizes_and_count.second.bytes, 2235 sizes_and_count.second.count, average, percent); 2236 object_bytes_total += sizes_and_count.second.bytes; 2237 } 2238 os << "\n" << std::flush; 2239 CHECK_EQ(object_bytes, object_bytes_total); 2240 2241 os << StringPrintf("oat_file_bytes = %8zd\n" 2242 "managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n" 2243 "managed_to_native_code_bytes = %8zd (%2.0f%% of oat file bytes)\n" 2244 "native_to_managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n" 2245 "class_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n" 2246 "large_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n" 2247 "large_method_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n", 2248 oat_file_bytes, 2249 managed_code_bytes, 2250 PercentOfOatBytes(managed_code_bytes), 2251 managed_to_native_code_bytes, 2252 PercentOfOatBytes(managed_to_native_code_bytes), 2253 native_to_managed_code_bytes, 2254 PercentOfOatBytes(native_to_managed_code_bytes), 2255 class_initializer_code_bytes, 2256 PercentOfOatBytes(class_initializer_code_bytes), 2257 large_initializer_code_bytes, 2258 PercentOfOatBytes(large_initializer_code_bytes), 2259 large_method_code_bytes, 2260 PercentOfOatBytes(large_method_code_bytes)) 2261 << "DexFile sizes:\n"; 2262 for (const std::pair<std::string, size_t>& oat_dex_file_size : oat_dex_file_sizes) { 2263 os << StringPrintf("%s = %zd (%2.0f%% of oat file bytes)\n", 2264 oat_dex_file_size.first.c_str(), oat_dex_file_size.second, 2265 PercentOfOatBytes(oat_dex_file_size.second)); 2266 } 2267 2268 os << "\n" << StringPrintf("gc_map_bytes = %7zd (%2.0f%% of oat file bytes)\n" 2269 "pc_mapping_table_bytes = %7zd (%2.0f%% of oat file bytes)\n" 2270 "vmap_table_bytes = %7zd (%2.0f%% of oat file bytes)\n\n", 2271 gc_map_bytes, PercentOfOatBytes(gc_map_bytes), 2272 pc_mapping_table_bytes, PercentOfOatBytes(pc_mapping_table_bytes), 2273 vmap_table_bytes, PercentOfOatBytes(vmap_table_bytes)) 2274 << std::flush; 2275 2276 os << StringPrintf("dex_instruction_bytes = %zd\n", dex_instruction_bytes) 2277 << StringPrintf("managed_code_bytes expansion = %.2f (ignoring deduplication %.2f)\n\n", 2278 static_cast<double>(managed_code_bytes) / 2279 static_cast<double>(dex_instruction_bytes), 2280 static_cast<double>(managed_code_bytes_ignoring_deduplication) / 2281 static_cast<double>(dex_instruction_bytes)) 2282 << std::flush; 2283 2284 DumpOutliers(os); 2285 } 2286 } stats_; 2287 2288 private: 2289 enum { 2290 // Number of bytes for a constructor to be considered large. Based on the 1000 basic block 2291 // threshold, we assume 2 bytes per instruction and 2 instructions per block. 2292 kLargeConstructorDexBytes = 4000, 2293 // Number of bytes for a method to be considered large. Based on the 4000 basic block 2294 // threshold, we assume 2 bytes per instruction and 2 instructions per block. 2295 kLargeMethodDexBytes = 16000 2296 }; 2297 2298 // For performance, use the *os_ directly for anything that doesn't need indentation 2299 // and prepare an indentation stream with default indentation 1. 2300 std::ostream* os_; 2301 VariableIndentationOutputStream vios_; 2302 ScopedIndentation indent1_; 2303 2304 gc::space::ImageSpace& image_space_; 2305 const ImageHeader& image_header_; 2306 std::unique_ptr<OatDumper> oat_dumper_; 2307 OatDumperOptions* oat_dumper_options_; 2308 std::set<mirror::Object*> dex_cache_arrays_; 2309 2310 DISALLOW_COPY_AND_ASSIGN(ImageDumper); 2311}; 2312 2313static int DumpImage(Runtime* runtime, const char* image_location, OatDumperOptions* options, 2314 std::ostream* os) { 2315 // Dumping the image, no explicit class loader. 2316 NullHandle<mirror::ClassLoader> null_class_loader; 2317 options->class_loader_ = &null_class_loader; 2318 2319 ScopedObjectAccess soa(Thread::Current()); 2320 gc::Heap* heap = runtime->GetHeap(); 2321 gc::space::ImageSpace* image_space = heap->GetImageSpace(); 2322 CHECK(image_space != nullptr); 2323 const ImageHeader& image_header = image_space->GetImageHeader(); 2324 if (!image_header.IsValid()) { 2325 fprintf(stderr, "Invalid image header %s\n", image_location); 2326 return EXIT_FAILURE; 2327 } 2328 2329 ImageDumper image_dumper(os, *image_space, image_header, options); 2330 2331 bool success = image_dumper.Dump(); 2332 return (success) ? EXIT_SUCCESS : EXIT_FAILURE; 2333} 2334 2335static int DumpOatWithRuntime(Runtime* runtime, OatFile* oat_file, OatDumperOptions* options, 2336 std::ostream* os) { 2337 CHECK(runtime != nullptr && oat_file != nullptr && options != nullptr); 2338 2339 Thread* self = Thread::Current(); 2340 CHECK(self != nullptr); 2341 // Need well-known-classes. 2342 WellKnownClasses::Init(self->GetJniEnv()); 2343 2344 // Need to register dex files to get a working dex cache. 2345 ScopedObjectAccess soa(self); 2346 ClassLinker* class_linker = runtime->GetClassLinker(); 2347 class_linker->RegisterOatFile(oat_file); 2348 std::vector<const DexFile*> class_path; 2349 for (const OatFile::OatDexFile* odf : oat_file->GetOatDexFiles()) { 2350 std::string error_msg; 2351 const DexFile* const dex_file = OpenDexFile(odf, &error_msg); 2352 CHECK(dex_file != nullptr) << error_msg; 2353 class_linker->RegisterDexFile(*dex_file); 2354 class_path.push_back(dex_file); 2355 } 2356 2357 // Need a class loader. 2358 // Fake that we're a compiler. 2359 jobject class_loader = class_linker->CreatePathClassLoader(self, class_path); 2360 2361 // Use the class loader while dumping. 2362 StackHandleScope<1> scope(self); 2363 Handle<mirror::ClassLoader> loader_handle = scope.NewHandle( 2364 soa.Decode<mirror::ClassLoader*>(class_loader)); 2365 options->class_loader_ = &loader_handle; 2366 2367 OatDumper oat_dumper(*oat_file, *options); 2368 bool success = oat_dumper.Dump(*os); 2369 return (success) ? EXIT_SUCCESS : EXIT_FAILURE; 2370} 2371 2372static int DumpOatWithoutRuntime(OatFile* oat_file, OatDumperOptions* options, std::ostream* os) { 2373 CHECK(oat_file != nullptr && options != nullptr); 2374 // No image = no class loader. 2375 NullHandle<mirror::ClassLoader> null_class_loader; 2376 options->class_loader_ = &null_class_loader; 2377 2378 OatDumper oat_dumper(*oat_file, *options); 2379 bool success = oat_dumper.Dump(*os); 2380 return (success) ? EXIT_SUCCESS : EXIT_FAILURE; 2381} 2382 2383static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions* options, 2384 std::ostream* os) { 2385 std::string error_msg; 2386 OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false, 2387 nullptr, &error_msg); 2388 if (oat_file == nullptr) { 2389 fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str()); 2390 return EXIT_FAILURE; 2391 } 2392 2393 if (runtime != nullptr) { 2394 return DumpOatWithRuntime(runtime, oat_file, options, os); 2395 } else { 2396 return DumpOatWithoutRuntime(oat_file, options, os); 2397 } 2398} 2399 2400static int SymbolizeOat(const char* oat_filename, std::string& output_name) { 2401 std::string error_msg; 2402 OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false, 2403 nullptr, &error_msg); 2404 if (oat_file == nullptr) { 2405 fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str()); 2406 return EXIT_FAILURE; 2407 } 2408 2409 OatSymbolizer oat_symbolizer(oat_file, output_name); 2410 if (!oat_symbolizer.Symbolize()) { 2411 fprintf(stderr, "Failed to symbolize\n"); 2412 return EXIT_FAILURE; 2413 } 2414 2415 return EXIT_SUCCESS; 2416} 2417 2418struct OatdumpArgs : public CmdlineArgs { 2419 protected: 2420 using Base = CmdlineArgs; 2421 2422 virtual ParseStatus ParseCustom(const StringPiece& option, 2423 std::string* error_msg) OVERRIDE { 2424 { 2425 ParseStatus base_parse = Base::ParseCustom(option, error_msg); 2426 if (base_parse != kParseUnknownArgument) { 2427 return base_parse; 2428 } 2429 } 2430 2431 if (option.starts_with("--oat-file=")) { 2432 oat_filename_ = option.substr(strlen("--oat-file=")).data(); 2433 } else if (option.starts_with("--image=")) { 2434 image_location_ = option.substr(strlen("--image=")).data(); 2435 } else if (option =="--dump:raw_mapping_table") { 2436 dump_raw_mapping_table_ = true; 2437 } else if (option == "--dump:raw_gc_map") { 2438 dump_raw_gc_map_ = true; 2439 } else if (option == "--no-dump:vmap") { 2440 dump_vmap_ = false; 2441 } else if (option =="--dump:code_info_stack_maps") { 2442 dump_code_info_stack_maps_ = true; 2443 } else if (option == "--no-disassemble") { 2444 disassemble_code_ = false; 2445 } else if (option.starts_with("--symbolize=")) { 2446 oat_filename_ = option.substr(strlen("--symbolize=")).data(); 2447 symbolize_ = true; 2448 } else if (option.starts_with("--class-filter=")) { 2449 class_filter_ = option.substr(strlen("--class-filter=")).data(); 2450 } else if (option.starts_with("--method-filter=")) { 2451 method_filter_ = option.substr(strlen("--method-filter=")).data(); 2452 } else if (option.starts_with("--list-classes")) { 2453 list_classes_ = true; 2454 } else if (option.starts_with("--list-methods")) { 2455 list_methods_ = true; 2456 } else if (option.starts_with("--export-dex-to=")) { 2457 export_dex_location_ = option.substr(strlen("--export-dex-to=")).data(); 2458 } else if (option.starts_with("--addr2instr=")) { 2459 if (!ParseUint(option.substr(strlen("--addr2instr=")).data(), &addr2instr_)) { 2460 *error_msg = "Address conversion failed"; 2461 return kParseError; 2462 } 2463 } else { 2464 return kParseUnknownArgument; 2465 } 2466 2467 return kParseOk; 2468 } 2469 2470 virtual ParseStatus ParseChecks(std::string* error_msg) OVERRIDE { 2471 // Infer boot image location from the image location if possible. 2472 if (boot_image_location_ == nullptr) { 2473 boot_image_location_ = image_location_; 2474 } 2475 2476 // Perform the parent checks. 2477 ParseStatus parent_checks = Base::ParseChecks(error_msg); 2478 if (parent_checks != kParseOk) { 2479 return parent_checks; 2480 } 2481 2482 // Perform our own checks. 2483 if (image_location_ == nullptr && oat_filename_ == nullptr) { 2484 *error_msg = "Either --image or --oat-file must be specified"; 2485 return kParseError; 2486 } else if (image_location_ != nullptr && oat_filename_ != nullptr) { 2487 *error_msg = "Either --image or --oat-file must be specified but not both"; 2488 return kParseError; 2489 } 2490 2491 return kParseOk; 2492 } 2493 2494 virtual std::string GetUsage() const { 2495 std::string usage; 2496 2497 usage += 2498 "Usage: oatdump [options] ...\n" 2499 " Example: oatdump --image=$ANDROID_PRODUCT_OUT/system/framework/boot.art\n" 2500 " Example: adb shell oatdump --image=/system/framework/boot.art\n" 2501 "\n" 2502 // Either oat-file or image is required. 2503 " --oat-file=<file.oat>: specifies an input oat filename.\n" 2504 " Example: --oat-file=/system/framework/boot.oat\n" 2505 "\n" 2506 " --image=<file.art>: specifies an input image location.\n" 2507 " Example: --image=/system/framework/boot.art\n" 2508 "\n"; 2509 2510 usage += Base::GetUsage(); 2511 2512 usage += // Optional. 2513 " --dump:raw_mapping_table enables dumping of the mapping table.\n" 2514 " Example: --dump:raw_mapping_table\n" 2515 "\n" 2516 " --dump:raw_gc_map enables dumping of the GC map.\n" 2517 " Example: --dump:raw_gc_map\n" 2518 "\n" 2519 " --no-dump:vmap may be used to disable vmap dumping.\n" 2520 " Example: --no-dump:vmap\n" 2521 "\n" 2522 " --dump:code_info_stack_maps enables dumping of stack maps in CodeInfo sections.\n" 2523 " Example: --dump:code_info_stack_maps\n" 2524 "\n" 2525 " --no-disassemble may be used to disable disassembly.\n" 2526 " Example: --no-disassemble\n" 2527 "\n" 2528 " --list-classes may be used to list target file classes (can be used with filters).\n" 2529 " Example: --list-classes\n" 2530 " Example: --list-classes --class-filter=com.example.foo\n" 2531 "\n" 2532 " --list-methods may be used to list target file methods (can be used with filters).\n" 2533 " Example: --list-methods\n" 2534 " Example: --list-methods --class-filter=com.example --method-filter=foo\n" 2535 "\n" 2536 " --symbolize=<file.oat>: output a copy of file.oat with elf symbols included.\n" 2537 " Example: --symbolize=/system/framework/boot.oat\n" 2538 "\n" 2539 " --class-filter=<class name>: only dumps classes that contain the filter.\n" 2540 " Example: --class-filter=com.example.foo\n" 2541 "\n" 2542 " --method-filter=<method name>: only dumps methods that contain the filter.\n" 2543 " Example: --method-filter=foo\n" 2544 "\n" 2545 " --export-dex-to=<directory>: may be used to export oat embedded dex files.\n" 2546 " Example: --export-dex-to=/data/local/tmp\n" 2547 "\n" 2548 " --addr2instr=<address>: output matching method disassembled code from relative\n" 2549 " address (e.g. PC from crash dump)\n" 2550 " Example: --addr2instr=0x00001a3b\n" 2551 "\n"; 2552 2553 return usage; 2554 } 2555 2556 public: 2557 const char* oat_filename_ = nullptr; 2558 const char* class_filter_ = ""; 2559 const char* method_filter_ = ""; 2560 const char* image_location_ = nullptr; 2561 std::string elf_filename_prefix_; 2562 bool dump_raw_mapping_table_ = false; 2563 bool dump_raw_gc_map_ = false; 2564 bool dump_vmap_ = true; 2565 bool dump_code_info_stack_maps_ = false; 2566 bool disassemble_code_ = true; 2567 bool symbolize_ = false; 2568 bool list_classes_ = false; 2569 bool list_methods_ = false; 2570 uint32_t addr2instr_ = 0; 2571 const char* export_dex_location_ = nullptr; 2572}; 2573 2574struct OatdumpMain : public CmdlineMain<OatdumpArgs> { 2575 virtual bool NeedsRuntime() OVERRIDE { 2576 CHECK(args_ != nullptr); 2577 2578 // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping. 2579 bool absolute_addresses = (args_->oat_filename_ == nullptr); 2580 2581 oat_dumper_options_ = std::unique_ptr<OatDumperOptions>(new OatDumperOptions( 2582 args_->dump_raw_mapping_table_, 2583 args_->dump_raw_gc_map_, 2584 args_->dump_vmap_, 2585 args_->dump_code_info_stack_maps_, 2586 args_->disassemble_code_, 2587 absolute_addresses, 2588 args_->class_filter_, 2589 args_->method_filter_, 2590 args_->list_classes_, 2591 args_->list_methods_, 2592 args_->export_dex_location_, 2593 args_->addr2instr_)); 2594 2595 return (args_->boot_image_location_ != nullptr || args_->image_location_ != nullptr) && 2596 !args_->symbolize_; 2597 } 2598 2599 virtual bool ExecuteWithoutRuntime() OVERRIDE { 2600 CHECK(args_ != nullptr); 2601 CHECK(args_->oat_filename_ != nullptr); 2602 2603 MemMap::Init(); 2604 2605 if (args_->symbolize_) { 2606 return SymbolizeOat(args_->oat_filename_, args_->output_name_) == EXIT_SUCCESS; 2607 } else { 2608 return DumpOat(nullptr, 2609 args_->oat_filename_, 2610 oat_dumper_options_.get(), 2611 args_->os_) == EXIT_SUCCESS; 2612 } 2613 } 2614 2615 virtual bool ExecuteWithRuntime(Runtime* runtime) { 2616 CHECK(args_ != nullptr); 2617 2618 if (args_->oat_filename_ != nullptr) { 2619 return DumpOat(runtime, 2620 args_->oat_filename_, 2621 oat_dumper_options_.get(), 2622 args_->os_) == EXIT_SUCCESS; 2623 } 2624 2625 return DumpImage(runtime, args_->image_location_, oat_dumper_options_.get(), args_->os_) 2626 == EXIT_SUCCESS; 2627 } 2628 2629 std::unique_ptr<OatDumperOptions> oat_dumper_options_; 2630}; 2631 2632} // namespace art 2633 2634int main(int argc, char** argv) { 2635 art::OatdumpMain main; 2636 return main.Main(argc, argv); 2637} 2638