xref: /aosp_15_r20/art/oatdump/oatdump.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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 #include <sys/stat.h>
20 
21 #include <algorithm>
22 #include <cstdlib>
23 #include <fstream>
24 #include <iomanip>
25 #include <iostream>
26 #include <map>
27 #include <optional>
28 #include <set>
29 #include <string>
30 #include <unordered_map>
31 #include <unordered_set>
32 #include <utility>
33 #include <vector>
34 
35 #include "android-base/logging.h"
36 #include "android-base/parseint.h"
37 #include "android-base/stringprintf.h"
38 #include "android-base/strings.h"
39 #include "arch/instruction_set.h"
40 #include "arch/instruction_set_features.h"
41 #include "art_field-inl.h"
42 #include "art_method-inl.h"
43 #include "base/array_ref.h"
44 #include "base/bit_utils_iterator.h"
45 #include "base/file_utils.h"
46 #include "base/indenter.h"
47 #include "base/os.h"
48 #include "base/safe_map.h"
49 #include "base/stats-inl.h"
50 #include "base/stl_util.h"
51 #include "base/unix_file/fd_file.h"
52 #include "class_linker-inl.h"
53 #include "class_linker.h"
54 #include "class_root-inl.h"
55 #include "cmdline.h"
56 #include "debug/debug_info.h"
57 #include "debug/elf_debug_writer.h"
58 #include "debug/method_debug_info.h"
59 #include "dex/art_dex_file_loader.h"
60 #include "dex/class_accessor-inl.h"
61 #include "dex/code_item_accessors-inl.h"
62 #include "dex/descriptors_names.h"
63 #include "dex/dex_file-inl.h"
64 #include "dex/dex_instruction-inl.h"
65 #include "dex/string_reference.h"
66 #include "dex/type_lookup_table.h"
67 #include "disassembler.h"
68 #include "elf/elf_builder.h"
69 #include "gc/accounting/space_bitmap-inl.h"
70 #include "gc/space/image_space.h"
71 #include "gc/space/large_object_space.h"
72 #include "gc/space/space-inl.h"
73 #include "imtable-inl.h"
74 #include "interpreter/unstarted_runtime.h"
75 #include "mirror/array-inl.h"
76 #include "mirror/class-inl.h"
77 #include "mirror/dex_cache-inl.h"
78 #include "mirror/object-inl.h"
79 #include "mirror/object_array-inl.h"
80 #include "oat/image-inl.h"
81 #include "oat/index_bss_mapping.h"
82 #include "oat/oat.h"
83 #include "oat/oat_file-inl.h"
84 #include "oat/oat_file_assistant.h"
85 #include "oat/oat_file_assistant_context.h"
86 #include "oat/oat_file_manager.h"
87 #include "oat/stack_map.h"
88 #include "scoped_thread_state_change-inl.h"
89 #include "stack.h"
90 #include "stream/buffered_output_stream.h"
91 #include "stream/file_output_stream.h"
92 #include "subtype_check.h"
93 #include "thread_list.h"
94 #include "vdex_file.h"
95 #include "verifier/method_verifier.h"
96 #include "verifier/verifier_deps.h"
97 #include "well_known_classes.h"
98 
99 namespace art {
100 
101 using android::base::StringPrintf;
102 
103 const char* image_methods_descriptions_[] = {
104   "kResolutionMethod",
105   "kImtConflictMethod",
106   "kImtUnimplementedMethod",
107   "kSaveAllCalleeSavesMethod",
108   "kSaveRefsOnlyMethod",
109   "kSaveRefsAndArgsMethod",
110   "kSaveEverythingMethod",
111   "kSaveEverythingMethodForClinit",
112   "kSaveEverythingMethodForSuspendCheck",
113 };
114 
115 const char* image_roots_descriptions_[] = {
116   "kDexCaches",
117   "kClassRoots",
118   "kSpecialRoots",
119 };
120 
121 // Map is so that we don't allocate multiple dex files for the same OatDexFile.
122 static std::map<const OatDexFile*, std::unique_ptr<const DexFile>> opened_dex_files;
123 
OpenDexFile(const OatDexFile * oat_dex_file,std::string * error_msg)124 const DexFile* OpenDexFile(const OatDexFile* oat_dex_file, std::string* error_msg) {
125   DCHECK(oat_dex_file != nullptr);
126   auto it = opened_dex_files.find(oat_dex_file);
127   if (it != opened_dex_files.end()) {
128     return it->second.get();
129   }
130   const DexFile* ret = oat_dex_file->OpenDexFile(error_msg).release();
131   opened_dex_files.emplace(oat_dex_file, std::unique_ptr<const DexFile>(ret));
132   return ret;
133 }
134 
135 template <typename ElfTypes>
136 class OatSymbolizer final {
137  public:
OatSymbolizer(const OatFile * oat_file,const std::string & output_name,bool no_bits)138   OatSymbolizer(const OatFile* oat_file, const std::string& output_name, bool no_bits) :
139       oat_file_(oat_file),
140       builder_(nullptr),
141       output_name_(output_name.empty() ? "symbolized.oat" : output_name),
142       no_bits_(no_bits) {
143   }
144 
Symbolize()145   bool Symbolize() {
146     const InstructionSet isa = oat_file_->GetOatHeader().GetInstructionSet();
147     std::unique_ptr<const InstructionSetFeatures> features = InstructionSetFeatures::FromBitmap(
148         isa, oat_file_->GetOatHeader().GetInstructionSetFeaturesBitmap());
149 
150     std::unique_ptr<File> elf_file(OS::CreateEmptyFile(output_name_.c_str()));
151     if (elf_file == nullptr) {
152       return false;
153     }
154     std::unique_ptr<BufferedOutputStream> output_stream =
155         std::make_unique<BufferedOutputStream>(
156             std::make_unique<FileOutputStream>(elf_file.get()));
157     builder_.reset(new ElfBuilder<ElfTypes>(isa, output_stream.get()));
158 
159     builder_->Start();
160 
161     auto* rodata = builder_->GetRoData();
162     auto* text = builder_->GetText();
163 
164     const uint8_t* rodata_begin = oat_file_->Begin();
165     const size_t rodata_size = oat_file_->GetOatHeader().GetExecutableOffset();
166     if (!no_bits_) {
167       rodata->Start();
168       rodata->WriteFully(rodata_begin, rodata_size);
169       rodata->End();
170     }
171 
172     const uint8_t* text_begin = oat_file_->Begin() + rodata_size;
173     const size_t text_size = oat_file_->End() - text_begin;
174     if (!no_bits_) {
175       text->Start();
176       text->WriteFully(text_begin, text_size);
177       text->End();
178     }
179 
180     builder_->PrepareDynamicSection(elf_file->GetPath(),
181                                     rodata_size,
182                                     text_size,
183                                     oat_file_->DataImgRelRoSize(),
184                                     oat_file_->DataImgRelRoAppImageOffset(),
185                                     oat_file_->BssSize(),
186                                     oat_file_->BssMethodsOffset(),
187                                     oat_file_->BssRootsOffset(),
188                                     oat_file_->VdexSize());
189     builder_->WriteDynamicSection();
190 
191     const OatHeader& oat_header = oat_file_->GetOatHeader();
192     #define DO_TRAMPOLINE(fn_name)                                                            \
193       if (oat_header.Get ## fn_name ## Offset() != 0) {                                       \
194         debug::MethodDebugInfo info = {};                                                     \
195         info.custom_name = #fn_name;                                                          \
196         info.isa = oat_header.GetInstructionSet();                                            \
197         info.is_code_address_text_relative = true;                                            \
198         size_t code_offset = oat_header.Get ## fn_name ## Offset();                           \
199         code_offset -= GetInstructionSetEntryPointAdjustment(oat_header.GetInstructionSet()); \
200         info.code_address = code_offset - oat_header.GetExecutableOffset();                   \
201         info.code_size = 0;  /* The symbol lasts until the next symbol. */                    \
202         method_debug_infos_.push_back(std::move(info));                                       \
203       }
204     DO_TRAMPOLINE(JniDlsymLookupTrampoline);
205     DO_TRAMPOLINE(JniDlsymLookupCriticalTrampoline);
206     DO_TRAMPOLINE(QuickGenericJniTrampoline);
207     DO_TRAMPOLINE(QuickImtConflictTrampoline);
208     DO_TRAMPOLINE(QuickResolutionTrampoline);
209     DO_TRAMPOLINE(QuickToInterpreterBridge);
210     DO_TRAMPOLINE(NterpTrampoline);
211     #undef DO_TRAMPOLINE
212 
213     Walk();
214 
215     // TODO: Try to symbolize link-time thunks?
216     // This would require disassembling all methods to find branches outside the method code.
217 
218     // TODO: Add symbols for dex bytecode in the .dex section.
219 
220     debug::DebugInfo debug_info{};
221     debug_info.compiled_methods = ArrayRef<const debug::MethodDebugInfo>(method_debug_infos_);
222 
223     debug::WriteDebugInfo(builder_.get(), debug_info);
224 
225     builder_->End();
226 
227     bool ret_value = builder_->Good();
228 
229     builder_.reset();
230     output_stream.reset();
231 
232     if (elf_file->FlushCloseOrErase() != 0) {
233       return false;
234     }
235     elf_file.reset();
236 
237     return ret_value;
238   }
239 
Walk()240   void Walk() {
241     std::vector<const OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles();
242     for (size_t i = 0; i < oat_dex_files.size(); i++) {
243       const OatDexFile* oat_dex_file = oat_dex_files[i];
244       CHECK(oat_dex_file != nullptr);
245       WalkOatDexFile(oat_dex_file);
246     }
247   }
248 
WalkOatDexFile(const OatDexFile * oat_dex_file)249   void WalkOatDexFile(const OatDexFile* oat_dex_file) {
250     std::string error_msg;
251     const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg);
252     if (dex_file == nullptr) {
253       return;
254     }
255     for (size_t class_def_index = 0;
256         class_def_index < dex_file->NumClassDefs();
257         class_def_index++) {
258       const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
259       OatClassType type = oat_class.GetType();
260       switch (type) {
261         case OatClassType::kAllCompiled:
262         case OatClassType::kSomeCompiled:
263           WalkOatClass(oat_class, *dex_file, class_def_index);
264           break;
265 
266         case OatClassType::kNoneCompiled:
267           // Ignore.
268           break;
269       }
270     }
271   }
272 
WalkOatClass(const OatFile::OatClass & oat_class,const DexFile & dex_file,uint32_t class_def_index)273   void WalkOatClass(const OatFile::OatClass& oat_class,
274                     const DexFile& dex_file,
275                     uint32_t class_def_index) {
276     ClassAccessor accessor(dex_file, class_def_index);
277     // Note: even if this is an interface or a native class, we still have to walk it, as there
278     //       might be a static initializer.
279     uint32_t class_method_idx = 0;
280     for (const ClassAccessor::Method& method : accessor.GetMethods()) {
281       WalkOatMethod(oat_class.GetOatMethod(class_method_idx++),
282                     dex_file,
283                     class_def_index,
284                     method.GetIndex(),
285                     method.GetCodeItem(),
286                     method.GetAccessFlags());
287     }
288   }
289 
WalkOatMethod(const OatFile::OatMethod & oat_method,const DexFile & dex_file,uint32_t class_def_index,uint32_t dex_method_index,const dex::CodeItem * code_item,uint32_t method_access_flags)290   void WalkOatMethod(const OatFile::OatMethod& oat_method,
291                      const DexFile& dex_file,
292                      uint32_t class_def_index,
293                      uint32_t dex_method_index,
294                      const dex::CodeItem* code_item,
295                      uint32_t method_access_flags) {
296     if ((method_access_flags & kAccAbstract) != 0) {
297       // Abstract method, no code.
298       return;
299     }
300     const OatHeader& oat_header = oat_file_->GetOatHeader();
301     const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader();
302     if (method_header == nullptr || method_header->GetCodeSize() == 0) {
303       // No code.
304       return;
305     }
306 
307     uint32_t entry_point = oat_method.GetCodeOffset() - oat_header.GetExecutableOffset();
308     // Clear Thumb2 bit.
309     const void* code_address = EntryPointToCodePointer(reinterpret_cast<void*>(entry_point));
310 
311     debug::MethodDebugInfo info = {};
312     DCHECK(info.custom_name.empty());
313     info.dex_file = &dex_file;
314     info.class_def_index = class_def_index;
315     info.dex_method_index = dex_method_index;
316     info.access_flags = method_access_flags;
317     info.code_item = code_item;
318     info.isa = oat_header.GetInstructionSet();
319     info.deduped = !seen_offsets_.insert(oat_method.GetCodeOffset()).second;
320     info.is_native_debuggable = oat_header.IsNativeDebuggable();
321     info.is_optimized = method_header->IsOptimized();
322     info.is_code_address_text_relative = true;
323     info.code_address = reinterpret_cast<uintptr_t>(code_address);
324     info.code_size = method_header->GetCodeSize();
325     info.frame_size_in_bytes = method_header->GetFrameSizeInBytes();
326     info.code_info = info.is_optimized ? method_header->GetOptimizedCodeInfoPtr() : nullptr;
327     info.cfi = ArrayRef<uint8_t>();
328     method_debug_infos_.push_back(info);
329   }
330 
331  private:
332   const OatFile* oat_file_;
333   std::unique_ptr<ElfBuilder<ElfTypes>> builder_;
334   std::vector<debug::MethodDebugInfo> method_debug_infos_;
335   std::unordered_set<uint32_t> seen_offsets_;
336   const std::string output_name_;
337   bool no_bits_;
338 };
339 
340 class OatDumperOptions {
341  public:
OatDumperOptions(bool dump_vmap,bool dump_code_info_stack_maps,bool disassemble_code,bool absolute_addresses,const char * class_filter,const char * method_filter,bool list_classes,bool list_methods,bool dump_header_only,bool dump_method_and_offset_as_json,const char * export_dex_location,const char * app_image,const char * oat_filename,const char * dex_filename,uint32_t addr2instr)342   OatDumperOptions(bool dump_vmap,
343                    bool dump_code_info_stack_maps,
344                    bool disassemble_code,
345                    bool absolute_addresses,
346                    const char* class_filter,
347                    const char* method_filter,
348                    bool list_classes,
349                    bool list_methods,
350                    bool dump_header_only,
351                    bool dump_method_and_offset_as_json,
352                    const char* export_dex_location,
353                    const char* app_image,
354                    const char* oat_filename,
355                    const char* dex_filename,
356                    uint32_t addr2instr)
357       : dump_vmap_(dump_vmap),
358         dump_code_info_stack_maps_(dump_code_info_stack_maps),
359         disassemble_code_(disassemble_code),
360         absolute_addresses_(absolute_addresses),
361         class_filter_(class_filter),
362         method_filter_(method_filter),
363         list_classes_(list_classes),
364         list_methods_(list_methods),
365         dump_header_only_(dump_header_only),
366         dump_method_and_offset_as_json(dump_method_and_offset_as_json),
367         export_dex_location_(export_dex_location),
368         app_image_(app_image),
369         oat_filename_(oat_filename != nullptr ? std::make_optional(oat_filename) : std::nullopt),
370         dex_filename_(dex_filename != nullptr ? std::make_optional(dex_filename) : std::nullopt),
371         addr2instr_(addr2instr),
372         class_loader_(nullptr) {}
373 
374   const bool dump_vmap_;
375   const bool dump_code_info_stack_maps_;
376   const bool disassemble_code_;
377   const bool absolute_addresses_;
378   const char* const class_filter_;
379   const char* const method_filter_;
380   const bool list_classes_;
381   const bool list_methods_;
382   const bool dump_header_only_;
383   const bool dump_method_and_offset_as_json;
384   const char* const export_dex_location_;
385   const char* const app_image_;
386   const std::optional<std::string> oat_filename_;
387   const std::optional<std::string> dex_filename_;
388   uint32_t addr2instr_;
389   Handle<mirror::ClassLoader>* class_loader_;
390 };
391 
392 class OatDumper {
393  public:
OatDumper(const OatFile & oat_file,const OatDumperOptions & options)394   OatDumper(const OatFile& oat_file, const OatDumperOptions& options)
395       : oat_file_(oat_file),
396         oat_dex_files_(oat_file.GetOatDexFiles()),
397         options_(options),
398         resolved_addr2instr_(0),
399         instruction_set_(oat_file_.GetOatHeader().GetInstructionSet()) {
400     CHECK(options_.class_loader_ != nullptr);
401     CHECK(options_.class_filter_ != nullptr);
402     CHECK(options_.method_filter_ != nullptr);
403 
404     std::string error_msg;
405     const uint8_t* elf_begin = oat_file.ComputeElfBegin(&error_msg);
406     DCHECK_NE(elf_begin, nullptr) << error_msg;
407     DCHECK_GE(oat_file.Begin(), elf_begin);
408     oat_offset_ = reinterpret_cast<size_t>(oat_file.Begin()) - reinterpret_cast<size_t>(elf_begin);
409 
410     disassembler_ = Disassembler::Create(
411         instruction_set_,
412         new DisassemblerOptions(options_.absolute_addresses_,
413                                 elf_begin,
414                                 oat_file.End(),
415                                 /* can_read_literals_= */ true,
416                                 Is64BitInstructionSet(instruction_set_) ?
417                                     &Thread::DumpThreadOffset<PointerSize::k64> :
418                                     &Thread::DumpThreadOffset<PointerSize::k32>));
419 
420     AddAllOffsets();
421   }
422 
~OatDumper()423   ~OatDumper() {
424     delete disassembler_;
425   }
426 
GetInstructionSet()427   InstructionSet GetInstructionSet() {
428     return instruction_set_;
429   }
430 
431   using DexFileUniqV = std::vector<std::unique_ptr<const DexFile>>;
432 
Dump(std::ostream & os)433   bool Dump(std::ostream& os) {
434     if (options_.dump_method_and_offset_as_json) {
435       return DumpMethodAndOffsetAsJson(os);
436     }
437 
438     bool success = true;
439     const OatHeader& oat_header = oat_file_.GetOatHeader();
440 
441     os << "MAGIC:\n";
442     os << oat_header.GetMagic() << "\n\n";
443 
444     os << "LOCATION:\n";
445     os << oat_file_.GetLocation() << "\n\n";
446 
447     os << "CHECKSUM:\n";
448     os << StringPrintf("0x%08x\n\n", oat_header.GetChecksum());
449 
450     os << "INSTRUCTION SET:\n";
451     os << oat_header.GetInstructionSet() << "\n\n";
452 
453     {
454       std::unique_ptr<const InstructionSetFeatures> features(
455           InstructionSetFeatures::FromBitmap(oat_header.GetInstructionSet(),
456                                              oat_header.GetInstructionSetFeaturesBitmap()));
457       os << "INSTRUCTION SET FEATURES:\n";
458       os << features->GetFeatureString() << "\n\n";
459     }
460 
461     os << "DEX FILE COUNT:\n";
462     os << oat_header.GetDexFileCount() << "\n\n";
463 
464 #define DUMP_OAT_HEADER_OFFSET(label, offset)                             \
465   os << label " OFFSET:\n";                                               \
466   os << StringPrintf("0x%08zx", AdjustOffset(oat_header.offset()));       \
467   if (oat_header.offset() != 0 && options_.absolute_addresses_) {         \
468     os << StringPrintf(" (%p)", oat_file_.Begin() + oat_header.offset()); \
469   }                                                                       \
470   os << StringPrintf("\n\n");
471 
472     DUMP_OAT_HEADER_OFFSET("EXECUTABLE", GetExecutableOffset);
473     DUMP_OAT_HEADER_OFFSET("JNI DLSYM LOOKUP TRAMPOLINE",
474                            GetJniDlsymLookupTrampolineOffset);
475     DUMP_OAT_HEADER_OFFSET("JNI DLSYM LOOKUP CRITICAL TRAMPOLINE",
476                            GetJniDlsymLookupCriticalTrampolineOffset);
477     DUMP_OAT_HEADER_OFFSET("QUICK GENERIC JNI TRAMPOLINE",
478                            GetQuickGenericJniTrampolineOffset);
479     DUMP_OAT_HEADER_OFFSET("QUICK IMT CONFLICT TRAMPOLINE",
480                            GetQuickImtConflictTrampolineOffset);
481     DUMP_OAT_HEADER_OFFSET("QUICK RESOLUTION TRAMPOLINE",
482                            GetQuickResolutionTrampolineOffset);
483     DUMP_OAT_HEADER_OFFSET("QUICK TO INTERPRETER BRIDGE",
484                            GetQuickToInterpreterBridgeOffset);
485     DUMP_OAT_HEADER_OFFSET("NTERP_TRAMPOLINE",
486                            GetNterpTrampolineOffset);
487 #undef DUMP_OAT_HEADER_OFFSET
488 
489     // Print the key-value store.
490     {
491       os << "KEY VALUE STORE:\n";
492       size_t index = 0;
493       const char* key;
494       const char* value;
495       while (oat_header.GetStoreKeyValuePairByIndex(index, &key, &value)) {
496         os << key << " = " << value << "\n";
497         index++;
498       }
499       os << "\n";
500     }
501 
502     if (options_.absolute_addresses_) {
503       os << "BEGIN:\n";
504       os << reinterpret_cast<const void*>(oat_file_.Begin()) << "\n\n";
505 
506       os << "END:\n";
507       os << reinterpret_cast<const void*>(oat_file_.End()) << "\n\n";
508     }
509 
510     os << "SIZE:\n";
511     os << oat_file_.Size() << "\n\n";
512 
513     os << std::flush;
514 
515     // If set, adjust relative address to be searched
516     if (options_.addr2instr_ != 0) {
517       resolved_addr2instr_ = options_.addr2instr_ + oat_header.GetExecutableOffset();
518       os << "SEARCH ADDRESS (executable offset + input):\n";
519       os << StringPrintf("0x%08zx\n\n", AdjustOffset(resolved_addr2instr_));
520     }
521 
522     // Dump .data.img.rel.ro entries.
523     DumpDataImgRelRoEntries(os);
524 
525     // Dump .bss summary, individual entries are dumped per dex file.
526     os << ".bss: ";
527     if (oat_file_.GetBssMethods().empty() && oat_file_.GetBssGcRoots().empty()) {
528       os << "empty.\n\n";
529     } else {
530       os << oat_file_.GetBssMethods().size() << " methods, ";
531       os << oat_file_.GetBssGcRoots().size() << " GC roots.\n\n";
532     }
533 
534     // Dumping the dex file overview is compact enough to do even if header only.
535     for (size_t i = 0; i < oat_dex_files_.size(); i++) {
536       const OatDexFile* oat_dex_file = oat_dex_files_[i];
537       CHECK(oat_dex_file != nullptr);
538       std::string error_msg;
539       const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg);
540       if (dex_file == nullptr) {
541         os << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation() << "': "
542            << error_msg;
543         continue;
544       }
545 
546       const DexLayoutSections* const layout_sections = oat_dex_file->GetDexLayoutSections();
547       if (layout_sections != nullptr) {
548         os << "Layout data\n";
549         os << *layout_sections;
550         os << "\n";
551       }
552 
553       if (!options_.dump_header_only_) {
554         DumpBssMappings(os,
555                         dex_file,
556                         oat_dex_file->GetMethodBssMapping(),
557                         oat_dex_file->GetTypeBssMapping(),
558                         oat_dex_file->GetPublicTypeBssMapping(),
559                         oat_dex_file->GetPackageTypeBssMapping(),
560                         oat_dex_file->GetStringBssMapping(),
561                         oat_dex_file->GetMethodTypeBssMapping());
562       }
563     }
564 
565     if (!options_.dump_header_only_) {
566       Runtime* const runtime = Runtime::Current();
567       ClassLinker* const linker = runtime != nullptr ? runtime->GetClassLinker() : nullptr;
568 
569       if (linker != nullptr) {
570         ArrayRef<const DexFile* const> bcp_dex_files(linker->GetBootClassPath());
571         // The guarantee that we have is that we can safely take a look the BCP DexFiles in
572         // [0..number_of_compiled_bcp_dexfiles) since the runtime may add more DexFiles after that.
573         // As a note, in the case of not having mappings or in the case of multi image we
574         // purposively leave `oat_file_.bcp_bss_info` empty.
575         CHECK_LE(oat_file_.bcp_bss_info_.size(), bcp_dex_files.size());
576         for (size_t i = 0; i < oat_file_.bcp_bss_info_.size(); i++) {
577           const DexFile* const dex_file = bcp_dex_files[i];
578           os << "Entries for BCP DexFile: " << dex_file->GetLocation() << "\n";
579           DumpBssMappings(os,
580                           dex_file,
581                           oat_file_.bcp_bss_info_[i].method_bss_mapping,
582                           oat_file_.bcp_bss_info_[i].type_bss_mapping,
583                           oat_file_.bcp_bss_info_[i].public_type_bss_mapping,
584                           oat_file_.bcp_bss_info_[i].package_type_bss_mapping,
585                           oat_file_.bcp_bss_info_[i].string_bss_mapping,
586                           oat_file_.bcp_bss_info_[i].method_type_bss_mapping);
587         }
588       } else {
589         // We don't have a runtime, just dump the offsets
590         for (size_t i = 0; i < oat_file_.bcp_bss_info_.size(); i++) {
591           os << "Offsets for BCP DexFile at index " << i << "\n";
592           DumpBssOffsets(os, "ArtMethod", oat_file_.bcp_bss_info_[i].method_bss_mapping);
593           DumpBssOffsets(os, "Class", oat_file_.bcp_bss_info_[i].type_bss_mapping);
594           DumpBssOffsets(os, "Public Class", oat_file_.bcp_bss_info_[i].public_type_bss_mapping);
595           DumpBssOffsets(os, "Package Class", oat_file_.bcp_bss_info_[i].package_type_bss_mapping);
596           DumpBssOffsets(os, "String", oat_file_.bcp_bss_info_[i].string_bss_mapping);
597           DumpBssOffsets(os, "MethodType", oat_file_.bcp_bss_info_[i].method_type_bss_mapping);
598         }
599       }
600     }
601 
602     if (!options_.dump_header_only_) {
603       VariableIndentationOutputStream vios(&os);
604       VdexFile::VdexFileHeader vdex_header = oat_file_.GetVdexFile()->GetVdexFileHeader();
605       if (vdex_header.IsValid()) {
606         std::string error_msg;
607         std::vector<const DexFile*> dex_files;
608         for (size_t i = 0; i < oat_dex_files_.size(); i++) {
609           const DexFile* dex_file = OpenDexFile(oat_dex_files_[i], &error_msg);
610           if (dex_file == nullptr) {
611             os << "Error opening dex file: " << error_msg << std::endl;
612             return false;
613           }
614           dex_files.push_back(dex_file);
615         }
616         verifier::VerifierDeps deps(dex_files, /*output_only=*/ false);
617         if (!deps.ParseStoredData(dex_files, oat_file_.GetVdexFile()->GetVerifierDepsData())) {
618           os << "Error parsing verifier dependencies." << std::endl;
619           return false;
620         }
621         deps.Dump(&vios);
622       } else {
623         os << "UNRECOGNIZED vdex file, magic "
624            << vdex_header.GetMagic()
625            << ", version "
626            << vdex_header.GetVdexVersion()
627            << "\n";
628       }
629       for (size_t i = 0; i < oat_dex_files_.size(); i++) {
630         const OatDexFile* oat_dex_file = oat_dex_files_[i];
631         CHECK(oat_dex_file != nullptr);
632         if (!DumpOatDexFile(os, *oat_dex_file)) {
633           success = false;
634         }
635       }
636     }
637 
638     if (options_.export_dex_location_) {
639       std::string error_msg;
640       std::string vdex_filename = GetVdexFilename(oat_file_.GetLocation());
641       if (!OS::FileExists(vdex_filename.c_str())) {
642         os << "File " << vdex_filename.c_str() << " does not exist\n";
643         return false;
644       }
645 
646       DexFileUniqV vdex_dex_files;
647       std::unique_ptr<const VdexFile> vdex_file = OpenVdex(vdex_filename,
648                                                            &vdex_dex_files,
649                                                            &error_msg);
650       if (vdex_file.get() == nullptr) {
651         os << "Failed to open vdex file: " << error_msg << "\n";
652         return false;
653       }
654       if (oat_dex_files_.size() != vdex_dex_files.size()) {
655         os << "Dex files number in Vdex file does not match Dex files number in Oat file: "
656            << vdex_dex_files.size() << " vs " << oat_dex_files_.size() << '\n';
657         return false;
658       }
659 
660       size_t i = 0;
661       for (const auto& vdex_dex_file : vdex_dex_files) {
662         const OatDexFile* oat_dex_file = oat_dex_files_[i];
663         CHECK(oat_dex_file != nullptr);
664         CHECK(vdex_dex_file != nullptr);
665 
666         if (!vdex_dex_file->IsDexContainerFirstEntry()) {
667           // All the data was already exported together with the primary dex file.
668           continue;
669         }
670 
671         if (!ExportDexFile(os, *oat_dex_file, vdex_dex_file.get(), /*used_dexlayout=*/ false)) {
672           success = false;
673           break;
674         }
675         i++;
676       }
677     }
678 
679     {
680       os << "OAT FILE STATS:\n";
681       VariableIndentationOutputStream vios(&os);
682       stats_.AddBytes(oat_file_.Size());
683       stats_.DumpSizes(vios, "OatFile");
684     }
685 
686     os << std::flush;
687     return success;
688   }
689 
DumpMethodAndOffsetAsJson(std::ostream & os)690   bool DumpMethodAndOffsetAsJson(std::ostream& os) {
691     for (const OatDexFile* oat_dex_file : oat_dex_files_) {
692       CHECK(oat_dex_file != nullptr);
693       // Create the dex file early. A lot of print-out things depend on it.
694       std::string error_msg;
695       const DexFile* const dex_file = art::OpenDexFile(oat_dex_file, &error_msg);
696       if (dex_file == nullptr) {
697         LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
698                      << "': " << error_msg;
699         return false;
700       }
701       for (ClassAccessor accessor : dex_file->GetClasses()) {
702         const char* descriptor = accessor.GetDescriptor();
703         if (DescriptorToDot(descriptor).find(options_.class_filter_) == std::string::npos) {
704           continue;
705         }
706 
707         const uint16_t class_def_index = accessor.GetClassDefIndex();
708         const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
709         uint32_t class_method_index = 0;
710 
711         // inspired by DumpOatMethod
712         for (const ClassAccessor::Method& method : accessor.GetMethods()) {
713           uint32_t code_offset = oat_class.GetOatMethod(class_method_index).GetCodeOffset();
714           class_method_index++;
715 
716           uint32_t dex_method_idx = method.GetIndex();
717           std::string method_name = dex_file->GetMethodName(dex_file->GetMethodId(dex_method_idx));
718           if (method_name.find(options_.method_filter_) == std::string::npos) {
719             continue;
720           }
721 
722           std::string pretty_method = dex_file->PrettyMethod(dex_method_idx, true);
723 
724           os << StringPrintf("{\"method\":\"%s\",\"offset\":\"0x%08zx\"}\n",
725                              pretty_method.c_str(),
726                              AdjustOffset(code_offset));
727         }
728       }
729     }
730     return true;
731   }
732 
ComputeSize(const void * oat_data)733   size_t ComputeSize(const void* oat_data) {
734     if (reinterpret_cast<const uint8_t*>(oat_data) < oat_file_.Begin() ||
735         reinterpret_cast<const uint8_t*>(oat_data) > oat_file_.End()) {
736       return 0;  // Address not in oat file
737     }
738     uintptr_t begin_offset = reinterpret_cast<uintptr_t>(oat_data) -
739                              reinterpret_cast<uintptr_t>(oat_file_.Begin());
740     auto it = offsets_.upper_bound(begin_offset);
741     CHECK(it != offsets_.end());
742     uintptr_t end_offset = *it;
743     return end_offset - begin_offset;
744   }
745 
GetOatInstructionSet()746   InstructionSet GetOatInstructionSet() {
747     return oat_file_.GetOatHeader().GetInstructionSet();
748   }
749 
GetQuickOatCode(ArtMethod * m)750   const void* GetQuickOatCode(ArtMethod* m) REQUIRES_SHARED(Locks::mutator_lock_) {
751     for (size_t i = 0; i < oat_dex_files_.size(); i++) {
752       const OatDexFile* oat_dex_file = oat_dex_files_[i];
753       CHECK(oat_dex_file != nullptr);
754       std::string error_msg;
755       const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg);
756       if (dex_file == nullptr) {
757         LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
758             << "': " << error_msg;
759       } else {
760         const char* descriptor = m->GetDeclaringClassDescriptor();
761         const dex::ClassDef* class_def =
762             OatDexFile::FindClassDef(*dex_file, descriptor, ComputeModifiedUtf8Hash(descriptor));
763         if (class_def != nullptr) {
764           uint16_t class_def_index = dex_file->GetIndexForClassDef(*class_def);
765           const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
766           uint32_t oat_method_index;
767           if (m->IsStatic() || m->IsDirect()) {
768             // Simple case where the oat method index was stashed at load time.
769             oat_method_index = m->GetMethodIndex();
770           } else {
771             // Compute the oat_method_index by search for its position in the class def.
772             ClassAccessor accessor(*dex_file, *class_def);
773             oat_method_index = accessor.NumDirectMethods();
774             bool found_virtual = false;
775             for (ClassAccessor::Method dex_method : accessor.GetVirtualMethods()) {
776               // Check method index instead of identity in case of duplicate method definitions.
777               if (dex_method.GetIndex() == m->GetDexMethodIndex()) {
778                 found_virtual = true;
779                 break;
780               }
781               ++oat_method_index;
782             }
783             CHECK(found_virtual) << "Didn't find oat method index for virtual method: "
784                                  << dex_file->PrettyMethod(m->GetDexMethodIndex());
785           }
786           return oat_class.GetOatMethod(oat_method_index).GetQuickCode();
787         }
788       }
789     }
790     return nullptr;
791   }
792 
793   // Returns nullptr and updates error_msg if the Vdex file cannot be opened, otherwise all Dex
794   // files are stored in dex_files.
OpenVdex(const std::string & vdex_filename,DexFileUniqV * dex_files,std::string * error_msg)795   std::unique_ptr<const VdexFile> OpenVdex(const std::string& vdex_filename,
796                                            /* out */ DexFileUniqV* dex_files,
797                                            /* out */ std::string* error_msg) {
798     std::unique_ptr<const File> file(OS::OpenFileForReading(vdex_filename.c_str()));
799     if (file == nullptr) {
800       *error_msg = "Could not open file " + vdex_filename + " for reading.";
801       return nullptr;
802     }
803 
804     int64_t vdex_length = file->GetLength();
805     if (vdex_length == -1) {
806       *error_msg = "Could not read the length of file " + vdex_filename;
807       return nullptr;
808     }
809 
810     MemMap mmap = MemMap::MapFile(
811         file->GetLength(),
812         PROT_READ | PROT_WRITE,
813         MAP_PRIVATE,
814         file->Fd(),
815         /* start offset= */ 0,
816         /* low_4gb= */ false,
817         vdex_filename.c_str(),
818         error_msg);
819     if (!mmap.IsValid()) {
820       *error_msg = "Failed to mmap file " + vdex_filename + ": " + *error_msg;
821       return nullptr;
822     }
823 
824     std::unique_ptr<VdexFile> vdex_file(new VdexFile(std::move(mmap)));
825     if (!vdex_file->IsValid()) {
826       *error_msg = "Vdex file is not valid";
827       return nullptr;
828     }
829 
830     DexFileUniqV tmp_dex_files;
831     if (!vdex_file->OpenAllDexFiles(&tmp_dex_files, error_msg)) {
832       *error_msg = "Failed to open Dex files from Vdex: " + *error_msg;
833       return nullptr;
834     }
835 
836     *dex_files = std::move(tmp_dex_files);
837     return vdex_file;
838   }
839 
AddStatsObject(const void * address)840   bool AddStatsObject(const void* address) {
841     return seen_stats_objects_.insert(address).second;  // Inserted new entry.
842   }
843 
844  private:
AddAllOffsets()845   void AddAllOffsets() {
846     // We don't know the length of the code for each method, but we need to know where to stop
847     // when disassembling. What we do know is that a region of code will be followed by some other
848     // region, so if we keep a sorted sequence of the start of each region, we can infer the length
849     // of a piece of code by using upper_bound to find the start of the next region.
850     for (size_t i = 0; i < oat_dex_files_.size(); i++) {
851       const OatDexFile* oat_dex_file = oat_dex_files_[i];
852       CHECK(oat_dex_file != nullptr);
853       std::string error_msg;
854       const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg);
855       if (dex_file == nullptr) {
856         LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
857             << "': " << error_msg;
858         continue;
859       }
860       offsets_.insert(reinterpret_cast<uintptr_t>(&dex_file->GetHeader()));
861       for (ClassAccessor accessor : dex_file->GetClasses()) {
862         const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(accessor.GetClassDefIndex());
863         for (uint32_t class_method_index = 0;
864             class_method_index < accessor.NumMethods();
865             ++class_method_index) {
866           AddOffsets(oat_class.GetOatMethod(class_method_index));
867         }
868       }
869     }
870 
871     // If the last thing in the file is code for a method, there won't be an offset for the "next"
872     // thing. Instead of having a special case in the upper_bound code, let's just add an entry
873     // for the end of the file.
874     offsets_.insert(oat_file_.Size());
875   }
876 
AlignCodeOffset(uint32_t maybe_thumb_offset)877   static uint32_t AlignCodeOffset(uint32_t maybe_thumb_offset) {
878     return maybe_thumb_offset & ~0x1;  // TODO: Make this Thumb2 specific.
879   }
880 
AddOffsets(const OatFile::OatMethod & oat_method)881   void AddOffsets(const OatFile::OatMethod& oat_method) {
882     uint32_t code_offset = oat_method.GetCodeOffset();
883     if (oat_file_.GetOatHeader().GetInstructionSet() == InstructionSet::kThumb2) {
884       code_offset &= ~0x1;
885     }
886     offsets_.insert(code_offset);
887     offsets_.insert(oat_method.GetVmapTableOffset());
888   }
889 
DumpOatDexFile(std::ostream & os,const OatDexFile & oat_dex_file)890   bool DumpOatDexFile(std::ostream& os, const OatDexFile& oat_dex_file) {
891     bool success = true;
892     bool stop_analysis = false;
893     os << "OatDexFile:\n";
894     os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str());
895     os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum());
896 
897     if (oat_dex_file.GetOatFile()->ContainsDexCode()) {
898       const uint8_t* const vdex_file_begin = oat_dex_file.GetOatFile()->DexBegin();
899 
900       // Print data range of the dex file embedded inside the corresponding vdex file.
901       const uint8_t* const dex_file_pointer = oat_dex_file.GetDexFilePointer();
902       uint32_t dex_offset = dchecked_integral_cast<uint32_t>(dex_file_pointer - vdex_file_begin);
903       os << StringPrintf(
904           "dex-file: 0x%08x..0x%08x\n",
905           dex_offset,
906           dchecked_integral_cast<uint32_t>(dex_offset + oat_dex_file.FileSize() - 1));
907     } else {
908       os << StringPrintf("dex-file not in VDEX file\n");
909     }
910 
911     // Create the dex file early. A lot of print-out things depend on it.
912     std::string error_msg;
913     const DexFile* const dex_file = OpenDexFile(&oat_dex_file, &error_msg);
914     if (dex_file == nullptr) {
915       os << "NOT FOUND: " << error_msg << "\n\n";
916       os << std::flush;
917       return false;
918     }
919 
920     // Print lookup table, if it exists.
921     if (oat_dex_file.GetLookupTableData() != nullptr) {
922       uint32_t table_offset = dchecked_integral_cast<uint32_t>(
923           oat_dex_file.GetLookupTableData() - oat_dex_file.GetOatFile()->DexBegin());
924       uint32_t table_size = TypeLookupTable::RawDataLength(dex_file->NumClassDefs());
925       os << StringPrintf("type-table: 0x%08x..0x%08x\n",
926                          table_offset,
927                          table_offset + table_size - 1);
928       const TypeLookupTable& lookup = oat_dex_file.GetTypeLookupTable();
929       lookup.Dump(os);
930     }
931 
932     VariableIndentationOutputStream vios(&os);
933     ScopedIndentation indent1(&vios);
934     for (ClassAccessor accessor : dex_file->GetClasses()) {
935       // TODO: Support regex
936       const char* descriptor = accessor.GetDescriptor();
937       if (DescriptorToDot(descriptor).find(options_.class_filter_) == std::string::npos) {
938         continue;
939       }
940 
941       const uint16_t class_def_index = accessor.GetClassDefIndex();
942       uint32_t oat_class_offset = oat_dex_file.GetOatClassOffset(class_def_index);
943       const OatFile::OatClass oat_class = oat_dex_file.GetOatClass(class_def_index);
944       os << StringPrintf("%zd: %s (offset=0x%08zx) (type_idx=%d)",
945                          static_cast<ssize_t>(class_def_index),
946                          descriptor,
947                          AdjustOffset(oat_class_offset),
948                          accessor.GetClassIdx().index_)
949          << " (" << oat_class.GetStatus() << ")" << " (" << oat_class.GetType() << ")\n";
950       // TODO: include bitmap here if type is kOatClassSomeCompiled?
951       if (options_.list_classes_) {
952         continue;
953       }
954       if (!DumpOatClass(&vios, oat_class, *dex_file, accessor, &stop_analysis)) {
955         success = false;
956       }
957       if (stop_analysis) {
958         os << std::flush;
959         return success;
960       }
961     }
962     os << "\n";
963     os << std::flush;
964     return success;
965   }
966 
967   // Backwards compatible Dex file export. If dex_file is nullptr (valid Vdex file not present) the
968   // Dex resource is extracted from the oat_dex_file and its checksum is repaired since it's not
969   // unquickened. Otherwise the dex_file has been fully unquickened and is expected to verify the
970   // original checksum.
ExportDexFile(std::ostream & os,const OatDexFile & oat_dex_file,const DexFile * dex_file,bool used_dexlayout)971   bool ExportDexFile(std::ostream& os,
972                      const OatDexFile& oat_dex_file,
973                      const DexFile* dex_file,
974                      bool used_dexlayout) {
975     std::string error_msg;
976     std::string dex_file_location = oat_dex_file.GetDexFileLocation();
977 
978     // If dex_file (from unquicken or dexlayout) is not available, the output DexFile size is the
979     // same as the one extracted from the Oat container (pre-oreo)
980     size_t fsize = dex_file == nullptr ? oat_dex_file.FileSize() : dex_file->Size();
981 
982     // Some quick checks just in case
983     if (fsize == 0 || fsize < sizeof(DexFile::Header)) {
984       os << "Invalid dex file\n";
985       return false;
986     }
987 
988     if (dex_file == nullptr) {
989       // Exported bytecode is quickened (dex-to-dex transformations present)
990       dex_file = OpenDexFile(&oat_dex_file, &error_msg);
991       if (dex_file == nullptr) {
992         os << "Failed to open dex file '" << dex_file_location << "': " << error_msg;
993         return false;
994       }
995 
996       // Recompute checksum
997       reinterpret_cast<DexFile::Header*>(const_cast<uint8_t*>(dex_file->Begin()))->checksum_ =
998           dex_file->CalculateChecksum();
999     } else {
1000       // If dexlayout was used to convert CompactDex back to StandardDex, checksum will be updated
1001       // due to `update_checksum_` option, otherwise we expect a reproducible checksum.
1002       if (!used_dexlayout) {
1003         // Vdex unquicken output should match original input bytecode
1004         uint32_t orig_checksum =
1005             reinterpret_cast<DexFile::Header*>(const_cast<uint8_t*>(dex_file->Begin()))->checksum_;
1006         if (orig_checksum != dex_file->CalculateChecksum()) {
1007           os << "Unexpected checksum from unquicken dex file '" << dex_file_location << "'\n";
1008           return false;
1009         }
1010       }
1011       // Extend the data range to export all the dex files in the container.
1012       CHECK(dex_file->IsDexContainerFirstEntry()) << dex_file_location;
1013       fsize = dex_file->GetHeader().ContainerSize();
1014     }
1015 
1016     // Verify output directory exists
1017     if (!OS::DirectoryExists(options_.export_dex_location_)) {
1018       // TODO: Extend OS::DirectoryExists if symlink support is required
1019       os << options_.export_dex_location_ << " output directory not found or symlink\n";
1020       return false;
1021     }
1022 
1023     // Beautify path names
1024     if (dex_file_location.size() > PATH_MAX || dex_file_location.size() <= 0) {
1025       return false;
1026     }
1027 
1028     std::string dex_orig_name;
1029     size_t dex_orig_pos = dex_file_location.rfind('/');
1030     if (dex_orig_pos == std::string::npos)
1031       dex_orig_name = dex_file_location;
1032     else
1033       dex_orig_name = dex_file_location.substr(dex_orig_pos + 1);
1034 
1035     // A more elegant approach to efficiently name user installed apps is welcome
1036     if (dex_orig_name.size() == 8 &&
1037         dex_orig_name.compare("base.apk") == 0 &&
1038         dex_orig_pos != std::string::npos) {
1039       dex_file_location.erase(dex_orig_pos, strlen("base.apk") + 1);
1040       size_t apk_orig_pos = dex_file_location.rfind('/');
1041       if (apk_orig_pos != std::string::npos) {
1042         dex_orig_name = dex_file_location.substr(++apk_orig_pos);
1043       }
1044     }
1045 
1046     std::string out_dex_path(options_.export_dex_location_);
1047     if (out_dex_path.back() != '/') {
1048       out_dex_path.append("/");
1049     }
1050     out_dex_path.append(dex_orig_name);
1051     out_dex_path.append("_export.dex");
1052     if (out_dex_path.length() > PATH_MAX) {
1053       return false;
1054     }
1055 
1056     std::unique_ptr<File> file(OS::CreateEmptyFile(out_dex_path.c_str()));
1057     if (file.get() == nullptr) {
1058       os << "Failed to open output dex file " << out_dex_path;
1059       return false;
1060     }
1061 
1062     bool success = file->WriteFully(dex_file->Begin(), fsize);
1063     if (!success) {
1064       os << "Failed to write dex file";
1065       file->Erase();
1066       return false;
1067     }
1068 
1069     if (file->FlushCloseOrErase() != 0) {
1070       os << "Flush and close failed";
1071       return false;
1072     }
1073 
1074     os << StringPrintf("Dex file exported at %s (%zd bytes)\n", out_dex_path.c_str(), fsize);
1075     os << std::flush;
1076 
1077     return true;
1078   }
1079 
DumpOatClass(VariableIndentationOutputStream * vios,const OatFile::OatClass & oat_class,const DexFile & dex_file,const ClassAccessor & class_accessor,bool * stop_analysis)1080   bool DumpOatClass(VariableIndentationOutputStream* vios,
1081                     const OatFile::OatClass& oat_class,
1082                     const DexFile& dex_file,
1083                     const ClassAccessor& class_accessor,
1084                     bool* stop_analysis) {
1085     bool success = true;
1086     bool addr_found = false;
1087     uint32_t class_method_index = 0;
1088     for (const ClassAccessor::Method& method : class_accessor.GetMethods()) {
1089       if (!DumpOatMethod(vios,
1090                          dex_file.GetClassDef(class_accessor.GetClassDefIndex()),
1091                          class_method_index,
1092                          oat_class,
1093                          dex_file,
1094                          method.GetIndex(),
1095                          method.GetCodeItem(),
1096                          method.GetAccessFlags(),
1097                          &addr_found)) {
1098         success = false;
1099       }
1100       if (addr_found) {
1101         *stop_analysis = true;
1102         return success;
1103       }
1104       class_method_index++;
1105     }
1106     vios->Stream() << std::flush;
1107     return success;
1108   }
1109 
1110   static constexpr uint32_t kPrologueBytes = 16;
1111 
1112   // When this was picked, the largest arm method was 55,256 bytes and arm64 was 50,412 bytes.
1113   static constexpr uint32_t kMaxCodeSize = 100 * 1000;
1114 
DumpOatMethod(VariableIndentationOutputStream * vios,const dex::ClassDef & class_def,uint32_t class_method_index,const OatFile::OatClass & oat_class,const DexFile & dex_file,uint32_t dex_method_idx,const dex::CodeItem * code_item,uint32_t method_access_flags,bool * addr_found)1115   bool DumpOatMethod(VariableIndentationOutputStream* vios,
1116                      const dex::ClassDef& class_def,
1117                      uint32_t class_method_index,
1118                      const OatFile::OatClass& oat_class,
1119                      const DexFile& dex_file,
1120                      uint32_t dex_method_idx,
1121                      const dex::CodeItem* code_item,
1122                      uint32_t method_access_flags,
1123                      bool* addr_found) {
1124     bool success = true;
1125 
1126     CodeItemDataAccessor code_item_accessor(dex_file, code_item);
1127 
1128     // TODO: Support regex
1129     std::string method_name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx));
1130     if (method_name.find(options_.method_filter_) == std::string::npos) {
1131       return success;
1132     }
1133 
1134     std::string pretty_method = dex_file.PrettyMethod(dex_method_idx, true);
1135     vios->Stream() << StringPrintf("%d: %s (dex_method_idx=%d)\n",
1136                                    class_method_index, pretty_method.c_str(),
1137                                    dex_method_idx);
1138     if (options_.list_methods_) {
1139       return success;
1140     }
1141 
1142     uint32_t oat_method_offsets_offset = oat_class.GetOatMethodOffsetsOffset(class_method_index);
1143     const OatMethodOffsets* oat_method_offsets = oat_class.GetOatMethodOffsets(class_method_index);
1144     const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
1145     uint32_t code_offset = oat_method.GetCodeOffset();
1146     uint32_t code_size = oat_method.GetQuickCodeSize();
1147     if (resolved_addr2instr_ != 0) {
1148       if (resolved_addr2instr_ > code_offset + code_size) {
1149         return success;
1150       } else {
1151         *addr_found = true;  // stop analyzing file at next iteration
1152       }
1153     }
1154 
1155     // Everything below is indented at least once.
1156     ScopedIndentation indent1(vios);
1157 
1158     {
1159       vios->Stream() << "DEX CODE:\n";
1160       ScopedIndentation indent2(vios);
1161       if (code_item_accessor.HasCodeItem()) {
1162         uint32_t max_pc = code_item_accessor.InsnsSizeInCodeUnits();
1163         for (const DexInstructionPcPair& inst : code_item_accessor) {
1164           if (inst.DexPc() + inst->SizeInCodeUnits() > max_pc) {
1165             LOG(WARNING) << "GLITCH: run-away instruction at idx=0x" << std::hex << inst.DexPc();
1166             break;
1167           }
1168           vios->Stream() << StringPrintf("0x%04x: ", inst.DexPc()) << inst->DumpHexLE(5)
1169                          << StringPrintf("\t| %s\n", inst->DumpString(&dex_file).c_str());
1170         }
1171       }
1172     }
1173 
1174     std::unique_ptr<StackHandleScope<1>> hs;
1175     std::unique_ptr<verifier::MethodVerifier> verifier;
1176     if (Runtime::Current() != nullptr) {
1177       // We need to have the handle scope stay live until after the verifier since the verifier has
1178       // a handle to the dex cache from hs.
1179       ScopedObjectAccess soa(Thread::Current());
1180       hs.reset(new StackHandleScope<1>(Thread::Current()));
1181       vios->Stream() << "VERIFIER TYPE ANALYSIS:\n";
1182       ScopedIndentation indent2(vios);
1183       DumpVerifier(vios,
1184                    soa,
1185                    hs.get(),
1186                    dex_method_idx,
1187                    &dex_file,
1188                    class_def,
1189                    code_item,
1190                    method_access_flags);
1191     }
1192     {
1193       vios->Stream() << "OatMethodOffsets ";
1194       if (options_.absolute_addresses_) {
1195         vios->Stream() << StringPrintf("%p ", oat_method_offsets);
1196       }
1197       vios->Stream() << StringPrintf("(offset=0x%08zx)\n", AdjustOffset(oat_method_offsets_offset));
1198       if (oat_method_offsets_offset > oat_file_.Size()) {
1199         vios->Stream() << StringPrintf(
1200             "WARNING: oat method offsets offset 0x%08zx is past end of file 0x%08zx.\n",
1201             AdjustOffset(oat_method_offsets_offset),
1202             AdjustOffset(oat_file_.Size()));
1203         // If we can't read OatMethodOffsets, the rest of the data is dangerous to read.
1204         vios->Stream() << std::flush;
1205         return false;
1206       }
1207 
1208       ScopedIndentation indent2(vios);
1209       vios->Stream() << StringPrintf("code_offset: 0x%08zx ", AdjustOffset(code_offset));
1210       uint32_t aligned_code_begin = AlignCodeOffset(oat_method.GetCodeOffset());
1211       if (aligned_code_begin > oat_file_.Size()) {
1212         vios->Stream() << StringPrintf(
1213             "WARNING: code offset 0x%08zx is past end of file 0x%08zx.\n",
1214             AdjustOffset(aligned_code_begin),
1215             AdjustOffset(oat_file_.Size()));
1216         success = false;
1217       }
1218       vios->Stream() << "\n";
1219     }
1220     {
1221       vios->Stream() << "OatQuickMethodHeader ";
1222       uint32_t method_header_offset = oat_method.GetOatQuickMethodHeaderOffset();
1223       const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader();
1224       if (method_header != nullptr && AddStatsObject(method_header)) {
1225         stats_["QuickMethodHeader"].AddBytes(sizeof(*method_header));
1226       }
1227       if (options_.absolute_addresses_) {
1228         vios->Stream() << StringPrintf("%p ", method_header);
1229       }
1230       vios->Stream() << StringPrintf("(offset=0x%08zx)\n", AdjustOffset(method_header_offset));
1231       if (method_header_offset > oat_file_.Size() ||
1232           sizeof(OatQuickMethodHeader) > oat_file_.Size() - method_header_offset) {
1233         vios->Stream() << StringPrintf(
1234             "WARNING: oat quick method header at offset 0x%08zx is past end of file 0x%08zx.\n",
1235             AdjustOffset(method_header_offset),
1236             AdjustOffset(oat_file_.Size()));
1237         // If we can't read the OatQuickMethodHeader, the rest of the data is dangerous to read.
1238         vios->Stream() << std::flush;
1239         return false;
1240       }
1241 
1242       ScopedIndentation indent2(vios);
1243       vios->Stream() << "vmap_table: ";
1244       if (options_.absolute_addresses_) {
1245         vios->Stream() << StringPrintf("%p ", oat_method.GetVmapTable());
1246       }
1247       uint32_t vmap_table_offset =
1248           (method_header == nullptr) ? 0 : method_header->GetCodeInfoOffset();
1249       vios->Stream() << StringPrintf("(offset=0x%08zx)\n", AdjustOffset(vmap_table_offset));
1250 
1251       size_t vmap_table_offset_limit =
1252           (method_header == nullptr) ? 0 : (method_header->GetCode() - oat_file_.Begin());
1253       if (method_header != nullptr && vmap_table_offset >= vmap_table_offset_limit) {
1254         vios->Stream() << StringPrintf(
1255             "WARNING: vmap table offset 0x%08zx is past end of file 0x%08zx. ",
1256             AdjustOffset(vmap_table_offset),
1257             AdjustOffset(vmap_table_offset_limit));
1258         success = false;
1259       } else if (options_.dump_vmap_) {
1260         DumpVmapData(vios, oat_method, code_item_accessor);
1261       }
1262     }
1263     {
1264       vios->Stream() << "QuickMethodFrameInfo\n";
1265 
1266       ScopedIndentation indent2(vios);
1267       vios->Stream()
1268           << StringPrintf("frame_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes());
1269       vios->Stream() << StringPrintf("core_spill_mask: 0x%08x ", oat_method.GetCoreSpillMask());
1270       DumpSpillMask(vios->Stream(), oat_method.GetCoreSpillMask(), false);
1271       vios->Stream() << "\n";
1272       vios->Stream() << StringPrintf("fp_spill_mask: 0x%08x ", oat_method.GetFpSpillMask());
1273       DumpSpillMask(vios->Stream(), oat_method.GetFpSpillMask(), true);
1274       vios->Stream() << "\n";
1275     }
1276     {
1277       // Based on spill masks from QuickMethodFrameInfo so placed
1278       // after it is dumped, but useful for understanding quick
1279       // code, so dumped here.
1280       ScopedIndentation indent2(vios);
1281       DumpVregLocations(vios->Stream(), oat_method, code_item_accessor);
1282     }
1283     {
1284       vios->Stream() << "CODE: ";
1285       {
1286         const void* code = oat_method.GetQuickCode();
1287         uint32_t aligned_code_begin = AlignCodeOffset(code_offset);
1288         uint32_t aligned_code_end = aligned_code_begin + code_size;
1289         if (AddStatsObject(code)) {
1290           stats_["Code"].AddBytes(code_size);
1291         }
1292 
1293         if (options_.absolute_addresses_) {
1294           vios->Stream() << StringPrintf("%p ", code);
1295         }
1296         vios->Stream() << StringPrintf("(code_offset=0x%08zx size=%u)%s\n",
1297                                        AdjustOffset(code_offset),
1298                                        code_size,
1299                                        code != nullptr ? "..." : "");
1300 
1301         ScopedIndentation indent2(vios);
1302         if (aligned_code_begin > oat_file_.Size()) {
1303           vios->Stream() << StringPrintf(
1304               "WARNING: start of code at 0x%08zx is past end of file 0x%08zx.",
1305               AdjustOffset(aligned_code_begin),
1306               AdjustOffset(oat_file_.Size()));
1307           success = false;
1308         } else if (aligned_code_end > oat_file_.Size()) {
1309           vios->Stream() << StringPrintf(
1310               "WARNING: end of code at 0x%08zx is past end of file 0x%08zx. code size is 0x%08x.\n",
1311               AdjustOffset(aligned_code_end),
1312               AdjustOffset(oat_file_.Size()),
1313               code_size);
1314           success = false;
1315           if (options_.disassemble_code_) {
1316             if (aligned_code_begin + kPrologueBytes <= oat_file_.Size()) {
1317               DumpCode(vios, oat_method, code_item_accessor, true, kPrologueBytes);
1318             }
1319           }
1320         } else if (code_size > kMaxCodeSize) {
1321           vios->Stream() << StringPrintf(
1322               "WARNING: "
1323               "code size %d is bigger than max expected threshold of %d. "
1324               "code size is 0x%08x.\n",
1325               code_size,
1326               kMaxCodeSize,
1327               code_size);
1328           success = false;
1329           if (options_.disassemble_code_) {
1330             if (aligned_code_begin + kPrologueBytes <= oat_file_.Size()) {
1331               DumpCode(vios, oat_method, code_item_accessor, true, kPrologueBytes);
1332             }
1333           }
1334         } else if (options_.disassemble_code_) {
1335           DumpCode(vios, oat_method, code_item_accessor, !success, 0);
1336         }
1337       }
1338     }
1339     vios->Stream() << std::flush;
1340     return success;
1341   }
1342 
DumpSpillMask(std::ostream & os,uint32_t spill_mask,bool is_float)1343   void DumpSpillMask(std::ostream& os, uint32_t spill_mask, bool is_float) {
1344     if (spill_mask == 0) {
1345       return;
1346     }
1347     os << "(";
1348     for (size_t i = 0; i < 32; i++) {
1349       if ((spill_mask & (1 << i)) != 0) {
1350         if (is_float) {
1351           os << "fr" << i;
1352         } else {
1353           os << "r" << i;
1354         }
1355         spill_mask ^= 1 << i;  // clear bit
1356         if (spill_mask != 0) {
1357           os << ", ";
1358         } else {
1359           break;
1360         }
1361       }
1362     }
1363     os << ")";
1364   }
1365 
1366   // Display data stored at the the vmap offset of an oat method.
DumpVmapData(VariableIndentationOutputStream * vios,const OatFile::OatMethod & oat_method,const CodeItemDataAccessor & code_item_accessor)1367   void DumpVmapData(VariableIndentationOutputStream* vios,
1368                     const OatFile::OatMethod& oat_method,
1369                     const CodeItemDataAccessor& code_item_accessor) {
1370     if (IsMethodGeneratedByOptimizingCompiler(oat_method, code_item_accessor)) {
1371       // The optimizing compiler outputs its CodeInfo data in the vmap table.
1372       const uint8_t* raw_code_info = oat_method.GetVmapTable();
1373       if (raw_code_info != nullptr) {
1374         CodeInfo code_info(raw_code_info);
1375         DCHECK(code_item_accessor.HasCodeItem());
1376         ScopedIndentation indent1(vios);
1377         DumpCodeInfo(vios, code_info, oat_method);
1378       }
1379     } else {
1380       // Otherwise, there is nothing to display.
1381     }
1382   }
1383 
1384   // Display a CodeInfo object emitted by the optimizing compiler.
DumpCodeInfo(VariableIndentationOutputStream * vios,const CodeInfo & code_info,const OatFile::OatMethod & oat_method)1385   void DumpCodeInfo(VariableIndentationOutputStream* vios,
1386                     const CodeInfo& code_info,
1387                     const OatFile::OatMethod& oat_method) {
1388     code_info.Dump(vios,
1389                    oat_method.GetCodeOffset(),
1390                    options_.dump_code_info_stack_maps_,
1391                    instruction_set_);
1392   }
1393 
GetOutVROffset(uint16_t out_num,InstructionSet isa)1394   static int GetOutVROffset(uint16_t out_num, InstructionSet isa) {
1395     // According to stack model, the first out is above the Method referernce.
1396     return static_cast<size_t>(InstructionSetPointerSize(isa)) + out_num * sizeof(uint32_t);
1397   }
1398 
GetVRegOffsetFromQuickCode(const CodeItemDataAccessor & code_item_accessor,uint32_t core_spills,uint32_t fp_spills,size_t frame_size,int reg,InstructionSet isa)1399   static uint32_t GetVRegOffsetFromQuickCode(const CodeItemDataAccessor& code_item_accessor,
1400                                              uint32_t core_spills,
1401                                              uint32_t fp_spills,
1402                                              size_t frame_size,
1403                                              int reg,
1404                                              InstructionSet isa) {
1405     PointerSize pointer_size = InstructionSetPointerSize(isa);
1406     if (kIsDebugBuild) {
1407       auto* runtime = Runtime::Current();
1408       if (runtime != nullptr) {
1409         CHECK_EQ(runtime->GetClassLinker()->GetImagePointerSize(), pointer_size);
1410       }
1411     }
1412     DCHECK_ALIGNED(frame_size, kStackAlignment);
1413     DCHECK_NE(reg, -1);
1414     int spill_size = POPCOUNT(core_spills) * GetBytesPerGprSpillLocation(isa)
1415         + POPCOUNT(fp_spills) * GetBytesPerFprSpillLocation(isa)
1416         + sizeof(uint32_t);  // Filler.
1417     int num_regs = code_item_accessor.RegistersSize() - code_item_accessor.InsSize();
1418     int temp_threshold = code_item_accessor.RegistersSize();
1419     const int max_num_special_temps = 1;
1420     if (reg == temp_threshold) {
1421       // The current method pointer corresponds to special location on stack.
1422       return 0;
1423     } else if (reg >= temp_threshold + max_num_special_temps) {
1424       /*
1425        * Special temporaries may have custom locations and the logic above deals with that.
1426        * However, non-special temporaries are placed relative to the outs.
1427        */
1428       int temps_start = code_item_accessor.OutsSize() * sizeof(uint32_t)
1429           + static_cast<size_t>(pointer_size) /* art method */;
1430       int relative_offset = (reg - (temp_threshold + max_num_special_temps)) * sizeof(uint32_t);
1431       return temps_start + relative_offset;
1432     } else if (reg < num_regs) {
1433       int locals_start = frame_size - spill_size - num_regs * sizeof(uint32_t);
1434       return locals_start + (reg * sizeof(uint32_t));
1435     } else {
1436       // Handle ins.
1437       return frame_size + ((reg - num_regs) * sizeof(uint32_t))
1438           + static_cast<size_t>(pointer_size) /* art method */;
1439     }
1440   }
1441 
DumpVregLocations(std::ostream & os,const OatFile::OatMethod & oat_method,const CodeItemDataAccessor & code_item_accessor)1442   void DumpVregLocations(std::ostream& os, const OatFile::OatMethod& oat_method,
1443                          const CodeItemDataAccessor& code_item_accessor) {
1444     if (code_item_accessor.HasCodeItem()) {
1445       size_t num_locals_ins = code_item_accessor.RegistersSize();
1446       size_t num_ins = code_item_accessor.InsSize();
1447       size_t num_locals = num_locals_ins - num_ins;
1448       size_t num_outs = code_item_accessor.OutsSize();
1449 
1450       os << "vr_stack_locations:";
1451       for (size_t reg = 0; reg <= num_locals_ins; reg++) {
1452         // For readability, delimit the different kinds of VRs.
1453         if (reg == num_locals_ins) {
1454           os << "\n\tmethod*:";
1455         } else if (reg == num_locals && num_ins > 0) {
1456           os << "\n\tins:";
1457         } else if (reg == 0 && num_locals > 0) {
1458           os << "\n\tlocals:";
1459         }
1460 
1461         uint32_t offset = GetVRegOffsetFromQuickCode(code_item_accessor,
1462                                                      oat_method.GetCoreSpillMask(),
1463                                                      oat_method.GetFpSpillMask(),
1464                                                      oat_method.GetFrameSizeInBytes(),
1465                                                      reg,
1466                                                      GetInstructionSet());
1467         os << " v" << reg << "[sp + #" << offset << "]";
1468       }
1469 
1470       for (size_t out_reg = 0; out_reg < num_outs; out_reg++) {
1471         if (out_reg == 0) {
1472           os << "\n\touts:";
1473         }
1474 
1475         uint32_t offset = GetOutVROffset(out_reg, GetInstructionSet());
1476         os << " v" << out_reg << "[sp + #" << offset << "]";
1477       }
1478 
1479       os << "\n";
1480     }
1481   }
1482 
1483   // Has `oat_method` -- corresponding to the Dex `code_item` -- been compiled by
1484   // the optimizing compiler?
IsMethodGeneratedByOptimizingCompiler(const OatFile::OatMethod & oat_method,const CodeItemDataAccessor & code_item_accessor)1485   static bool IsMethodGeneratedByOptimizingCompiler(
1486       const OatFile::OatMethod& oat_method,
1487       const CodeItemDataAccessor& code_item_accessor) {
1488     // If the native GC map is null and the Dex `code_item` is not
1489     // null, then this method has been compiled with the optimizing
1490     // compiler.
1491     return oat_method.GetQuickCode() != nullptr &&
1492            oat_method.GetVmapTable() != nullptr &&
1493            code_item_accessor.HasCodeItem();
1494   }
1495 
DumpVerifier(VariableIndentationOutputStream * vios,ScopedObjectAccess & soa,StackHandleScope<1> * hs,uint32_t dex_method_idx,const DexFile * dex_file,const dex::ClassDef & class_def,const dex::CodeItem * code_item,uint32_t method_access_flags)1496   void DumpVerifier(VariableIndentationOutputStream* vios,
1497                     ScopedObjectAccess& soa,
1498                     StackHandleScope<1>* hs,
1499                     uint32_t dex_method_idx,
1500                     const DexFile* dex_file,
1501                     const dex::ClassDef& class_def,
1502                     const dex::CodeItem* code_item,
1503                     uint32_t method_access_flags)
1504       REQUIRES_SHARED(Locks::mutator_lock_) {
1505     if ((method_access_flags & kAccNative) == 0) {
1506       Runtime* const runtime = Runtime::Current();
1507       DCHECK(options_.class_loader_ != nullptr);
1508       Handle<mirror::DexCache> dex_cache = hs->NewHandle(
1509           runtime->GetClassLinker()->RegisterDexFile(*dex_file, options_.class_loader_->Get()));
1510       CHECK(dex_cache != nullptr);
1511       ArtMethod* method = runtime->GetClassLinker()->ResolveMethodId(
1512           dex_method_idx, dex_cache, *options_.class_loader_);
1513       if (method == nullptr) {
1514         soa.Self()->ClearException();
1515         return;
1516       }
1517       verifier::MethodVerifier::VerifyMethodAndDump(
1518           soa.Self(),
1519           vios,
1520           dex_method_idx,
1521           dex_file,
1522           dex_cache,
1523           *options_.class_loader_,
1524           class_def,
1525           code_item,
1526           method_access_flags,
1527           /* api_level= */ 0);
1528     }
1529   }
1530 
DumpCode(VariableIndentationOutputStream * vios,const OatFile::OatMethod & oat_method,const CodeItemDataAccessor & code_item_accessor,bool bad_input,size_t code_size)1531   void DumpCode(VariableIndentationOutputStream* vios,
1532                 const OatFile::OatMethod& oat_method,
1533                 const CodeItemDataAccessor& code_item_accessor,
1534                 bool bad_input, size_t code_size) {
1535     const void* quick_code = oat_method.GetQuickCode();
1536 
1537     if (code_size == 0) {
1538       code_size = oat_method.GetQuickCodeSize();
1539     }
1540     if (code_size == 0 || quick_code == nullptr) {
1541       vios->Stream() << "NO CODE!\n";
1542       return;
1543     } else if (!bad_input && IsMethodGeneratedByOptimizingCompiler(oat_method,
1544                                                                    code_item_accessor)) {
1545       // The optimizing compiler outputs its CodeInfo data in the vmap table.
1546       CodeInfo code_info(oat_method.GetVmapTable());
1547       if (AddStatsObject(oat_method.GetVmapTable())) {
1548         code_info.CollectSizeStats(oat_method.GetVmapTable(), stats_["CodeInfo"]);
1549       }
1550       std::unordered_map<uint32_t, std::vector<StackMap>> stack_maps;
1551       for (const StackMap& it : code_info.GetStackMaps()) {
1552         stack_maps[it.GetNativePcOffset(instruction_set_)].push_back(it);
1553       }
1554 
1555       const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code);
1556       size_t offset = 0;
1557       while (offset < code_size) {
1558         offset += disassembler_->Dump(vios->Stream(), quick_native_pc + offset);
1559         auto it = stack_maps.find(offset);
1560         if (it != stack_maps.end()) {
1561           ScopedIndentation indent1(vios);
1562           for (StackMap stack_map : it->second) {
1563             stack_map.Dump(vios, code_info, oat_method.GetCodeOffset(), instruction_set_);
1564           }
1565           stack_maps.erase(it);
1566         }
1567       }
1568       DCHECK_EQ(stack_maps.size(), 0u);  // Check that all stack maps have been printed.
1569     } else {
1570       const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code);
1571       size_t offset = 0;
1572       while (offset < code_size) {
1573         offset += disassembler_->Dump(vios->Stream(), quick_native_pc + offset);
1574       }
1575     }
1576   }
1577 
GetBootImageLiveObjectsDataRange(gc::Heap * heap) const1578   std::pair<const uint8_t*, const uint8_t*> GetBootImageLiveObjectsDataRange(gc::Heap* heap) const
1579       REQUIRES_SHARED(Locks::mutator_lock_) {
1580     const std::vector<gc::space::ImageSpace*>& boot_image_spaces = heap->GetBootImageSpaces();
1581     const ImageHeader& main_header = boot_image_spaces[0]->GetImageHeader();
1582     ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects =
1583         ObjPtr<mirror::ObjectArray<mirror::Object>>::DownCast(
1584             main_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kBootImageLiveObjects));
1585     DCHECK(boot_image_live_objects != nullptr);
1586     DCHECK(heap->ObjectIsInBootImageSpace(boot_image_live_objects));
1587     const uint8_t* boot_image_live_objects_address =
1588         reinterpret_cast<const uint8_t*>(boot_image_live_objects.Ptr());
1589     uint32_t begin_offset = mirror::ObjectArray<mirror::Object>::OffsetOfElement(0).Uint32Value();
1590     uint32_t end_offset = mirror::ObjectArray<mirror::Object>::OffsetOfElement(
1591         boot_image_live_objects->GetLength()).Uint32Value();
1592     return std::make_pair(boot_image_live_objects_address + begin_offset,
1593                           boot_image_live_objects_address + end_offset);
1594   }
1595 
DumpDataImgRelRoEntries(std::ostream & os)1596   void DumpDataImgRelRoEntries(std::ostream& os) {
1597     os << ".data.img.rel.ro: ";
1598     if (oat_file_.GetBootImageRelocations().empty()) {
1599       os << "empty.\n\n";
1600       return;
1601     }
1602 
1603     os << oat_file_.GetBootImageRelocations().size() << " entries.\n";
1604     Runtime* runtime = Runtime::Current();
1605     if (runtime != nullptr && !runtime->GetHeap()->GetBootImageSpaces().empty()) {
1606       const std::vector<gc::space::ImageSpace*>& boot_image_spaces =
1607           runtime->GetHeap()->GetBootImageSpaces();
1608       ScopedObjectAccess soa(Thread::Current());
1609       auto live_objects = GetBootImageLiveObjectsDataRange(runtime->GetHeap());
1610       const uint8_t* live_objects_begin = live_objects.first;
1611       const uint8_t* live_objects_end = live_objects.second;
1612       for (const uint32_t& object_offset : oat_file_.GetBootImageRelocations()) {
1613         uint32_t entry_index = &object_offset - oat_file_.GetBootImageRelocations().data();
1614         uint32_t entry_offset = entry_index * sizeof(oat_file_.GetBootImageRelocations()[0]);
1615         os << StringPrintf("  0x%x: 0x%08x", entry_offset, object_offset);
1616         uint8_t* address = boot_image_spaces[0]->Begin() + object_offset;
1617         bool found = false;
1618         for (gc::space::ImageSpace* space : boot_image_spaces) {
1619           uint64_t local_offset = address - space->Begin();
1620           if (local_offset < space->GetImageHeader().GetImageSize()) {
1621             if (space->GetImageHeader().GetObjectsSection().Contains(local_offset)) {
1622               if (address >= live_objects_begin && address < live_objects_end) {
1623                 size_t index =
1624                     (address - live_objects_begin) / sizeof(mirror::HeapReference<mirror::Object>);
1625                 os << StringPrintf("   0x%08x BootImageLiveObject[%zu]",
1626                                    object_offset,
1627                                    index);
1628               } else {
1629                 ObjPtr<mirror::Object> o = reinterpret_cast<mirror::Object*>(address);
1630                 if (o->IsString()) {
1631                   os << "   String: " << o->AsString()->ToModifiedUtf8();
1632                 } else if (o->IsClass()) {
1633                   os << "   Class: " << o->AsClass()->PrettyDescriptor();
1634                 } else {
1635                   os << StringPrintf("   0x%08x %s",
1636                                      object_offset,
1637                                      o->GetClass()->PrettyDescriptor().c_str());
1638                 }
1639               }
1640             } else if (space->GetImageHeader().GetMethodsSection().Contains(local_offset)) {
1641               ArtMethod* m = reinterpret_cast<ArtMethod*>(address);
1642               os << "   ArtMethod: " << m->PrettyMethod();
1643             } else {
1644               os << StringPrintf("   0x%08x <unexpected section in %s>",
1645                                  object_offset,
1646                                  space->GetImageFilename().c_str());
1647             }
1648             found = true;
1649             break;
1650           }
1651         }
1652         if (!found) {
1653           os << StringPrintf("   0x%08x <outside boot image spaces>", object_offset);
1654         }
1655         os << "\n";
1656       }
1657     } else {
1658       for (const uint32_t& object_offset : oat_file_.GetBootImageRelocations()) {
1659         uint32_t entry_index = &object_offset - oat_file_.GetBootImageRelocations().data();
1660         uint32_t entry_offset = entry_index * sizeof(oat_file_.GetBootImageRelocations()[0]);
1661         os << StringPrintf("  0x%x: 0x%08x\n", entry_offset, object_offset);
1662       }
1663     }
1664     os << "\n";
1665   }
1666 
1667   template <typename NameGetter>
DumpBssEntries(std::ostream & os,const char * slot_type,const IndexBssMapping * mapping,uint32_t number_of_indexes,size_t slot_size,NameGetter name)1668   void DumpBssEntries(std::ostream& os,
1669                       const char* slot_type,
1670                       const IndexBssMapping* mapping,
1671                       uint32_t number_of_indexes,
1672                       size_t slot_size,
1673                       NameGetter name) {
1674     os << ".bss mapping for " << slot_type << ": ";
1675     if (mapping == nullptr) {
1676       os << "empty.\n";
1677       return;
1678     }
1679     size_t index_bits = IndexBssMappingEntry::IndexBits(number_of_indexes);
1680     size_t num_valid_indexes = 0u;
1681     for (const IndexBssMappingEntry& entry : *mapping) {
1682       num_valid_indexes += 1u + POPCOUNT(entry.GetMask(index_bits));
1683     }
1684     os << mapping->size() << " entries for " << num_valid_indexes << " valid indexes.\n";
1685     os << std::hex;
1686     for (const IndexBssMappingEntry& entry : *mapping) {
1687       uint32_t index = entry.GetIndex(index_bits);
1688       uint32_t mask = entry.GetMask(index_bits);
1689       size_t bss_offset = entry.bss_offset - POPCOUNT(mask) * slot_size;
1690       for (uint32_t n : LowToHighBits(mask)) {
1691         size_t current_index = index - (32u - index_bits) + n;
1692         os << "  0x" << bss_offset << ": " << slot_type << ": " << name(current_index) << "\n";
1693         bss_offset += slot_size;
1694       }
1695       DCHECK_EQ(bss_offset, entry.bss_offset);
1696       os << "  0x" << bss_offset << ": " << slot_type << ": " << name(index) << "\n";
1697     }
1698     os << std::dec;
1699   }
1700 
DumpBssMappings(std::ostream & os,const DexFile * dex_file,const IndexBssMapping * method_bss_mapping,const IndexBssMapping * type_bss_mapping,const IndexBssMapping * public_type_bss_mapping,const IndexBssMapping * package_type_bss_mapping,const IndexBssMapping * string_bss_mapping,const IndexBssMapping * method_type_bss_mapping)1701   void DumpBssMappings(std::ostream& os,
1702                        const DexFile* dex_file,
1703                        const IndexBssMapping* method_bss_mapping,
1704                        const IndexBssMapping* type_bss_mapping,
1705                        const IndexBssMapping* public_type_bss_mapping,
1706                        const IndexBssMapping* package_type_bss_mapping,
1707                        const IndexBssMapping* string_bss_mapping,
1708                        const IndexBssMapping* method_type_bss_mapping) {
1709     DumpBssEntries(os,
1710                    "ArtMethod",
1711                    method_bss_mapping,
1712                    dex_file->NumMethodIds(),
1713                    static_cast<size_t>(GetInstructionSetPointerSize(instruction_set_)),
1714                    [=](uint32_t index) { return dex_file->PrettyMethod(index); });
1715     DumpBssEntries(os,
1716                    "Class",
1717                    type_bss_mapping,
1718                    dex_file->NumTypeIds(),
1719                    sizeof(GcRoot<mirror::Class>),
1720                    [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
1721     DumpBssEntries(os,
1722                    "Public Class",
1723                    public_type_bss_mapping,
1724                    dex_file->NumTypeIds(),
1725                    sizeof(GcRoot<mirror::Class>),
1726                    [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
1727     DumpBssEntries(os,
1728                    "Package Class",
1729                    package_type_bss_mapping,
1730                    dex_file->NumTypeIds(),
1731                    sizeof(GcRoot<mirror::Class>),
1732                    [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
1733     DumpBssEntries(
1734         os,
1735         "String",
1736         string_bss_mapping,
1737         dex_file->NumStringIds(),
1738         sizeof(GcRoot<mirror::Class>),
1739         [=](uint32_t index) { return dex_file->GetStringData(dex::StringIndex(index)); });
1740     DumpBssEntries(os,
1741                    "MethodType",
1742                    method_type_bss_mapping,
1743                    dex_file->NumProtoIds(),
1744                    sizeof(GcRoot<mirror::MethodType>),
1745                    [=](uint32_t index) {
1746                      const dex::ProtoId& proto_id = dex_file->GetProtoId(dex::ProtoIndex(index));
1747                      return dex_file->GetProtoSignature(proto_id).ToString();
1748                    });
1749   }
1750 
DumpBssOffsets(std::ostream & os,const char * slot_type,const IndexBssMapping * mapping)1751   void DumpBssOffsets(std::ostream& os, const char* slot_type, const IndexBssMapping* mapping) {
1752     os << ".bss offset for " << slot_type << ": ";
1753     if (mapping == nullptr) {
1754       os << "empty.\n";
1755       return;
1756     }
1757 
1758     os << "Mapping size: " << mapping->size() << "\n";
1759     for (size_t i = 0; i < mapping->size(); ++i) {
1760       os << "Entry[" << i << "]: index_and_mask: "
1761          << mapping->At(i).index_and_mask
1762          << ", bss_offset: "
1763          << mapping->At(i).bss_offset << "\n";
1764     }
1765 
1766     // TODO(solanes, 154012332): We are dumping the raw values but we could make assumptions about
1767     // ordering of the entries and deconstruct even the `index_and_mask`. This would allow us to use
1768     // DumpBssEntries and dump more information. The size and alignment of the entry (ArtMethod*
1769     // depends on instruction set but Class and String references are 32-bit) and the difference
1770     // from the previous `bss_offset` (or from the "oatbss" symbol for the first item) tell us how
1771     // many .bss entries a single `IndexBssMappingEntry` should describe. So we know how many most
1772     // significant set bits represent the mask and the rest is the actual index. And the position of
1773     // the mask bits would allow reconstructing the other indexes.
1774   }
1775 
1776   // Adjusts an offset relative to the OAT file begin to an offset relative to the ELF file begin.
AdjustOffset(size_t offset) const1777   size_t AdjustOffset(size_t offset) const { return (offset > 0) ? (oat_offset_ + offset) : 0; }
1778 
1779   const OatFile& oat_file_;
1780   size_t oat_offset_;
1781   const std::vector<const OatDexFile*> oat_dex_files_;
1782   const OatDumperOptions& options_;
1783   uint32_t resolved_addr2instr_;
1784   const InstructionSet instruction_set_;
1785   std::set<uintptr_t> offsets_;
1786   Disassembler* disassembler_;
1787   Stats stats_;
1788   std::unordered_set<const void*> seen_stats_objects_;
1789 };
1790 
1791 class ImageDumper {
1792  public:
ImageDumper(std::ostream * os,gc::space::ImageSpace & image_space,const ImageHeader & image_header,OatDumperOptions * oat_dumper_options)1793   ImageDumper(std::ostream* os,
1794               gc::space::ImageSpace& image_space,
1795               const ImageHeader& image_header,
1796               OatDumperOptions* oat_dumper_options)
1797       : os_(os),
1798         vios_(os),
1799         indent1_(&vios_),
1800         image_space_(image_space),
1801         image_header_(image_header),
1802         oat_dumper_options_(oat_dumper_options) {}
1803 
Dump()1804   bool Dump() REQUIRES_SHARED(Locks::mutator_lock_) {
1805     std::ostream& os = *os_;
1806     std::ostream& indent_os = vios_.Stream();
1807 
1808     os << "MAGIC: " << image_header_.GetMagic() << "\n\n";
1809 
1810     os << "IMAGE LOCATION: " << image_space_.GetImageLocation() << "\n\n";
1811 
1812     os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n";
1813     os << "IMAGE SIZE: " << image_header_.GetImageSize() << "\n";
1814     os << "IMAGE CHECKSUM: " << std::hex << image_header_.GetImageChecksum() << std::dec << "\n\n";
1815 
1816     os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum()) << "\n";
1817     os << "OAT FILE BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatFileBegin()) << "\n";
1818     os << "OAT DATA BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatDataBegin()) << "\n";
1819     os << "OAT DATA END:" << reinterpret_cast<void*>(image_header_.GetOatDataEnd()) << "\n";
1820     os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n";
1821 
1822     os << "BOOT IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetBootImageBegin())
1823         << "\n";
1824     os << "BOOT IMAGE SIZE: " << image_header_.GetBootImageSize() << "\n\n";
1825 
1826     for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
1827       auto section = static_cast<ImageHeader::ImageSections>(i);
1828       os << "IMAGE SECTION " << section << ": " << image_header_.GetImageSection(section) << "\n\n";
1829     }
1830 
1831     {
1832       os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots().Ptr()) << "\n";
1833       static_assert(arraysize(image_roots_descriptions_) ==
1834           static_cast<size_t>(ImageHeader::kImageRootsMax), "sizes must match");
1835       DCHECK_LE(image_header_.GetImageRoots()->GetLength(), ImageHeader::kImageRootsMax);
1836       for (int32_t i = 0, size = image_header_.GetImageRoots()->GetLength(); i != size; ++i) {
1837         ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
1838         const char* image_root_description = image_roots_descriptions_[i];
1839         ObjPtr<mirror::Object> image_root_object = image_header_.GetImageRoot(image_root);
1840         indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object.Ptr());
1841         if (image_root_object != nullptr && image_root_object->IsObjectArray()) {
1842           ObjPtr<mirror::ObjectArray<mirror::Object>> image_root_object_array
1843               = image_root_object->AsObjectArray<mirror::Object>();
1844           ScopedIndentation indent2(&vios_);
1845           for (int j = 0; j < image_root_object_array->GetLength(); j++) {
1846             ObjPtr<mirror::Object> value = image_root_object_array->Get(j);
1847             size_t run = 0;
1848             for (int32_t k = j + 1; k < image_root_object_array->GetLength(); k++) {
1849               if (value == image_root_object_array->Get(k)) {
1850                 run++;
1851               } else {
1852                 break;
1853               }
1854             }
1855             if (run == 0) {
1856               indent_os << StringPrintf("%d: ", j);
1857             } else {
1858               indent_os << StringPrintf("%d to %zd: ", j, j + run);
1859               j = j + run;
1860             }
1861             if (value != nullptr) {
1862               PrettyObjectValue(indent_os, value->GetClass(), value);
1863             } else {
1864               indent_os << j << ": null\n";
1865             }
1866           }
1867         }
1868       }
1869     }
1870 
1871     {
1872       os << "METHOD ROOTS\n";
1873       static_assert(arraysize(image_methods_descriptions_) ==
1874           static_cast<size_t>(ImageHeader::kImageMethodsCount), "sizes must match");
1875       for (int i = 0; i < ImageHeader::kImageMethodsCount; i++) {
1876         auto image_root = static_cast<ImageHeader::ImageMethod>(i);
1877         const char* description = image_methods_descriptions_[i];
1878         auto* image_method = image_header_.GetImageMethod(image_root);
1879         indent_os << StringPrintf("%s: %p\n", description, image_method);
1880       }
1881     }
1882     os << "\n";
1883 
1884     Runtime* const runtime = Runtime::Current();
1885     std::string image_filename = image_space_.GetImageFilename();
1886     std::string oat_location = ImageHeader::GetOatLocationFromImageLocation(image_filename);
1887     os << "OAT LOCATION: " << oat_location;
1888     os << "\n";
1889     std::string error_msg;
1890     const OatFile* oat_file = image_space_.GetOatFile();
1891     if (oat_file == nullptr) {
1892       oat_file = runtime->GetOatFileManager().FindOpenedOatFileFromOatLocation(oat_location);
1893     }
1894     if (oat_file == nullptr) {
1895       oat_file = OatFile::Open(/*zip_fd=*/ -1,
1896                                oat_location,
1897                                oat_location,
1898                                /*executable=*/ false,
1899                                /*low_4gb=*/ false,
1900                                &error_msg);
1901     }
1902     if (oat_file == nullptr) {
1903       os << "OAT FILE NOT FOUND: " << error_msg << "\n";
1904       return EXIT_FAILURE;
1905     }
1906     os << "\n";
1907 
1908     stats_.oat_file_bytes = oat_file->Size();
1909     stats_.oat_file_stats.AddBytes(oat_file->Size());
1910 
1911     oat_dumper_.reset(new OatDumper(*oat_file, *oat_dumper_options_));
1912 
1913     for (const OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
1914       CHECK(oat_dex_file != nullptr);
1915       stats_.oat_dex_file_sizes.push_back(std::make_pair(oat_dex_file->GetDexFileLocation(),
1916                                                          oat_dex_file->FileSize()));
1917     }
1918 
1919     os << "OBJECTS:\n" << std::flush;
1920 
1921     // Loop through the image space and dump its objects.
1922     gc::Heap* heap = runtime->GetHeap();
1923     Thread* self = Thread::Current();
1924     {
1925       {
1926         WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
1927         heap->FlushAllocStack();
1928       }
1929       // Since FlushAllocStack() above resets the (active) allocation
1930       // stack. Need to revoke the thread-local allocation stacks that
1931       // point into it.
1932       ScopedThreadSuspension sts(self, ThreadState::kNative);
1933       ScopedSuspendAll ssa(__FUNCTION__);
1934       heap->RevokeAllThreadLocalAllocationStacks(self);
1935     }
1936     {
1937       auto dump_visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
1938         DumpObject(obj);
1939       };
1940       ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
1941       // Dump the normal objects before ArtMethods.
1942       image_space_.GetLiveBitmap()->Walk(dump_visitor);
1943       indent_os << "\n";
1944       // TODO: Dump fields.
1945       // Dump methods after.
1946       image_header_.VisitPackedArtMethods([&](ArtMethod& method)
1947           REQUIRES_SHARED(Locks::mutator_lock_) {
1948         std::ostream& indent_os = vios_.Stream();
1949         indent_os << &method << " " << " ArtMethod: " << method.PrettyMethod() << "\n";
1950         DumpMethod(&method, indent_os);
1951         indent_os << "\n";
1952       },  image_space_.Begin(), image_header_.GetPointerSize());
1953       // Dump the large objects separately.
1954       heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(dump_visitor);
1955       indent_os << "\n";
1956     }
1957     os << "STATS:\n" << std::flush;
1958     std::unique_ptr<File> file(OS::OpenFileForReading(image_filename.c_str()));
1959     size_t data_size = image_header_.GetDataSize();  // stored size in file.
1960     if (file == nullptr) {
1961       LOG(WARNING) << "Failed to find image in " << image_filename;
1962     } else {
1963       size_t file_bytes = file->GetLength();
1964       // If the image is compressed, adjust to decompressed size.
1965       size_t uncompressed_size = image_header_.GetImageSize() - sizeof(ImageHeader);
1966       if (!image_header_.HasCompressedBlock()) {
1967         DCHECK_EQ(uncompressed_size, data_size) << "Sizes should match for uncompressed image";
1968       }
1969       file_bytes += uncompressed_size - data_size;
1970       stats_.art_file_stats.AddBytes(file_bytes);
1971       stats_.art_file_stats["Header"].AddBytes(sizeof(ImageHeader));
1972     }
1973 
1974     size_t pointer_size = static_cast<size_t>(image_header_.GetPointerSize());
1975     CHECK_ALIGNED(image_header_.GetFieldsSection().Offset(), 4);
1976     CHECK_ALIGNED_PARAM(image_header_.GetMethodsSection().Offset(), pointer_size);
1977     CHECK_ALIGNED(image_header_.GetInternedStringsSection().Offset(), 8);
1978     CHECK_ALIGNED(image_header_.GetImageBitmapSection().Offset(), kElfSegmentAlignment);
1979 
1980     for (size_t i = 0; i < ImageHeader::ImageSections::kSectionCount; i++) {
1981       ImageHeader::ImageSections index = ImageHeader::ImageSections(i);
1982       const char* name = ImageHeader::GetImageSectionName(index);
1983       stats_.art_file_stats[name].AddBytes(image_header_.GetImageSection(index).Size());
1984     }
1985 
1986     stats_.object_stats.AddBytes(image_header_.GetObjectsSection().Size());
1987     stats_.Dump(os);
1988     os << "\n";
1989 
1990     os << std::flush;
1991 
1992     return oat_dumper_->Dump(os);
1993   }
1994 
1995  private:
PrettyObjectValue(std::ostream & os,ObjPtr<mirror::Class> type,ObjPtr<mirror::Object> value)1996   static void PrettyObjectValue(std::ostream& os,
1997                                 ObjPtr<mirror::Class> type,
1998                                 ObjPtr<mirror::Object> value)
1999       REQUIRES_SHARED(Locks::mutator_lock_) {
2000     CHECK(type != nullptr);
2001     if (value == nullptr) {
2002       os << StringPrintf("null   %s\n", type->PrettyDescriptor().c_str());
2003     } else if (type->IsStringClass()) {
2004       ObjPtr<mirror::String> string = value->AsString();
2005       os << StringPrintf("%p   String: %s\n",
2006                          string.Ptr(),
2007                          PrintableString(string->ToModifiedUtf8().c_str()).c_str());
2008     } else if (type->IsClassClass()) {
2009       ObjPtr<mirror::Class> klass = value->AsClass();
2010       os << StringPrintf("%p   Class: %s\n",
2011                          klass.Ptr(),
2012                          mirror::Class::PrettyDescriptor(klass).c_str());
2013     } else {
2014       os << StringPrintf("%p   %s\n", value.Ptr(), type->PrettyDescriptor().c_str());
2015     }
2016   }
2017 
PrintField(std::ostream & os,ArtField * field,ObjPtr<mirror::Object> obj)2018   static void PrintField(std::ostream& os, ArtField* field, ObjPtr<mirror::Object> obj)
2019       REQUIRES_SHARED(Locks::mutator_lock_) {
2020     os << StringPrintf("%s: ", field->GetName());
2021     switch (field->GetTypeAsPrimitiveType()) {
2022       case Primitive::kPrimLong:
2023         os << StringPrintf("%" PRId64 " (0x%" PRIx64 ")\n", field->Get64(obj), field->Get64(obj));
2024         break;
2025       case Primitive::kPrimDouble:
2026         os << StringPrintf("%f (%a)\n", field->GetDouble(obj), field->GetDouble(obj));
2027         break;
2028       case Primitive::kPrimFloat:
2029         os << StringPrintf("%f (%a)\n", field->GetFloat(obj), field->GetFloat(obj));
2030         break;
2031       case Primitive::kPrimInt:
2032         os << StringPrintf("%d (0x%x)\n", field->Get32(obj), field->Get32(obj));
2033         break;
2034       case Primitive::kPrimChar:
2035         os << StringPrintf("%u (0x%x)\n", field->GetChar(obj), field->GetChar(obj));
2036         break;
2037       case Primitive::kPrimShort:
2038         os << StringPrintf("%d (0x%x)\n", field->GetShort(obj), field->GetShort(obj));
2039         break;
2040       case Primitive::kPrimBoolean:
2041         os << StringPrintf("%s (0x%x)\n", field->GetBoolean(obj) ? "true" : "false",
2042             field->GetBoolean(obj));
2043         break;
2044       case Primitive::kPrimByte:
2045         os << StringPrintf("%d (0x%x)\n", field->GetByte(obj), field->GetByte(obj));
2046         break;
2047       case Primitive::kPrimNot: {
2048         // Get the value, don't compute the type unless it is non-null as we don't want
2049         // to cause class loading.
2050         ObjPtr<mirror::Object> value = field->GetObj(obj);
2051         if (value == nullptr) {
2052           os << StringPrintf("null   %s\n", PrettyDescriptor(field->GetTypeDescriptor()).c_str());
2053         } else {
2054           // Grab the field type without causing resolution.
2055           ObjPtr<mirror::Class> field_type = field->LookupResolvedType();
2056           if (field_type != nullptr) {
2057             PrettyObjectValue(os, field_type, value);
2058           } else {
2059             os << StringPrintf("%p   %s\n",
2060                                value.Ptr(),
2061                                PrettyDescriptor(field->GetTypeDescriptor()).c_str());
2062           }
2063         }
2064         break;
2065       }
2066       default:
2067         os << "unexpected field type: " << field->GetTypeDescriptor() << "\n";
2068         break;
2069     }
2070   }
2071 
DumpFields(std::ostream & os,mirror::Object * obj,ObjPtr<mirror::Class> klass)2072   static void DumpFields(std::ostream& os, mirror::Object* obj, ObjPtr<mirror::Class> klass)
2073       REQUIRES_SHARED(Locks::mutator_lock_) {
2074     ObjPtr<mirror::Class> super = klass->GetSuperClass();
2075     if (super != nullptr) {
2076       DumpFields(os, obj, super);
2077     }
2078     for (ArtField& field : klass->GetIFields()) {
2079       PrintField(os, &field, obj);
2080     }
2081   }
2082 
InDumpSpace(const mirror::Object * object)2083   bool InDumpSpace(const mirror::Object* object) {
2084     return image_space_.Contains(object);
2085   }
2086 
GetQuickOatCodeBegin(ArtMethod * m)2087   const void* GetQuickOatCodeBegin(ArtMethod* m) REQUIRES_SHARED(Locks::mutator_lock_) {
2088     const void* quick_code = m->GetEntryPointFromQuickCompiledCodePtrSize(
2089         image_header_.GetPointerSize());
2090     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
2091     if (class_linker->IsQuickResolutionStub(quick_code) ||
2092         class_linker->IsQuickToInterpreterBridge(quick_code) ||
2093         class_linker->IsNterpTrampoline(quick_code) ||
2094         class_linker->IsQuickGenericJniStub(quick_code) ||
2095         class_linker->IsJniDlsymLookupStub(quick_code) ||
2096         class_linker->IsJniDlsymLookupCriticalStub(quick_code)) {
2097       quick_code = oat_dumper_->GetQuickOatCode(m);
2098     }
2099     if (oat_dumper_->GetInstructionSet() == InstructionSet::kThumb2) {
2100       quick_code = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(quick_code) & ~0x1);
2101     }
2102     return quick_code;
2103   }
2104 
GetQuickOatCodeSize(ArtMethod * m)2105   uint32_t GetQuickOatCodeSize(ArtMethod* m)
2106       REQUIRES_SHARED(Locks::mutator_lock_) {
2107     const uint32_t* oat_code_begin = reinterpret_cast<const uint32_t*>(GetQuickOatCodeBegin(m));
2108     if (oat_code_begin == nullptr) {
2109       return 0;
2110     }
2111     OatQuickMethodHeader* method_header = reinterpret_cast<OatQuickMethodHeader*>(
2112         reinterpret_cast<uintptr_t>(oat_code_begin) - sizeof(OatQuickMethodHeader));
2113     return method_header->GetCodeSize();
2114   }
2115 
GetQuickOatCodeEnd(ArtMethod * m)2116   const void* GetQuickOatCodeEnd(ArtMethod* m)
2117       REQUIRES_SHARED(Locks::mutator_lock_) {
2118     const uint8_t* oat_code_begin = reinterpret_cast<const uint8_t*>(GetQuickOatCodeBegin(m));
2119     if (oat_code_begin == nullptr) {
2120       return nullptr;
2121     }
2122     return oat_code_begin + GetQuickOatCodeSize(m);
2123   }
2124 
DumpObject(mirror::Object * obj)2125   void DumpObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
2126     DCHECK(obj != nullptr);
2127     if (!InDumpSpace(obj)) {
2128       return;
2129     }
2130 
2131     std::ostream& os = vios_.Stream();
2132 
2133     ObjPtr<mirror::Class> obj_class = obj->GetClass();
2134     if (obj_class->IsArrayClass()) {
2135       os << StringPrintf("%p: %s length:%d\n", obj, obj_class->PrettyDescriptor().c_str(),
2136                          obj->AsArray()->GetLength());
2137     } else if (obj_class->IsClassClass()) {
2138       ObjPtr<mirror::Class> klass = obj->AsClass();
2139       os << StringPrintf("%p: java.lang.Class \"%s\" (",
2140                          obj,
2141                          mirror::Class::PrettyDescriptor(klass).c_str())
2142          << klass->GetStatus() << ")\n";
2143     } else if (obj_class->IsStringClass()) {
2144       os << StringPrintf("%p: java.lang.String %s\n",
2145                          obj,
2146                          PrintableString(obj->AsString()->ToModifiedUtf8().c_str()).c_str());
2147     } else {
2148       os << StringPrintf("%p: %s\n", obj, obj_class->PrettyDescriptor().c_str());
2149     }
2150     ScopedIndentation indent1(&vios_);
2151     DumpFields(os, obj, obj_class);
2152     if (obj->IsObjectArray()) {
2153       ObjPtr<mirror::ObjectArray<mirror::Object>> obj_array = obj->AsObjectArray<mirror::Object>();
2154       for (int32_t i = 0, length = obj_array->GetLength(); i < length; i++) {
2155         ObjPtr<mirror::Object> value = obj_array->Get(i);
2156         size_t run = 0;
2157         for (int32_t j = i + 1; j < length; j++) {
2158           if (value == obj_array->Get(j)) {
2159             run++;
2160           } else {
2161             break;
2162           }
2163         }
2164         if (run == 0) {
2165           os << StringPrintf("%d: ", i);
2166         } else {
2167           os << StringPrintf("%d to %zd: ", i, i + run);
2168           i = i + run;
2169         }
2170         ObjPtr<mirror::Class> value_class =
2171             (value == nullptr) ? obj_class->GetComponentType() : value->GetClass();
2172         PrettyObjectValue(os, value_class, value);
2173       }
2174     } else if (obj_class->IsClassClass()) {
2175       ObjPtr<mirror::Class> klass = obj->AsClass();
2176 
2177       if (kBitstringSubtypeCheckEnabled) {
2178         os << "SUBTYPE_CHECK_BITS: ";
2179         SubtypeCheck<ObjPtr<mirror::Class>>::Dump(klass, os);
2180         os << "\n";
2181       }
2182 
2183       if (klass->ShouldHaveEmbeddedVTable()) {
2184         os << "EMBEDDED VTABLE:\n";
2185         ScopedIndentation indent2(&vios_);
2186         const PointerSize pointer_size = image_header_.GetPointerSize();
2187         for (size_t i = 0, length = klass->GetEmbeddedVTableLength(); i != length; ++i) {
2188           os << i << ": "
2189              << ArtMethod::PrettyMethod(klass->GetEmbeddedVTableEntry(i, pointer_size)) << '\n';
2190         }
2191       }
2192 
2193       if (klass->NumStaticFields() != 0) {
2194         os << "STATICS:\n";
2195         ScopedIndentation indent2(&vios_);
2196         for (ArtField& field : klass->GetSFields()) {
2197           PrintField(os, &field, field.GetDeclaringClass());
2198         }
2199       }
2200     }
2201     std::string temp;
2202     const char* desc = obj_class->GetDescriptor(&temp);
2203     desc = stats_.descriptors.emplace(desc).first->c_str();  // Dedup and keep alive.
2204     stats_.object_stats[desc].AddBytes(obj->SizeOf());
2205   }
2206 
DumpMethod(ArtMethod * method,std::ostream & indent_os)2207   void DumpMethod(ArtMethod* method, std::ostream& indent_os)
2208       REQUIRES_SHARED(Locks::mutator_lock_) {
2209     DCHECK(method != nullptr);
2210     const PointerSize pointer_size = image_header_.GetPointerSize();
2211     if (method->IsNative()) {
2212       const void* quick_oat_code_begin = GetQuickOatCodeBegin(method);
2213       bool first_occurrence;
2214       uint32_t quick_oat_code_size = GetQuickOatCodeSize(method);
2215       ComputeOatSize(quick_oat_code_begin, &first_occurrence);
2216       if (first_occurrence) {
2217         stats_.oat_file_stats["native_code"].AddBytes(quick_oat_code_size);
2218       }
2219       if (quick_oat_code_begin != method->GetEntryPointFromQuickCompiledCodePtrSize(
2220           image_header_.GetPointerSize())) {
2221         indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code_begin);
2222       }
2223     } else if (method->IsAbstract() || method->IsClassInitializer()) {
2224       // Don't print information for these.
2225     } else if (method->IsRuntimeMethod()) {
2226       if (method == Runtime::Current()->GetResolutionMethod()) {
2227         const void* resolution_trampoline =
2228             method->GetEntryPointFromQuickCompiledCodePtrSize(image_header_.GetPointerSize());
2229         indent_os << StringPrintf("Resolution trampoline: %p\n", resolution_trampoline);
2230         const void* critical_native_resolution_trampoline =
2231             method->GetEntryPointFromJniPtrSize(image_header_.GetPointerSize());
2232         indent_os << StringPrintf("Resolution trampoline for @CriticalNative: %p\n",
2233                                   critical_native_resolution_trampoline);
2234       } else {
2235         ImtConflictTable* table = method->GetImtConflictTable(image_header_.GetPointerSize());
2236         if (table != nullptr) {
2237           indent_os << "IMT conflict table " << table << " method: ";
2238           for (size_t i = 0, count = table->NumEntries(pointer_size); i < count; ++i) {
2239             indent_os << ArtMethod::PrettyMethod(table->GetImplementationMethod(i, pointer_size))
2240                       << " ";
2241           }
2242         }
2243       }
2244     } else {
2245       CodeItemDataAccessor code_item_accessor(method->DexInstructionData());
2246       size_t dex_instruction_bytes = code_item_accessor.InsnsSizeInCodeUnits() * 2;
2247       stats_.dex_instruction_bytes += dex_instruction_bytes;
2248 
2249       const void* quick_oat_code_begin = GetQuickOatCodeBegin(method);
2250       const void* quick_oat_code_end = GetQuickOatCodeEnd(method);
2251 
2252       bool first_occurrence;
2253       size_t vmap_table_bytes = 0u;
2254       if (quick_oat_code_begin != nullptr) {
2255         OatQuickMethodHeader* method_header = reinterpret_cast<OatQuickMethodHeader*>(
2256             reinterpret_cast<uintptr_t>(quick_oat_code_begin) - sizeof(OatQuickMethodHeader));
2257         vmap_table_bytes = ComputeOatSize(method_header->GetOptimizedCodeInfoPtr(),
2258                                           &first_occurrence);
2259         if (first_occurrence) {
2260           stats_.vmap_table_bytes += vmap_table_bytes;
2261         }
2262       }
2263 
2264       uint32_t quick_oat_code_size = GetQuickOatCodeSize(method);
2265       ComputeOatSize(quick_oat_code_begin, &first_occurrence);
2266       if (first_occurrence) {
2267         stats_.managed_code_bytes += quick_oat_code_size;
2268         art::Stats& managed_code_stats = stats_.oat_file_stats["managed_code"];
2269         managed_code_stats.AddBytes(quick_oat_code_size);
2270         if (method->IsConstructor()) {
2271           if (method->IsStatic()) {
2272             managed_code_stats["class_initializer"].AddBytes(quick_oat_code_size);
2273           } else if (dex_instruction_bytes > kLargeConstructorDexBytes) {
2274             managed_code_stats["large_initializer"].AddBytes(quick_oat_code_size);
2275           }
2276         } else if (dex_instruction_bytes > kLargeMethodDexBytes) {
2277           managed_code_stats["large_method"].AddBytes(quick_oat_code_size);
2278         }
2279       }
2280       stats_.managed_code_bytes_ignoring_deduplication += quick_oat_code_size;
2281 
2282       uint32_t method_access_flags = method->GetAccessFlags();
2283 
2284       indent_os << StringPrintf("OAT CODE: %p-%p\n", quick_oat_code_begin, quick_oat_code_end);
2285       indent_os << StringPrintf("SIZE: Dex Instructions=%zd StackMaps=%zd AccessFlags=0x%x\n",
2286                                 dex_instruction_bytes,
2287                                 vmap_table_bytes,
2288                                 method_access_flags);
2289 
2290       size_t total_size = dex_instruction_bytes +
2291           vmap_table_bytes + quick_oat_code_size + ArtMethod::Size(image_header_.GetPointerSize());
2292 
2293       double expansion =
2294       static_cast<double>(quick_oat_code_size) / static_cast<double>(dex_instruction_bytes);
2295       stats_.ComputeOutliers(total_size, expansion, method);
2296     }
2297   }
2298 
2299   std::set<const void*> already_seen_;
2300   // Compute the size of the given data within the oat file and whether this is the first time
2301   // this data has been requested
ComputeOatSize(const void * oat_data,bool * first_occurrence)2302   size_t ComputeOatSize(const void* oat_data, bool* first_occurrence) {
2303     if (already_seen_.count(oat_data) == 0) {
2304       *first_occurrence = true;
2305       already_seen_.insert(oat_data);
2306     } else {
2307       *first_occurrence = false;
2308     }
2309     return oat_dumper_->ComputeSize(oat_data);
2310   }
2311 
2312  public:
2313   struct Stats {
2314     art::Stats art_file_stats;
2315     art::Stats oat_file_stats;
2316     art::Stats object_stats;
2317     std::set<std::string> descriptors;
2318 
2319     size_t oat_file_bytes = 0u;
2320     size_t managed_code_bytes = 0u;
2321     size_t managed_code_bytes_ignoring_deduplication = 0u;
2322 
2323     size_t vmap_table_bytes = 0u;
2324 
2325     size_t dex_instruction_bytes = 0u;
2326 
2327     std::vector<ArtMethod*> method_outlier;
2328     std::vector<size_t> method_outlier_size;
2329     std::vector<double> method_outlier_expansion;
2330     std::vector<std::pair<std::string, size_t>> oat_dex_file_sizes;
2331 
Statsart::ImageDumper::Stats2332     Stats() {}
2333 
PercentOfOatBytesart::ImageDumper::Stats2334     double PercentOfOatBytes(size_t size) {
2335       return (static_cast<double>(size) / static_cast<double>(oat_file_bytes)) * 100;
2336     }
2337 
ComputeOutliersart::ImageDumper::Stats2338     void ComputeOutliers(size_t total_size, double expansion, ArtMethod* method) {
2339       method_outlier_size.push_back(total_size);
2340       method_outlier_expansion.push_back(expansion);
2341       method_outlier.push_back(method);
2342     }
2343 
DumpOutliersart::ImageDumper::Stats2344     void DumpOutliers(std::ostream& os)
2345         REQUIRES_SHARED(Locks::mutator_lock_) {
2346       size_t sum_of_sizes = 0;
2347       size_t sum_of_sizes_squared = 0;
2348       size_t sum_of_expansion = 0;
2349       size_t sum_of_expansion_squared = 0;
2350       size_t n = method_outlier_size.size();
2351       if (n <= 1) {
2352         return;
2353       }
2354       for (size_t i = 0; i < n; i++) {
2355         size_t cur_size = method_outlier_size[i];
2356         sum_of_sizes += cur_size;
2357         sum_of_sizes_squared += cur_size * cur_size;
2358         double cur_expansion = method_outlier_expansion[i];
2359         sum_of_expansion += cur_expansion;
2360         sum_of_expansion_squared += cur_expansion * cur_expansion;
2361       }
2362       size_t size_mean = sum_of_sizes / n;
2363       size_t size_variance = (sum_of_sizes_squared - sum_of_sizes * size_mean) / (n - 1);
2364       double expansion_mean = sum_of_expansion / n;
2365       double expansion_variance =
2366           (sum_of_expansion_squared - sum_of_expansion * expansion_mean) / (n - 1);
2367 
2368       // Dump methods whose size is a certain number of standard deviations from the mean
2369       size_t dumped_values = 0;
2370       size_t skipped_values = 0;
2371       for (size_t i = 100; i > 0; i--) {  // i is the current number of standard deviations
2372         size_t cur_size_variance = i * i * size_variance;
2373         bool first = true;
2374         for (size_t j = 0; j < n; j++) {
2375           size_t cur_size = method_outlier_size[j];
2376           if (cur_size > size_mean) {
2377             size_t cur_var = cur_size - size_mean;
2378             cur_var = cur_var * cur_var;
2379             if (cur_var > cur_size_variance) {
2380               if (dumped_values > 20) {
2381                 if (i == 1) {
2382                   skipped_values++;
2383                 } else {
2384                   i = 2;  // jump to counting for 1 standard deviation
2385                   break;
2386                 }
2387               } else {
2388                 if (first) {
2389                   os << "\nBig methods (size > " << i << " standard deviations the norm):\n";
2390                   first = false;
2391                 }
2392                 os << ArtMethod::PrettyMethod(method_outlier[j]) << " requires storage of "
2393                     << PrettySize(cur_size) << "\n";
2394                 method_outlier_size[j] = 0;  // don't consider this method again
2395                 dumped_values++;
2396               }
2397             }
2398           }
2399         }
2400       }
2401       if (skipped_values > 0) {
2402         os << "... skipped " << skipped_values
2403            << " methods with size > 1 standard deviation from the norm\n";
2404       }
2405       os << std::flush;
2406 
2407       // Dump methods whose expansion is a certain number of standard deviations from the mean
2408       dumped_values = 0;
2409       skipped_values = 0;
2410       for (size_t i = 10; i > 0; i--) {  // i is the current number of standard deviations
2411         double cur_expansion_variance = i * i * expansion_variance;
2412         bool first = true;
2413         for (size_t j = 0; j < n; j++) {
2414           double cur_expansion = method_outlier_expansion[j];
2415           if (cur_expansion > expansion_mean) {
2416             size_t cur_var = cur_expansion - expansion_mean;
2417             cur_var = cur_var * cur_var;
2418             if (cur_var > cur_expansion_variance) {
2419               if (dumped_values > 20) {
2420                 if (i == 1) {
2421                   skipped_values++;
2422                 } else {
2423                   i = 2;  // jump to counting for 1 standard deviation
2424                   break;
2425                 }
2426               } else {
2427                 if (first) {
2428                   os << "\nLarge expansion methods (size > " << i
2429                       << " standard deviations the norm):\n";
2430                   first = false;
2431                 }
2432                 os << ArtMethod::PrettyMethod(method_outlier[j]) << " expanded code by "
2433                    << cur_expansion << "\n";
2434                 method_outlier_expansion[j] = 0.0;  // don't consider this method again
2435                 dumped_values++;
2436               }
2437             }
2438           }
2439         }
2440       }
2441       if (skipped_values > 0) {
2442         os << "... skipped " << skipped_values
2443            << " methods with expansion > 1 standard deviation from the norm\n";
2444       }
2445       os << "\n" << std::flush;
2446     }
2447 
Dumpart::ImageDumper::Stats2448     void Dump(std::ostream& os)
2449         REQUIRES_SHARED(Locks::mutator_lock_) {
2450       VariableIndentationOutputStream vios(&os);
2451       art_file_stats.DumpSizes(vios, "ArtFile");
2452       os << "\n" << std::flush;
2453       object_stats.DumpSizes(vios, "Objects");
2454       os << "\n" << std::flush;
2455       oat_file_stats.DumpSizes(vios, "OatFile");
2456       os << "\n" << std::flush;
2457 
2458       for (const std::pair<std::string, size_t>& oat_dex_file_size : oat_dex_file_sizes) {
2459         os << StringPrintf("%s = %zd (%2.0f%% of oat file bytes)\n",
2460                            oat_dex_file_size.first.c_str(), oat_dex_file_size.second,
2461                            PercentOfOatBytes(oat_dex_file_size.second));
2462       }
2463 
2464       os << "\n" << StringPrintf("vmap_table_bytes       = %7zd (%2.0f%% of oat file bytes)\n\n",
2465                                  vmap_table_bytes, PercentOfOatBytes(vmap_table_bytes))
2466          << std::flush;
2467 
2468       os << StringPrintf("dex_instruction_bytes = %zd\n", dex_instruction_bytes)
2469          << StringPrintf("managed_code_bytes expansion = %.2f (ignoring deduplication %.2f)\n\n",
2470                          static_cast<double>(managed_code_bytes) /
2471                              static_cast<double>(dex_instruction_bytes),
2472                          static_cast<double>(managed_code_bytes_ignoring_deduplication) /
2473                              static_cast<double>(dex_instruction_bytes))
2474          << std::flush;
2475 
2476       DumpOutliers(os);
2477     }
2478   } stats_;
2479 
2480  private:
2481   enum {
2482     // Number of bytes for a constructor to be considered large. Based on the 1000 basic block
2483     // threshold, we assume 2 bytes per instruction and 2 instructions per block.
2484     kLargeConstructorDexBytes = 4000,
2485     // Number of bytes for a method to be considered large. Based on the 4000 basic block
2486     // threshold, we assume 2 bytes per instruction and 2 instructions per block.
2487     kLargeMethodDexBytes = 16000
2488   };
2489 
2490   // For performance, use the *os_ directly for anything that doesn't need indentation
2491   // and prepare an indentation stream with default indentation 1.
2492   std::ostream* os_;
2493   VariableIndentationOutputStream vios_;
2494   ScopedIndentation indent1_;
2495 
2496   gc::space::ImageSpace& image_space_;
2497   const ImageHeader& image_header_;
2498   std::unique_ptr<OatDumper> oat_dumper_;
2499   OatDumperOptions* oat_dumper_options_;
2500 
2501   DISALLOW_COPY_AND_ASSIGN(ImageDumper);
2502 };
2503 
OpenOat(const std::string & oat_filename,const std::optional<std::string> & dex_filename,std::string * error_msg)2504 static std::unique_ptr<OatFile> OpenOat(const std::string& oat_filename,
2505                                         const std::optional<std::string>& dex_filename,
2506                                         std::string* error_msg) {
2507   if (!dex_filename.has_value()) {
2508     LOG(WARNING) << "No dex filename provided, "
2509                  << "oatdump might fail if the oat file does not contain the dex code.";
2510   }
2511   ArrayRef<const std::string> dex_filenames =
2512       dex_filename.has_value() ? ArrayRef<const std::string>(&dex_filename.value(), /*size=*/1) :
2513                                  ArrayRef<const std::string>();
2514   return std::unique_ptr<OatFile>(OatFile::Open(/*zip_fd=*/-1,
2515                                                 oat_filename,
2516                                                 oat_filename,
2517                                                 /*executable=*/false,
2518                                                 /*low_4gb=*/false,
2519                                                 dex_filenames,
2520                                                 /*dex_files=*/{},
2521                                                 /*reservation=*/nullptr,
2522                                                 error_msg));
2523 }
2524 
DumpImage(gc::space::ImageSpace * image_space,OatDumperOptions * options,std::ostream * os)2525 static int DumpImage(gc::space::ImageSpace* image_space,
2526                      OatDumperOptions* options,
2527                      std::ostream* os) REQUIRES_SHARED(Locks::mutator_lock_) {
2528   const ImageHeader& image_header = image_space->GetImageHeader();
2529   if (!image_header.IsValid()) {
2530     LOG(ERROR) << "Invalid image header " << image_space->GetImageLocation();
2531     return EXIT_FAILURE;
2532   }
2533   ImageDumper image_dumper(os, *image_space, image_header, options);
2534   if (!image_dumper.Dump()) {
2535     return EXIT_FAILURE;
2536   }
2537   return EXIT_SUCCESS;
2538 }
2539 
DumpImages(Runtime * runtime,OatDumperOptions * options,std::ostream * os)2540 static int DumpImages(Runtime* runtime, OatDumperOptions* options, std::ostream* os) {
2541   // Dumping the image, no explicit class loader.
2542   ScopedNullHandle<mirror::ClassLoader> null_class_loader;
2543   options->class_loader_ = &null_class_loader;
2544 
2545   if (options->app_image_ != nullptr) {
2546     if (!options->oat_filename_.has_value()) {
2547       LOG(ERROR) << "Can not dump app image without app oat file";
2548       return EXIT_FAILURE;
2549     }
2550     // We can't know if the app image is 32 bits yet, but it contains pointers into the oat file.
2551     // We need to map the oat file in the low 4gb or else the fixup wont be able to fit oat file
2552     // pointers into 32 bit pointer sized ArtMethods.
2553     std::string error_msg;
2554     std::unique_ptr<OatFile> oat_file =
2555         OpenOat(*options->oat_filename_, options->dex_filename_, &error_msg);
2556     if (oat_file == nullptr) {
2557       LOG(ERROR) << "Failed to open oat file " << *options->oat_filename_ << " with error "
2558                  << error_msg;
2559       return EXIT_FAILURE;
2560     }
2561     std::unique_ptr<gc::space::ImageSpace> space(
2562         gc::space::ImageSpace::CreateFromAppImage(options->app_image_, oat_file.get(), &error_msg));
2563     if (space == nullptr) {
2564       LOG(ERROR) << "Failed to open app image " << options->app_image_ << " with error "
2565                  << error_msg;
2566       return EXIT_FAILURE;
2567     }
2568     // Open dex files for the image.
2569     ScopedObjectAccess soa(Thread::Current());
2570     std::vector<std::unique_ptr<const DexFile>> dex_files;
2571     if (!runtime->GetClassLinker()->OpenImageDexFiles(space.get(), &dex_files, &error_msg)) {
2572       LOG(ERROR) << "Failed to open app image dex files " << options->app_image_ << " with error "
2573                  << error_msg;
2574       return EXIT_FAILURE;
2575     }
2576     // Dump the actual image.
2577     return DumpImage(space.get(), options, os);
2578   }
2579 
2580   gc::Heap* heap = runtime->GetHeap();
2581   if (!heap->HasBootImageSpace()) {
2582     LOG(ERROR) << "No image spaces";
2583     return EXIT_FAILURE;
2584   }
2585   ScopedObjectAccess soa(Thread::Current());
2586   for (gc::space::ImageSpace* image_space : heap->GetBootImageSpaces()) {
2587     int result = DumpImage(image_space, options, os);
2588     if (result != EXIT_SUCCESS) {
2589       return result;
2590     }
2591   }
2592   return EXIT_SUCCESS;
2593 }
2594 
InstallOatFile(Runtime * runtime,std::unique_ptr<OatFile> oat_file,std::vector<const DexFile * > * class_path)2595 static jobject InstallOatFile(Runtime* runtime,
2596                               std::unique_ptr<OatFile> oat_file,
2597                               std::vector<const DexFile*>* class_path)
2598     REQUIRES_SHARED(Locks::mutator_lock_) {
2599   Thread* self = Thread::Current();
2600   CHECK(self != nullptr);
2601   // Need well-known-classes.
2602   WellKnownClasses::Init(self->GetJniEnv());
2603 
2604   // Open dex files.
2605   OatFile* oat_file_ptr = oat_file.get();
2606   ClassLinker* class_linker = runtime->GetClassLinker();
2607   runtime->GetOatFileManager().RegisterOatFile(std::move(oat_file));
2608   for (const OatDexFile* odf : oat_file_ptr->GetOatDexFiles()) {
2609     std::string error_msg;
2610     const DexFile* const dex_file = OpenDexFile(odf, &error_msg);
2611     CHECK(dex_file != nullptr) << error_msg;
2612     class_path->push_back(dex_file);
2613   }
2614 
2615   // Need a class loader. Fake that we're a compiler.
2616   // Note: this will run initializers through the unstarted runtime, so make sure it's
2617   //       initialized.
2618   interpreter::UnstartedRuntime::Initialize();
2619 
2620   jobject class_loader = class_linker->CreatePathClassLoader(self, *class_path);
2621 
2622   // Need to register dex files to get a working dex cache.
2623   for (const DexFile* dex_file : *class_path) {
2624     ObjPtr<mirror::DexCache> dex_cache = class_linker->RegisterDexFile(
2625         *dex_file, self->DecodeJObject(class_loader)->AsClassLoader());
2626     CHECK(dex_cache != nullptr);
2627   }
2628 
2629   return class_loader;
2630 }
2631 
DumpOatWithRuntime(Runtime * runtime,std::unique_ptr<OatFile> oat_file,OatDumperOptions * options,std::ostream * os)2632 static int DumpOatWithRuntime(Runtime* runtime,
2633                               std::unique_ptr<OatFile> oat_file,
2634                               OatDumperOptions* options,
2635                               std::ostream* os) {
2636   CHECK(runtime != nullptr && oat_file != nullptr && options != nullptr);
2637   ScopedObjectAccess soa(Thread::Current());
2638 
2639   OatFile* oat_file_ptr = oat_file.get();
2640   std::vector<const DexFile*> class_path;
2641   jobject class_loader = InstallOatFile(runtime, std::move(oat_file), &class_path);
2642 
2643   // Use the class loader while dumping.
2644   StackHandleScope<1> scope(soa.Self());
2645   Handle<mirror::ClassLoader> loader_handle = scope.NewHandle(
2646       soa.Decode<mirror::ClassLoader>(class_loader));
2647   options->class_loader_ = &loader_handle;
2648 
2649   OatDumper oat_dumper(*oat_file_ptr, *options);
2650   bool success = oat_dumper.Dump(*os);
2651   return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
2652 }
2653 
DumpOatWithoutRuntime(OatFile * oat_file,OatDumperOptions * options,std::ostream * os)2654 static int DumpOatWithoutRuntime(OatFile* oat_file, OatDumperOptions* options, std::ostream* os) {
2655   CHECK(oat_file != nullptr && options != nullptr);
2656   // No image = no class loader.
2657   ScopedNullHandle<mirror::ClassLoader> null_class_loader;
2658   options->class_loader_ = &null_class_loader;
2659 
2660   OatDumper oat_dumper(*oat_file, *options);
2661   bool success = oat_dumper.Dump(*os);
2662   return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
2663 }
2664 
DumpOat(Runtime * runtime,OatDumperOptions * options,std::ostream * os)2665 static int DumpOat(Runtime* runtime, OatDumperOptions* options, std::ostream* os) {
2666   std::string error_msg;
2667   std::unique_ptr<OatFile> oat_file =
2668       OpenOat(*options->oat_filename_, options->dex_filename_, &error_msg);
2669   if (oat_file == nullptr) {
2670     LOG(ERROR) << "Failed to open oat file from '" << *options->oat_filename_ << "': " << error_msg;
2671     return EXIT_FAILURE;
2672   }
2673 
2674   if (runtime != nullptr) {
2675     return DumpOatWithRuntime(runtime, std::move(oat_file), options, os);
2676   } else {
2677     return DumpOatWithoutRuntime(oat_file.get(), options, os);
2678   }
2679 }
2680 
SymbolizeOat(const char * oat_filename,const char * dex_filename,std::string & output_name,bool no_bits)2681 static int SymbolizeOat(const char* oat_filename,
2682                         const char* dex_filename,
2683                         std::string& output_name,
2684                         bool no_bits) {
2685   std::string error_msg;
2686   std::unique_ptr<OatFile> oat_file =
2687       OpenOat(oat_filename,
2688               dex_filename != nullptr ? std::make_optional(dex_filename) : std::nullopt,
2689               &error_msg);
2690   if (oat_file == nullptr) {
2691     LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg;
2692     return EXIT_FAILURE;
2693   }
2694 
2695   bool result;
2696   // Try to produce an ELF file of the same type. This is finicky, as we have used 32-bit ELF
2697   // files for 64-bit code in the past.
2698   if (Is64BitInstructionSet(oat_file->GetOatHeader().GetInstructionSet())) {
2699     OatSymbolizer<ElfTypes64> oat_symbolizer(oat_file.get(), output_name, no_bits);
2700     result = oat_symbolizer.Symbolize();
2701   } else {
2702     OatSymbolizer<ElfTypes32> oat_symbolizer(oat_file.get(), output_name, no_bits);
2703     result = oat_symbolizer.Symbolize();
2704   }
2705   if (!result) {
2706     LOG(ERROR) << "Failed to symbolize";
2707     return EXIT_FAILURE;
2708   }
2709 
2710   return EXIT_SUCCESS;
2711 }
2712 
2713 class IMTDumper {
2714  public:
Dump(Runtime * runtime,const std::string & imt_file,bool dump_imt_stats,const char * oat_filename,const char * dex_filename)2715   static bool Dump(Runtime* runtime,
2716                    const std::string& imt_file,
2717                    bool dump_imt_stats,
2718                    const char* oat_filename,
2719                    const char* dex_filename) {
2720     Thread* self = Thread::Current();
2721 
2722     ScopedObjectAccess soa(self);
2723     StackHandleScope<1> scope(self);
2724     MutableHandle<mirror::ClassLoader> class_loader = scope.NewHandle<mirror::ClassLoader>(nullptr);
2725     std::vector<const DexFile*> class_path;
2726 
2727     if (oat_filename != nullptr) {
2728       std::string error_msg;
2729       std::unique_ptr<OatFile> oat_file =
2730           OpenOat(oat_filename,
2731                   dex_filename != nullptr ? std::make_optional(dex_filename) : std::nullopt,
2732                   &error_msg);
2733       if (oat_file == nullptr) {
2734         LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg;
2735         return false;
2736       }
2737 
2738       class_loader.Assign(soa.Decode<mirror::ClassLoader>(
2739           InstallOatFile(runtime, std::move(oat_file), &class_path)));
2740     } else {
2741       class_loader.Assign(nullptr);  // Boot classloader. Just here for explicit documentation.
2742       class_path = runtime->GetClassLinker()->GetBootClassPath();
2743     }
2744 
2745     if (!imt_file.empty()) {
2746       return DumpImt(runtime, imt_file, class_loader);
2747     }
2748 
2749     if (dump_imt_stats) {
2750       return DumpImtStats(runtime, class_path, class_loader);
2751     }
2752 
2753     LOG(FATAL) << "Should not reach here";
2754     UNREACHABLE();
2755   }
2756 
2757  private:
DumpImt(Runtime * runtime,const std::string & imt_file,Handle<mirror::ClassLoader> h_class_loader)2758   static bool DumpImt(Runtime* runtime,
2759                       const std::string& imt_file,
2760                       Handle<mirror::ClassLoader> h_class_loader)
2761       REQUIRES_SHARED(Locks::mutator_lock_) {
2762     std::vector<std::string> lines = ReadCommentedInputFromFile(imt_file);
2763     std::unordered_set<std::string> prepared;
2764 
2765     for (const std::string& line : lines) {
2766       // A line should be either a class descriptor, in which case we will dump the complete IMT,
2767       // or a class descriptor and an interface method, in which case we will lookup the method,
2768       // determine its IMT slot, and check the class' IMT.
2769       size_t first_space = line.find(' ');
2770       if (first_space == std::string::npos) {
2771         DumpIMTForClass(runtime, line, h_class_loader, &prepared);
2772       } else {
2773         DumpIMTForMethod(runtime,
2774                          line.substr(0, first_space),
2775                          line.substr(first_space + 1, std::string::npos),
2776                          h_class_loader,
2777                          &prepared);
2778       }
2779       std::cerr << std::endl;
2780     }
2781 
2782     return true;
2783   }
2784 
DumpImtStats(Runtime * runtime,const std::vector<const DexFile * > & dex_files,Handle<mirror::ClassLoader> h_class_loader)2785   static bool DumpImtStats(Runtime* runtime,
2786                            const std::vector<const DexFile*>& dex_files,
2787                            Handle<mirror::ClassLoader> h_class_loader)
2788       REQUIRES_SHARED(Locks::mutator_lock_) {
2789     size_t without_imt = 0;
2790     size_t with_imt = 0;
2791     std::map<size_t, size_t> histogram;
2792 
2793     ClassLinker* class_linker = runtime->GetClassLinker();
2794     const PointerSize pointer_size = class_linker->GetImagePointerSize();
2795     std::unordered_set<std::string> prepared;
2796 
2797     Thread* self = Thread::Current();
2798     StackHandleScope<1> scope(self);
2799     MutableHandle<mirror::Class> h_klass(scope.NewHandle<mirror::Class>(nullptr));
2800 
2801     for (const DexFile* dex_file : dex_files) {
2802       for (uint32_t class_def_index = 0;
2803            class_def_index != dex_file->NumClassDefs();
2804            ++class_def_index) {
2805         const dex::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
2806         h_klass.Assign(
2807             class_linker->FindClass(self, *dex_file, class_def.class_idx_, h_class_loader));
2808         if (h_klass == nullptr) {
2809           std::cerr << "Warning: could not load "
2810                     << dex_file->GetTypeDescriptor(class_def.class_idx_) << std::endl;
2811           continue;
2812         }
2813 
2814         if (HasNoIMT(runtime, h_klass, pointer_size, &prepared)) {
2815           without_imt++;
2816           continue;
2817         }
2818 
2819         ImTable* im_table = PrepareAndGetImTable(runtime, h_klass, pointer_size, &prepared);
2820         if (im_table == nullptr) {
2821           // Should not happen, but accept.
2822           without_imt++;
2823           continue;
2824         }
2825 
2826         with_imt++;
2827         for (size_t imt_index = 0; imt_index != ImTable::kSize; ++imt_index) {
2828           ArtMethod* ptr = im_table->Get(imt_index, pointer_size);
2829           if (ptr->IsRuntimeMethod()) {
2830             if (ptr->IsImtUnimplementedMethod()) {
2831               histogram[0]++;
2832             } else {
2833               ImtConflictTable* current_table = ptr->GetImtConflictTable(pointer_size);
2834               histogram[current_table->NumEntries(pointer_size)]++;
2835             }
2836           } else {
2837             histogram[1]++;
2838           }
2839         }
2840       }
2841     }
2842 
2843     std::cerr << "IMT stats:"
2844               << std::endl << std::endl;
2845 
2846     std::cerr << "  " << with_imt << " classes with IMT."
2847               << std::endl << std::endl;
2848     std::cerr << "  " << without_imt << " classes without IMT (or copy from Object)."
2849               << std::endl << std::endl;
2850 
2851     double sum_one = 0;
2852     size_t count_one = 0;
2853 
2854     std::cerr << "  " << "IMT histogram" << std::endl;
2855     for (auto& bucket : histogram) {
2856       std::cerr << "    " << bucket.first << " " << bucket.second << std::endl;
2857       if (bucket.first > 0) {
2858         sum_one += bucket.second * bucket.first;
2859         count_one += bucket.second;
2860       }
2861     }
2862 
2863     double count_zero = count_one + histogram[0];
2864     std::cerr << "   Stats:" << std::endl;
2865     std::cerr << "     Average depth (including empty): " << (sum_one / count_zero) << std::endl;
2866     std::cerr << "     Average depth (excluding empty): " << (sum_one / count_one) << std::endl;
2867 
2868     return true;
2869   }
2870 
2871   // Return whether the given class has no IMT (or the one shared with java.lang.Object).
HasNoIMT(Runtime * runtime,Handle<mirror::Class> klass,const PointerSize pointer_size,std::unordered_set<std::string> * prepared)2872   static bool HasNoIMT(Runtime* runtime,
2873                        Handle<mirror::Class> klass,
2874                        const PointerSize pointer_size,
2875                        std::unordered_set<std::string>* prepared)
2876       REQUIRES_SHARED(Locks::mutator_lock_) {
2877     if (klass->IsObjectClass() || !klass->ShouldHaveImt()) {
2878       return true;
2879     }
2880 
2881     if (klass->GetImt(pointer_size) == nullptr) {
2882       PrepareClass(runtime, klass, prepared);
2883     }
2884 
2885     ObjPtr<mirror::Class> object_class = GetClassRoot<mirror::Object>();
2886     DCHECK(object_class->IsObjectClass());
2887 
2888     bool result = klass->GetImt(pointer_size) == object_class->GetImt(pointer_size);
2889 
2890     if (klass->GetIfTable()->Count() == 0) {
2891       DCHECK(result);
2892     }
2893 
2894     return result;
2895   }
2896 
PrintTable(ImtConflictTable * table,PointerSize pointer_size)2897   static void PrintTable(ImtConflictTable* table, PointerSize pointer_size)
2898       REQUIRES_SHARED(Locks::mutator_lock_) {
2899     if (table == nullptr) {
2900       std::cerr << "    <No IMT?>" << std::endl;
2901       return;
2902     }
2903     size_t table_index = 0;
2904     for (;;) {
2905       ArtMethod* ptr = table->GetInterfaceMethod(table_index, pointer_size);
2906       if (ptr == nullptr) {
2907         return;
2908       }
2909       table_index++;
2910       std::cerr << "    " << ptr->PrettyMethod(true) << std::endl;
2911     }
2912   }
2913 
PrepareAndGetImTable(Runtime * runtime,Thread * self,Handle<mirror::ClassLoader> h_loader,const std::string & class_name,const PointerSize pointer_size,ObjPtr<mirror::Class> * klass_out,std::unordered_set<std::string> * prepared)2914   static ImTable* PrepareAndGetImTable(Runtime* runtime,
2915                                        Thread* self,
2916                                        Handle<mirror::ClassLoader> h_loader,
2917                                        const std::string& class_name,
2918                                        const PointerSize pointer_size,
2919                                        /*out*/ ObjPtr<mirror::Class>* klass_out,
2920                                        /*inout*/ std::unordered_set<std::string>* prepared)
2921       REQUIRES_SHARED(Locks::mutator_lock_) {
2922     if (class_name.empty()) {
2923       return nullptr;
2924     }
2925 
2926     std::string descriptor;
2927     if (class_name[0] == 'L') {
2928       descriptor = class_name;
2929     } else {
2930       descriptor = DotToDescriptor(class_name.c_str());
2931     }
2932 
2933     ObjPtr<mirror::Class> klass = runtime->GetClassLinker()->FindClass(
2934         self, descriptor.c_str(), descriptor.length(), h_loader);
2935 
2936     if (klass == nullptr) {
2937       self->ClearException();
2938       std::cerr << "Did not find " <<  class_name << std::endl;
2939       *klass_out = nullptr;
2940       return nullptr;
2941     }
2942 
2943     StackHandleScope<1> scope(Thread::Current());
2944     Handle<mirror::Class> h_klass = scope.NewHandle<mirror::Class>(klass);
2945 
2946     ImTable* ret = PrepareAndGetImTable(runtime, h_klass, pointer_size, prepared);
2947     *klass_out = h_klass.Get();
2948     return ret;
2949   }
2950 
PrepareAndGetImTable(Runtime * runtime,Handle<mirror::Class> h_klass,const PointerSize pointer_size,std::unordered_set<std::string> * prepared)2951   static ImTable* PrepareAndGetImTable(Runtime* runtime,
2952                                        Handle<mirror::Class> h_klass,
2953                                        const PointerSize pointer_size,
2954                                        /*inout*/ std::unordered_set<std::string>* prepared)
2955       REQUIRES_SHARED(Locks::mutator_lock_) {
2956     PrepareClass(runtime, h_klass, prepared);
2957     return h_klass->GetImt(pointer_size);
2958   }
2959 
DumpIMTForClass(Runtime * runtime,const std::string & class_name,Handle<mirror::ClassLoader> h_loader,std::unordered_set<std::string> * prepared)2960   static void DumpIMTForClass(Runtime* runtime,
2961                               const std::string& class_name,
2962                               Handle<mirror::ClassLoader> h_loader,
2963                               std::unordered_set<std::string>* prepared)
2964       REQUIRES_SHARED(Locks::mutator_lock_) {
2965     const PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
2966     ObjPtr<mirror::Class> klass;
2967     ImTable* imt = PrepareAndGetImTable(runtime,
2968                                         Thread::Current(),
2969                                         h_loader,
2970                                         class_name,
2971                                         pointer_size,
2972                                         &klass,
2973                                         prepared);
2974     if (imt == nullptr) {
2975       return;
2976     }
2977 
2978     std::cerr << class_name << std::endl << " IMT:" << std::endl;
2979     for (size_t index = 0; index < ImTable::kSize; ++index) {
2980       std::cerr << "  " << index << ":" << std::endl;
2981       ArtMethod* ptr = imt->Get(index, pointer_size);
2982       if (ptr->IsRuntimeMethod()) {
2983         if (ptr->IsImtUnimplementedMethod()) {
2984           std::cerr << "    <empty>" << std::endl;
2985         } else {
2986           ImtConflictTable* current_table = ptr->GetImtConflictTable(pointer_size);
2987           PrintTable(current_table, pointer_size);
2988         }
2989       } else {
2990         std::cerr << "    " << ptr->PrettyMethod(true) << std::endl;
2991       }
2992     }
2993 
2994     std::cerr << " Interfaces:" << std::endl;
2995     // Run through iftable, find methods that slot here, see if they fit.
2996     ObjPtr<mirror::IfTable> if_table = klass->GetIfTable();
2997     for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
2998       ObjPtr<mirror::Class> iface = if_table->GetInterface(i);
2999       std::string iface_name;
3000       std::cerr << "  " << iface->GetDescriptor(&iface_name) << std::endl;
3001 
3002       for (ArtMethod& iface_method : iface->GetVirtualMethods(pointer_size)) {
3003         uint32_t class_hash, name_hash, signature_hash;
3004         ImTable::GetImtHashComponents(&iface_method, &class_hash, &name_hash, &signature_hash);
3005         uint32_t imt_slot = ImTable::GetImtIndex(&iface_method);
3006         std::cerr << "    " << iface_method.PrettyMethod(true)
3007             << " slot=" << imt_slot
3008             << std::hex
3009             << " class_hash=0x" << class_hash
3010             << " name_hash=0x" << name_hash
3011             << " signature_hash=0x" << signature_hash
3012             << std::dec
3013             << std::endl;
3014       }
3015     }
3016   }
3017 
DumpIMTForMethod(Runtime * runtime,const std::string & class_name,const std::string & method,Handle<mirror::ClassLoader> h_loader,std::unordered_set<std::string> * prepared)3018   static void DumpIMTForMethod(Runtime* runtime,
3019                                const std::string& class_name,
3020                                const std::string& method,
3021                                Handle<mirror::ClassLoader> h_loader,
3022                                /*inout*/ std::unordered_set<std::string>* prepared)
3023       REQUIRES_SHARED(Locks::mutator_lock_) {
3024     const PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
3025     ObjPtr<mirror::Class> klass;
3026     ImTable* imt = PrepareAndGetImTable(runtime,
3027                                         Thread::Current(),
3028                                         h_loader,
3029                                         class_name,
3030                                         pointer_size,
3031                                         &klass,
3032                                         prepared);
3033     if (imt == nullptr) {
3034       return;
3035     }
3036 
3037     std::cerr << class_name << " <" << method << ">" << std::endl;
3038     for (size_t index = 0; index < ImTable::kSize; ++index) {
3039       ArtMethod* ptr = imt->Get(index, pointer_size);
3040       if (ptr->IsRuntimeMethod()) {
3041         if (ptr->IsImtUnimplementedMethod()) {
3042           continue;
3043         }
3044 
3045         ImtConflictTable* current_table = ptr->GetImtConflictTable(pointer_size);
3046         if (current_table == nullptr) {
3047           continue;
3048         }
3049 
3050         size_t table_index = 0;
3051         for (;;) {
3052           ArtMethod* ptr2 = current_table->GetInterfaceMethod(table_index, pointer_size);
3053           if (ptr2 == nullptr) {
3054             break;
3055           }
3056           table_index++;
3057 
3058           std::string p_name = ptr2->PrettyMethod(true);
3059           if (p_name.starts_with(method)) {
3060             std::cerr << "  Slot "
3061                       << index
3062                       << " ("
3063                       << current_table->NumEntries(pointer_size)
3064                       << ")"
3065                       << std::endl;
3066             PrintTable(current_table, pointer_size);
3067             return;
3068           }
3069         }
3070       } else {
3071         std::string p_name = ptr->PrettyMethod(true);
3072         if (p_name.starts_with(method)) {
3073           std::cerr << "  Slot " << index << " (1)" << std::endl;
3074           std::cerr << "    " << p_name << std::endl;
3075         } else {
3076           // Run through iftable, find methods that slot here, see if they fit.
3077           ObjPtr<mirror::IfTable> if_table = klass->GetIfTable();
3078           for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
3079             ObjPtr<mirror::Class> iface = if_table->GetInterface(i);
3080             size_t num_methods = iface->NumDeclaredVirtualMethods();
3081             if (num_methods > 0) {
3082               for (ArtMethod& iface_method : iface->GetMethods(pointer_size)) {
3083                 if (ImTable::GetImtIndex(&iface_method) == index) {
3084                   std::string i_name = iface_method.PrettyMethod(true);
3085                   if (i_name.starts_with(method)) {
3086                     std::cerr << "  Slot " << index << " (1)" << std::endl;
3087                     std::cerr << "    " << p_name << " (" << i_name << ")" << std::endl;
3088                   }
3089                 }
3090               }
3091             }
3092           }
3093         }
3094       }
3095     }
3096   }
3097 
3098   // Read lines from the given stream, dropping comments and empty lines
ReadCommentedInputStream(std::istream & in_stream)3099   static std::vector<std::string> ReadCommentedInputStream(std::istream& in_stream) {
3100     std::vector<std::string> output;
3101     while (in_stream.good()) {
3102       std::string dot;
3103       std::getline(in_stream, dot);
3104       if (dot.starts_with("#") || dot.empty()) {
3105         continue;
3106       }
3107       output.push_back(dot);
3108     }
3109     return output;
3110   }
3111 
3112   // Read lines from the given file, dropping comments and empty lines.
ReadCommentedInputFromFile(const std::string & input_filename)3113   static std::vector<std::string> ReadCommentedInputFromFile(const std::string& input_filename) {
3114     std::unique_ptr<std::ifstream> input_file(new std::ifstream(input_filename, std::ifstream::in));
3115     if (input_file.get() == nullptr) {
3116       LOG(ERROR) << "Failed to open input file " << input_filename;
3117       return std::vector<std::string>();
3118     }
3119     std::vector<std::string> result = ReadCommentedInputStream(*input_file);
3120     input_file->close();
3121     return result;
3122   }
3123 
3124   // Prepare a class, i.e., ensure it has a filled IMT. Will do so recursively for superclasses,
3125   // and note in the given set that the work was done.
PrepareClass(Runtime * runtime,Handle<mirror::Class> h_klass,std::unordered_set<std::string> * done)3126   static void PrepareClass(Runtime* runtime,
3127                            Handle<mirror::Class> h_klass,
3128                            /*inout*/ std::unordered_set<std::string>* done)
3129       REQUIRES_SHARED(Locks::mutator_lock_) {
3130     if (!h_klass->ShouldHaveImt()) {
3131       return;
3132     }
3133 
3134     std::string name;
3135     name = h_klass->GetDescriptor(&name);
3136 
3137     if (done->find(name) != done->end()) {
3138       return;
3139     }
3140     done->insert(name);
3141 
3142     if (h_klass->HasSuperClass()) {
3143       StackHandleScope<1> h(Thread::Current());
3144       PrepareClass(runtime, h.NewHandle<mirror::Class>(h_klass->GetSuperClass()), done);
3145     }
3146 
3147     if (!h_klass->IsTemp()) {
3148       runtime->GetClassLinker()->FillIMTAndConflictTables(h_klass.Get());
3149     }
3150   }
3151 };
3152 
3153 enum class OatDumpMode {
3154   kSymbolize,
3155   kDumpImt,
3156   kDumpImage,
3157   kDumpOat,
3158 };
3159 
3160 struct OatdumpArgs : public CmdlineArgs {
3161  protected:
3162   using Base = CmdlineArgs;
3163 
ParseCustomart::OatdumpArgs3164   ParseStatus ParseCustom(const char* raw_option,
3165                           size_t raw_option_length,
3166                           std::string* error_msg) override {
3167     DCHECK_EQ(strlen(raw_option), raw_option_length);
3168     {
3169       ParseStatus base_parse = Base::ParseCustom(raw_option, raw_option_length, error_msg);
3170       if (base_parse != kParseUnknownArgument) {
3171         return base_parse;
3172       }
3173     }
3174 
3175     std::string_view option(raw_option, raw_option_length);
3176     if (option.starts_with("--oat-file=")) {
3177       oat_filename_ = raw_option + strlen("--oat-file=");
3178     } else if (option.starts_with("--dex-file=")) {
3179       dex_filename_ = raw_option + strlen("--dex-file=");
3180     } else if (option.starts_with("--image=")) {
3181       image_location_ = raw_option + strlen("--image=");
3182     } else if (option == "--no-dump:vmap") {
3183       dump_vmap_ = false;
3184     } else if (option =="--dump:code_info_stack_maps") {
3185       dump_code_info_stack_maps_ = true;
3186     } else if (option == "--no-disassemble") {
3187       disassemble_code_ = false;
3188     } else if (option =="--header-only") {
3189       dump_header_only_ = true;
3190     } else if (option.starts_with("--symbolize=")) {
3191       oat_filename_ = raw_option + strlen("--symbolize=");
3192       symbolize_ = true;
3193     } else if (option.starts_with("--only-keep-debug")) {
3194       only_keep_debug_ = true;
3195     } else if (option.starts_with("--class-filter=")) {
3196       class_filter_ = raw_option + strlen("--class-filter=");
3197     } else if (option.starts_with("--method-filter=")) {
3198       method_filter_ = raw_option + strlen("--method-filter=");
3199     } else if (option.starts_with("--list-classes")) {
3200       list_classes_ = true;
3201     } else if (option.starts_with("--list-methods")) {
3202       list_methods_ = true;
3203     } else if (option.starts_with("--export-dex-to=")) {
3204       export_dex_location_ = raw_option + strlen("--export-dex-to=");
3205     } else if (option.starts_with("--addr2instr=")) {
3206       if (!android::base::ParseUint(raw_option + strlen("--addr2instr="), &addr2instr_)) {
3207         *error_msg = "Address conversion failed";
3208         return kParseError;
3209       }
3210     } else if (option.starts_with("--app-image=")) {
3211       app_image_ = raw_option + strlen("--app-image=");
3212     } else if (option.starts_with("--app-oat=")) {
3213       app_oat_ = raw_option + strlen("--app-oat=");
3214     } else if (option.starts_with("--dump-imt=")) {
3215       imt_dump_ = std::string(option.substr(strlen("--dump-imt=")));
3216     } else if (option == "--dump-imt-stats") {
3217       imt_stat_dump_ = true;
3218     } else if (option == "--dump-method-and-offset-as-json") {
3219       dump_method_and_offset_as_json = true;
3220     } else {
3221       return kParseUnknownArgument;
3222     }
3223 
3224     return kParseOk;
3225   }
3226 
ParseChecksart::OatdumpArgs3227   ParseStatus ParseChecks(std::string* error_msg) override {
3228     if (image_location_ != nullptr) {
3229       if (!boot_image_locations_.empty()) {
3230         std::cerr << "Warning: Invalid combination of --boot-image and --image\n";
3231         std::cerr << "Use --image alone to dump boot image(s)\n";
3232         std::cerr << "Ignoring --boot-image\n";
3233         std::cerr << "\n";
3234         boot_image_locations_.clear();
3235       }
3236       Split(image_location_, ':', &boot_image_locations_);
3237     }
3238 
3239     // Perform the parent checks.
3240     ParseStatus parent_checks = Base::ParseChecks(error_msg);
3241     if (parent_checks != kParseOk) {
3242       return parent_checks;
3243     }
3244 
3245     // Perform our own checks.
3246     if (image_location_ == nullptr && app_image_ == nullptr && oat_filename_ == nullptr) {
3247       *error_msg = "Either --image, --app-image, --oat-file, or --symbolize must be specified";
3248       return kParseError;
3249     }
3250 
3251     if (app_image_ != nullptr && image_location_ != nullptr) {
3252       std::cerr << "Warning: Combining --app-image with --image is no longer supported\n";
3253       std::cerr << "Use --app-image alone to dump an app image, and optionally pass --boot-image "
3254                    "to specify the boot image that the app image is based on\n";
3255       std::cerr << "Use --image alone to dump boot image(s)\n";
3256       std::cerr << "Ignoring --image\n";
3257       std::cerr << "\n";
3258       image_location_ = nullptr;
3259     }
3260 
3261     if (image_location_ != nullptr && oat_filename_ != nullptr) {
3262       *error_msg =
3263           "--image and --oat-file must not be specified together\n"
3264           "Use --image alone to dump both boot image(s) and their oat file(s)\n"
3265           "Use --oat-file alone to dump an oat file";
3266       return kParseError;
3267     }
3268 
3269     if (app_oat_ != nullptr) {
3270       std::cerr << "Warning: --app-oat is deprecated. Use --oat-file instead\n";
3271       std::cerr << "\n";
3272       oat_filename_ = app_oat_;
3273     }
3274 
3275     if (boot_image_locations_.empty() && app_image_ != nullptr) {
3276       // At this point, boot image inference is impossible or has failed, and the user has been
3277       // warned about the failure.
3278       // When dumping an app image, we need at least one valid boot image, so we have to stop.
3279       // When dumping other things, we can continue to start the runtime in imageless mode.
3280       *error_msg = "--boot-image must be specified";
3281       return kParseError;
3282     }
3283 
3284     return kParseOk;
3285   }
3286 
GetUsageart::OatdumpArgs3287   std::string GetUsage() const override {
3288     std::string usage;
3289 
3290     usage += R"(
3291 Usage: oatdump [options] ...
3292 
3293 Examples:
3294 - Dump a primary boot image with its oat file.
3295     oatdump --image=/system/framework/boot.art
3296 
3297 - Dump a primary boot image and extension(s) with their oat files.
3298     oatdump --image=/system/framework/boot.art:/system/framework/boot-framework-adservices.art
3299 
3300 - Dump an app image with its oat file.
3301     oatdump --app-image=app.art --oat-file=app.odex [--dex-file=app.apk] [--boot-image=boot.art]
3302 
3303 - Dump an app oat file.
3304     oatdump --oat-file=app.odex [--dex-file=app.apk] [--boot-image=boot.art]
3305 
3306 - Dump IMT collisions. (See --dump-imt for details.)
3307     oatdump --oat-file=app.odex --dump-imt=imt.txt [--dex-file=app.apk] [--boot-image=boot.art]
3308         [--dump-imt-stats]
3309 
3310 - Symbolize an oat file. (See --symbolize for details.)
3311     oatdump --symbolize=app.odex [--dex-file=app.apk] [--only-keep-debug]
3312 
3313 Options:
3314   --oat-file=<file.oat>: dumps an oat file with the given filename.
3315       Example: --oat-file=/system/framework/arm64/boot.oat
3316 
3317   --image=<file.art>: dumps boot image(s) specified at the given location.
3318       Example: --image=/system/framework/boot.art
3319 
3320   --app-image=<file.art>: dumps an app image with the given filename.
3321       Must also have a specified app oat file (with --oat-file).
3322       Example: --app-image=app.art
3323 
3324   --app-oat=<file.odex>: deprecated. Use --oat-file instead.
3325 
3326 )";
3327 
3328     usage += Base::GetUsage();
3329 
3330     usage +=  // Optional.
3331         "  --no-dump:vmap may be used to disable vmap dumping.\n"
3332         "      Example: --no-dump:vmap\n"
3333         "\n"
3334         "  --dump:code_info_stack_maps enables dumping of stack maps in CodeInfo sections.\n"
3335         "      Example: --dump:code_info_stack_maps\n"
3336         "\n"
3337         "  --no-disassemble may be used to disable disassembly.\n"
3338         "      Example: --no-disassemble\n"
3339         "\n"
3340         "  --header-only may be used to print only the oat header.\n"
3341         "      Example: --header-only\n"
3342         "\n"
3343         "  --list-classes may be used to list target file classes (can be used with filters).\n"
3344         "      Example: --list-classes\n"
3345         "      Example: --list-classes --class-filter=com.example.foo\n"
3346         "\n"
3347         "  --list-methods may be used to list target file methods (can be used with filters).\n"
3348         "      Example: --list-methods\n"
3349         "      Example: --list-methods --class-filter=com.example --method-filter=foo\n"
3350         "\n"
3351         "  --symbolize=<file.oat>: output a copy of file.oat with elf symbols included.\n"
3352         "      Example: --symbolize=/system/framework/boot.oat\n"
3353         "\n"
3354         "  --only-keep-debug: modifies the behaviour of --symbolize so that\n"
3355         "      .rodata and .text sections are omitted in the output file to save space.\n"
3356         "      Example: --symbolize=/system/framework/boot.oat --only-keep-debug\n"
3357         "\n"
3358         "  --class-filter=<class name>: only dumps classes that contain the filter.\n"
3359         "      Example: --class-filter=com.example.foo\n"
3360         "\n"
3361         "  --method-filter=<method name>: only dumps methods that contain the filter.\n"
3362         "      Example: --method-filter=foo\n"
3363         "\n"
3364         "  --dump-method-and-offset-as-json: dumps fully qualified method names and\n"
3365         "                                    signatures ONLY, in a standard json format.\n"
3366         "      Example: --dump-method-and-offset-as-json\n"
3367         "\n"
3368         "  --export-dex-to=<directory>: may be used to export oat embedded dex files.\n"
3369         "      Example: --export-dex-to=/data/local/tmp\n"
3370         "\n"
3371         "  --addr2instr=<address>: output matching method disassembled code from relative\n"
3372         "                          address (e.g. PC from crash dump)\n"
3373         "      Example: --addr2instr=0x00001a3b\n"
3374         "\n"
3375         "  --dump-imt=<file.txt>: output IMT collisions (if any) for the given receiver\n"
3376         "                         types and interface methods in the given file. The file\n"
3377         "                         is read line-wise, where each line should either be a class\n"
3378         "                         name or descriptor, or a class name/descriptor and a prefix\n"
3379         "                         of a complete method name (separated by a whitespace).\n"
3380         "      Example: --dump-imt=imt.txt\n"
3381         "\n"
3382         "  --dump-imt-stats: modifies the behavior of --dump-imt to also output IMT statistics\n"
3383         "      for the boot image.\n"
3384         "      Example: --dump-imt-stats"
3385         "\n";
3386 
3387     return usage;
3388   }
3389 
3390  public:
GetModeart::OatdumpArgs3391   OatDumpMode GetMode() {
3392     // Keep the order of precedence for backward compatibility.
3393     if (symbolize_) {
3394       return OatDumpMode::kSymbolize;
3395     }
3396     if (!imt_dump_.empty()) {
3397       return OatDumpMode::kDumpImt;
3398     }
3399     if (image_location_ != nullptr || app_image_ != nullptr) {
3400       return OatDumpMode::kDumpImage;
3401     }
3402     CHECK_NE(oat_filename_, nullptr);
3403     return OatDumpMode::kDumpOat;
3404   }
3405 
3406   const char* oat_filename_ = nullptr;
3407   const char* dex_filename_ = nullptr;
3408   const char* class_filter_ = "";
3409   const char* method_filter_ = "";
3410   const char* image_location_ = nullptr;
3411   std::string elf_filename_prefix_;
3412   std::string imt_dump_;
3413   bool dump_vmap_ = true;
3414   bool dump_code_info_stack_maps_ = false;
3415   bool disassemble_code_ = true;
3416   bool symbolize_ = false;
3417   bool only_keep_debug_ = false;
3418   bool list_classes_ = false;
3419   bool list_methods_ = false;
3420   bool dump_header_only_ = false;
3421   bool imt_stat_dump_ = false;
3422   bool dump_method_and_offset_as_json = false;
3423   uint32_t addr2instr_ = 0;
3424   const char* export_dex_location_ = nullptr;
3425   const char* app_image_ = nullptr;
3426   const char* app_oat_ = nullptr;
3427 };
3428 
3429 struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
NeedsRuntimeart::OatdumpMain3430   bool NeedsRuntime() override {
3431     CHECK(args_ != nullptr);
3432 
3433     OatDumpMode mode = args_->GetMode();
3434 
3435     // Only enable absolute_addresses for image dumping.
3436     bool absolute_addresses = mode == OatDumpMode::kDumpImage;
3437 
3438     oat_dumper_options_.reset(new OatDumperOptions(args_->dump_vmap_,
3439                                                    args_->dump_code_info_stack_maps_,
3440                                                    args_->disassemble_code_,
3441                                                    absolute_addresses,
3442                                                    args_->class_filter_,
3443                                                    args_->method_filter_,
3444                                                    args_->list_classes_,
3445                                                    args_->list_methods_,
3446                                                    args_->dump_header_only_,
3447                                                    args_->dump_method_and_offset_as_json,
3448                                                    args_->export_dex_location_,
3449                                                    args_->app_image_,
3450                                                    args_->oat_filename_,
3451                                                    args_->dex_filename_,
3452                                                    args_->addr2instr_));
3453 
3454     switch (mode) {
3455       case OatDumpMode::kDumpImt:
3456       case OatDumpMode::kDumpImage:
3457         return true;
3458       case OatDumpMode::kSymbolize:
3459         return false;
3460       case OatDumpMode::kDumpOat:
3461         std::string error_msg;
3462         if (CanDumpWithRuntime(&error_msg)) {
3463           LOG(INFO) << "Dumping oat file with runtime";
3464           return true;
3465         } else {
3466           LOG(INFO) << ART_FORMAT("Cannot dump oat file with runtime: {}. Dumping without runtime",
3467                                   error_msg);
3468           return false;
3469         }
3470     }
3471   }
3472 
ExecuteWithoutRuntimeart::OatdumpMain3473   bool ExecuteWithoutRuntime() override {
3474     CHECK(args_ != nullptr);
3475 
3476     OatDumpMode mode = args_->GetMode();
3477     CHECK(mode == OatDumpMode::kSymbolize || mode == OatDumpMode::kDumpOat);
3478 
3479     MemMap::Init();
3480 
3481     if (mode == OatDumpMode::kSymbolize) {
3482       // ELF has special kind of section called SHT_NOBITS which allows us to create
3483       // sections which exist but their data is omitted from the ELF file to save space.
3484       // This is what "strip --only-keep-debug" does when it creates separate ELF file
3485       // with only debug data. We use it in similar way to exclude .rodata and .text.
3486       bool no_bits = args_->only_keep_debug_;
3487       return SymbolizeOat(
3488                  args_->oat_filename_, args_->dex_filename_, args_->output_name_, no_bits) ==
3489              EXIT_SUCCESS;
3490     }
3491 
3492     return DumpOat(nullptr, oat_dumper_options_.get(), args_->os_) == EXIT_SUCCESS;
3493   }
3494 
ExecuteWithRuntimeart::OatdumpMain3495   bool ExecuteWithRuntime(Runtime* runtime) override {
3496     CHECK(args_ != nullptr);
3497     OatDumpMode mode = args_->GetMode();
3498     CHECK(mode == OatDumpMode::kDumpImt || mode == OatDumpMode::kDumpImage ||
3499           mode == OatDumpMode::kDumpOat);
3500 
3501     if (mode == OatDumpMode::kDumpImt) {
3502       return IMTDumper::Dump(runtime,
3503                              args_->imt_dump_,
3504                              args_->imt_stat_dump_,
3505                              args_->oat_filename_,
3506                              args_->dex_filename_);
3507     }
3508 
3509     if (mode == OatDumpMode::kDumpOat) {
3510       return DumpOat(runtime, oat_dumper_options_.get(), args_->os_) == EXIT_SUCCESS;
3511     }
3512 
3513     return DumpImages(runtime, oat_dumper_options_.get(), args_->os_) == EXIT_SUCCESS;
3514   }
3515 
CanDumpWithRuntimeart::OatdumpMain3516   bool CanDumpWithRuntime(std::string* error_msg) {
3517     std::unique_ptr<OatFileAssistantContext> ofa_context =
3518         args_->GetOatFileAssistantContext(error_msg);
3519     if (ofa_context == nullptr) {
3520       return false;
3521     }
3522 
3523     std::unique_ptr<OatFile> oat_file =
3524         OpenOat(*oat_dumper_options_->oat_filename_, oat_dumper_options_->dex_filename_, error_msg);
3525     if (oat_file == nullptr) {
3526       *error_msg = ART_FORMAT(
3527           "Failed to open oat file from '{}': {}", *oat_dumper_options_->oat_filename_, *error_msg);
3528       return false;
3529     }
3530 
3531     const std::vector<const OatDexFile*>& dex_files = oat_file->GetOatDexFiles();
3532     if (dex_files.empty()) {
3533       // Dump header only. Don't need a runtime.
3534       *error_msg = "No dex code";
3535       return false;
3536     }
3537 
3538     OatFileAssistant oat_file_assistant(dex_files[0]->GetLocation().c_str(),
3539                                         args_->instruction_set_,
3540                                         /*context=*/nullptr,
3541                                         /*load_executable=*/false,
3542                                         /*only_load_trusted_executable=*/false,
3543                                         ofa_context.get());
3544 
3545     if (!oat_file_assistant.ValidateBootClassPathChecksums(*oat_file)) {
3546       *error_msg = "BCP checksum check failed";
3547       return false;
3548     }
3549 
3550     return true;
3551   }
3552 
3553   std::unique_ptr<OatDumperOptions> oat_dumper_options_;
3554 };
3555 
3556 }  // namespace art
3557 
main(int argc,char ** argv)3558 int main(int argc, char** argv) {
3559   // Output all logging to stderr.
3560   android::base::SetLogger(android::base::StderrLogger);
3561 
3562   art::OatdumpMain main;
3563   return main.Main(argc, argv);
3564 }
3565