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