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