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