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