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