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