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