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