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