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