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 <string>
23 #include <vector>
24
25 #include "base/stringpiece.h"
26 #include "base/unix_file/fd_file.h"
27 #include "class_linker.h"
28 #include "class_linker-inl.h"
29 #include "dex_file-inl.h"
30 #include "dex_instruction.h"
31 #include "disassembler.h"
32 #include "field_helper.h"
33 #include "gc_map.h"
34 #include "gc/space/image_space.h"
35 #include "gc/space/large_object_space.h"
36 #include "gc/space/space-inl.h"
37 #include "image.h"
38 #include "indenter.h"
39 #include "mapping_table.h"
40 #include "mirror/art_field-inl.h"
41 #include "mirror/art_method-inl.h"
42 #include "mirror/array-inl.h"
43 #include "mirror/class-inl.h"
44 #include "mirror/object-inl.h"
45 #include "mirror/object_array-inl.h"
46 #include "noop_compiler_callbacks.h"
47 #include "oat.h"
48 #include "oat_file-inl.h"
49 #include "os.h"
50 #include "runtime.h"
51 #include "safe_map.h"
52 #include "scoped_thread_state_change.h"
53 #include "thread_list.h"
54 #include "verifier/dex_gc_map.h"
55 #include "verifier/method_verifier.h"
56 #include "vmap_table.h"
57
58 namespace art {
59
usage()60 static void usage() {
61 fprintf(stderr,
62 "Usage: oatdump [options] ...\n"
63 " Example: oatdump --image=$ANDROID_PRODUCT_OUT/system/framework/boot.art\n"
64 " Example: adb shell oatdump --image=/system/framework/boot.art\n"
65 "\n");
66 fprintf(stderr,
67 " --oat-file=<file.oat>: specifies an input oat filename.\n"
68 " Example: --oat-file=/system/framework/boot.oat\n"
69 "\n");
70 fprintf(stderr,
71 " --image=<file.art>: specifies an input image filename.\n"
72 " Example: --image=/system/framework/boot.art\n"
73 "\n");
74 fprintf(stderr,
75 " --boot-image=<file.art>: provide the image file for the boot class path.\n"
76 " Example: --boot-image=/system/framework/boot.art\n"
77 "\n");
78 fprintf(stderr,
79 " --instruction-set=(arm|arm64|mips|x86|x86_64): for locating the image\n"
80 " file based on the image location set.\n"
81 " Example: --instruction-set=x86\n"
82 " Default: %s\n"
83 "\n",
84 GetInstructionSetString(kRuntimeISA));
85 fprintf(stderr,
86 " --output=<file> may be used to send the output to a file.\n"
87 " Example: --output=/tmp/oatdump.txt\n"
88 "\n");
89 fprintf(stderr,
90 " --dump:raw_mapping_table enables dumping of the mapping table.\n"
91 " Example: --dump:raw_mapping_table\n"
92 "\n");
93 fprintf(stderr,
94 " --dump:raw_mapping_table enables dumping of the GC map.\n"
95 " Example: --dump:raw_gc_map\n"
96 "\n");
97 fprintf(stderr,
98 " --no-dump:vmap may be used to disable vmap dumping.\n"
99 " Example: --no-dump:vmap\n"
100 "\n");
101 fprintf(stderr,
102 " --no-disassemble may be used to disable disassembly.\n"
103 " Example: --no-disassemble\n"
104 "\n");
105 exit(EXIT_FAILURE);
106 }
107
108 const char* image_roots_descriptions_[] = {
109 "kResolutionMethod",
110 "kImtConflictMethod",
111 "kImtUnimplementedMethod",
112 "kDefaultImt",
113 "kCalleeSaveMethod",
114 "kRefsOnlySaveMethod",
115 "kRefsAndArgsSaveMethod",
116 "kDexCaches",
117 "kClassRoots",
118 };
119
120 class OatDumperOptions {
121 public:
OatDumperOptions(bool dump_raw_mapping_table,bool dump_raw_gc_map,bool dump_vmap,bool disassemble_code,bool absolute_addresses)122 OatDumperOptions(bool dump_raw_mapping_table,
123 bool dump_raw_gc_map,
124 bool dump_vmap,
125 bool disassemble_code,
126 bool absolute_addresses)
127 : dump_raw_mapping_table_(dump_raw_mapping_table),
128 dump_raw_gc_map_(dump_raw_gc_map),
129 dump_vmap_(dump_vmap),
130 disassemble_code_(disassemble_code),
131 absolute_addresses_(absolute_addresses) {}
132
133 const bool dump_raw_mapping_table_;
134 const bool dump_raw_gc_map_;
135 const bool dump_vmap_;
136 const bool disassemble_code_;
137 const bool absolute_addresses_;
138 };
139
140 class OatDumper {
141 public:
OatDumper(const OatFile & oat_file,OatDumperOptions * options)142 explicit OatDumper(const OatFile& oat_file, OatDumperOptions* options)
143 : oat_file_(oat_file),
144 oat_dex_files_(oat_file.GetOatDexFiles()),
145 options_(options),
146 instruction_set_(oat_file_.GetOatHeader().GetInstructionSet()),
147 disassembler_(Disassembler::Create(instruction_set_,
148 new DisassemblerOptions(options_->absolute_addresses_,
149 oat_file.Begin()))) {
150 AddAllOffsets();
151 }
152
~OatDumper()153 ~OatDumper() {
154 delete options_;
155 delete disassembler_;
156 }
157
GetInstructionSet()158 InstructionSet GetInstructionSet() {
159 return instruction_set_;
160 }
161
Dump(std::ostream & os)162 bool Dump(std::ostream& os) {
163 bool success = true;
164 const OatHeader& oat_header = oat_file_.GetOatHeader();
165
166 os << "MAGIC:\n";
167 os << oat_header.GetMagic() << "\n\n";
168
169 os << "CHECKSUM:\n";
170 os << StringPrintf("0x%08x\n\n", oat_header.GetChecksum());
171
172 os << "INSTRUCTION SET:\n";
173 os << oat_header.GetInstructionSet() << "\n\n";
174
175 os << "INSTRUCTION SET FEATURES:\n";
176 os << oat_header.GetInstructionSetFeatures().GetFeatureString() << "\n\n";
177
178 os << "DEX FILE COUNT:\n";
179 os << oat_header.GetDexFileCount() << "\n\n";
180
181 #define DUMP_OAT_HEADER_OFFSET(label, offset) \
182 os << label " OFFSET:\n"; \
183 os << StringPrintf("0x%08x", oat_header.offset()); \
184 if (oat_header.offset() != 0 && options_->absolute_addresses_) { \
185 os << StringPrintf(" (%p)", oat_file_.Begin() + oat_header.offset()); \
186 } \
187 os << StringPrintf("\n\n");
188
189 DUMP_OAT_HEADER_OFFSET("EXECUTABLE", GetExecutableOffset);
190 DUMP_OAT_HEADER_OFFSET("INTERPRETER TO INTERPRETER BRIDGE",
191 GetInterpreterToInterpreterBridgeOffset);
192 DUMP_OAT_HEADER_OFFSET("INTERPRETER TO COMPILED CODE BRIDGE",
193 GetInterpreterToCompiledCodeBridgeOffset);
194 DUMP_OAT_HEADER_OFFSET("JNI DLSYM LOOKUP",
195 GetJniDlsymLookupOffset);
196 DUMP_OAT_HEADER_OFFSET("PORTABLE IMT CONFLICT TRAMPOLINE",
197 GetPortableImtConflictTrampolineOffset);
198 DUMP_OAT_HEADER_OFFSET("PORTABLE RESOLUTION TRAMPOLINE",
199 GetPortableResolutionTrampolineOffset);
200 DUMP_OAT_HEADER_OFFSET("PORTABLE TO INTERPRETER BRIDGE",
201 GetPortableToInterpreterBridgeOffset);
202 DUMP_OAT_HEADER_OFFSET("QUICK GENERIC JNI TRAMPOLINE",
203 GetQuickGenericJniTrampolineOffset);
204 DUMP_OAT_HEADER_OFFSET("QUICK IMT CONFLICT TRAMPOLINE",
205 GetQuickImtConflictTrampolineOffset);
206 DUMP_OAT_HEADER_OFFSET("QUICK RESOLUTION TRAMPOLINE",
207 GetQuickResolutionTrampolineOffset);
208 DUMP_OAT_HEADER_OFFSET("QUICK TO INTERPRETER BRIDGE",
209 GetQuickToInterpreterBridgeOffset);
210 #undef DUMP_OAT_HEADER_OFFSET
211
212 os << "IMAGE PATCH DELTA:\n";
213 os << StringPrintf("%d (0x%08x)\n\n",
214 oat_header.GetImagePatchDelta(),
215 oat_header.GetImagePatchDelta());
216
217 os << "IMAGE FILE LOCATION OAT CHECKSUM:\n";
218 os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum());
219
220 os << "IMAGE FILE LOCATION OAT BEGIN:\n";
221 os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatDataBegin());
222
223 // Print the key-value store.
224 {
225 os << "KEY VALUE STORE:\n";
226 size_t index = 0;
227 const char* key;
228 const char* value;
229 while (oat_header.GetStoreKeyValuePairByIndex(index, &key, &value)) {
230 os << key << " = " << value << "\n";
231 index++;
232 }
233 os << "\n";
234 }
235
236 if (options_->absolute_addresses_) {
237 os << "BEGIN:\n";
238 os << reinterpret_cast<const void*>(oat_file_.Begin()) << "\n\n";
239
240 os << "END:\n";
241 os << reinterpret_cast<const void*>(oat_file_.End()) << "\n\n";
242 }
243
244 os << "SIZE:\n";
245 os << oat_file_.Size() << "\n\n";
246
247 os << std::flush;
248
249 for (size_t i = 0; i < oat_dex_files_.size(); i++) {
250 const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
251 CHECK(oat_dex_file != nullptr);
252 if (!DumpOatDexFile(os, *oat_dex_file)) {
253 success = false;
254 }
255 }
256 os << std::flush;
257 return success;
258 }
259
ComputeSize(const void * oat_data)260 size_t ComputeSize(const void* oat_data) {
261 if (reinterpret_cast<const byte*>(oat_data) < oat_file_.Begin() ||
262 reinterpret_cast<const byte*>(oat_data) > oat_file_.End()) {
263 return 0; // Address not in oat file
264 }
265 uintptr_t begin_offset = reinterpret_cast<uintptr_t>(oat_data) -
266 reinterpret_cast<uintptr_t>(oat_file_.Begin());
267 auto it = offsets_.upper_bound(begin_offset);
268 CHECK(it != offsets_.end());
269 uintptr_t end_offset = *it;
270 return end_offset - begin_offset;
271 }
272
GetOatInstructionSet()273 InstructionSet GetOatInstructionSet() {
274 return oat_file_.GetOatHeader().GetInstructionSet();
275 }
276
GetQuickOatCode(mirror::ArtMethod * m)277 const void* GetQuickOatCode(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
278 for (size_t i = 0; i < oat_dex_files_.size(); i++) {
279 const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
280 CHECK(oat_dex_file != nullptr);
281 std::string error_msg;
282 std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(&error_msg));
283 if (dex_file.get() == nullptr) {
284 LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
285 << "': " << error_msg;
286 } else {
287 const char* descriptor = m->GetDeclaringClassDescriptor();
288 const DexFile::ClassDef* class_def =
289 dex_file->FindClassDef(descriptor, ComputeModifiedUtf8Hash(descriptor));
290 if (class_def != nullptr) {
291 uint16_t class_def_index = dex_file->GetIndexForClassDef(*class_def);
292 const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
293 size_t method_index = m->GetMethodIndex();
294 return oat_class.GetOatMethod(method_index).GetQuickCode();
295 }
296 }
297 }
298 return nullptr;
299 }
300
301 private:
AddAllOffsets()302 void AddAllOffsets() {
303 // We don't know the length of the code for each method, but we need to know where to stop
304 // when disassembling. What we do know is that a region of code will be followed by some other
305 // region, so if we keep a sorted sequence of the start of each region, we can infer the length
306 // of a piece of code by using upper_bound to find the start of the next region.
307 for (size_t i = 0; i < oat_dex_files_.size(); i++) {
308 const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
309 CHECK(oat_dex_file != nullptr);
310 std::string error_msg;
311 std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(&error_msg));
312 if (dex_file.get() == nullptr) {
313 LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
314 << "': " << error_msg;
315 continue;
316 }
317 offsets_.insert(reinterpret_cast<uintptr_t>(&dex_file->GetHeader()));
318 for (size_t class_def_index = 0;
319 class_def_index < dex_file->NumClassDefs();
320 class_def_index++) {
321 const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
322 const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
323 const byte* class_data = dex_file->GetClassData(class_def);
324 if (class_data != nullptr) {
325 ClassDataItemIterator it(*dex_file, class_data);
326 SkipAllFields(it);
327 uint32_t class_method_index = 0;
328 while (it.HasNextDirectMethod()) {
329 AddOffsets(oat_class.GetOatMethod(class_method_index++));
330 it.Next();
331 }
332 while (it.HasNextVirtualMethod()) {
333 AddOffsets(oat_class.GetOatMethod(class_method_index++));
334 it.Next();
335 }
336 }
337 }
338 }
339
340 // If the last thing in the file is code for a method, there won't be an offset for the "next"
341 // thing. Instead of having a special case in the upper_bound code, let's just add an entry
342 // for the end of the file.
343 offsets_.insert(oat_file_.Size());
344 }
345
AlignCodeOffset(uint32_t maybe_thumb_offset)346 static uint32_t AlignCodeOffset(uint32_t maybe_thumb_offset) {
347 return maybe_thumb_offset & ~0x1; // TODO: Make this Thumb2 specific.
348 }
349
AddOffsets(const OatFile::OatMethod & oat_method)350 void AddOffsets(const OatFile::OatMethod& oat_method) {
351 uint32_t code_offset = oat_method.GetCodeOffset();
352 if (oat_file_.GetOatHeader().GetInstructionSet() == kThumb2) {
353 code_offset &= ~0x1;
354 }
355 offsets_.insert(code_offset);
356 offsets_.insert(oat_method.GetMappingTableOffset());
357 offsets_.insert(oat_method.GetVmapTableOffset());
358 offsets_.insert(oat_method.GetGcMapOffset());
359 }
360
DumpOatDexFile(std::ostream & os,const OatFile::OatDexFile & oat_dex_file)361 bool DumpOatDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) {
362 bool success = true;
363 os << "OatDexFile:\n";
364 os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str());
365 os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum());
366
367 // Create the verifier early.
368
369 std::string error_msg;
370 std::unique_ptr<const DexFile> dex_file(oat_dex_file.OpenDexFile(&error_msg));
371 if (dex_file.get() == nullptr) {
372 os << "NOT FOUND: " << error_msg << "\n\n";
373 os << std::flush;
374 return false;
375 }
376 for (size_t class_def_index = 0;
377 class_def_index < dex_file->NumClassDefs();
378 class_def_index++) {
379 const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
380 const char* descriptor = dex_file->GetClassDescriptor(class_def);
381 uint32_t oat_class_offset = oat_dex_file.GetOatClassOffset(class_def_index);
382 const OatFile::OatClass oat_class = oat_dex_file.GetOatClass(class_def_index);
383 os << StringPrintf("%zd: %s (offset=0x%08x) (type_idx=%d)",
384 class_def_index, descriptor, oat_class_offset, class_def.class_idx_)
385 << " (" << oat_class.GetStatus() << ")"
386 << " (" << oat_class.GetType() << ")\n";
387 // TODO: include bitmap here if type is kOatClassSomeCompiled?
388 Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
389 std::ostream indented_os(&indent_filter);
390 if (!DumpOatClass(indented_os, oat_class, *(dex_file.get()), class_def)) {
391 success = false;
392 }
393 }
394
395 os << std::flush;
396 return success;
397 }
398
SkipAllFields(ClassDataItemIterator & it)399 static void SkipAllFields(ClassDataItemIterator& it) {
400 while (it.HasNextStaticField()) {
401 it.Next();
402 }
403 while (it.HasNextInstanceField()) {
404 it.Next();
405 }
406 }
407
DumpOatClass(std::ostream & os,const OatFile::OatClass & oat_class,const DexFile & dex_file,const DexFile::ClassDef & class_def)408 bool DumpOatClass(std::ostream& os, const OatFile::OatClass& oat_class, const DexFile& dex_file,
409 const DexFile::ClassDef& class_def) {
410 bool success = true;
411 const byte* class_data = dex_file.GetClassData(class_def);
412 if (class_data == nullptr) { // empty class such as a marker interface?
413 os << std::flush;
414 return success;
415 }
416 ClassDataItemIterator it(dex_file, class_data);
417 SkipAllFields(it);
418 uint32_t class_method_index = 0;
419 while (it.HasNextDirectMethod()) {
420 if (!DumpOatMethod(os, class_def, class_method_index, oat_class, dex_file,
421 it.GetMemberIndex(), it.GetMethodCodeItem(),
422 it.GetRawMemberAccessFlags())) {
423 success = false;
424 }
425 class_method_index++;
426 it.Next();
427 }
428 while (it.HasNextVirtualMethod()) {
429 if (!DumpOatMethod(os, class_def, class_method_index, oat_class, dex_file,
430 it.GetMemberIndex(), it.GetMethodCodeItem(),
431 it.GetRawMemberAccessFlags())) {
432 success = false;
433 }
434 class_method_index++;
435 it.Next();
436 }
437 DCHECK(!it.HasNext());
438 os << std::flush;
439 return success;
440 }
441
442 static constexpr uint32_t kPrologueBytes = 16;
443
444 // When this was picked, the largest arm method was 55,256 bytes and arm64 was 50,412 bytes.
445 static constexpr uint32_t kMaxCodeSize = 100 * 1000;
446
DumpOatMethod(std::ostream & os,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)447 bool DumpOatMethod(std::ostream& os, const DexFile::ClassDef& class_def,
448 uint32_t class_method_index,
449 const OatFile::OatClass& oat_class, const DexFile& dex_file,
450 uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
451 uint32_t method_access_flags) {
452 bool success = true;
453 os << StringPrintf("%d: %s (dex_method_idx=%d)\n",
454 class_method_index, PrettyMethod(dex_method_idx, dex_file, true).c_str(),
455 dex_method_idx);
456 Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
457 std::unique_ptr<std::ostream> indent1_os(new std::ostream(&indent1_filter));
458 Indenter indent2_filter(indent1_os->rdbuf(), kIndentChar, kIndentBy1Count);
459 std::unique_ptr<std::ostream> indent2_os(new std::ostream(&indent2_filter));
460 {
461 *indent1_os << "DEX CODE:\n";
462 DumpDexCode(*indent2_os, dex_file, code_item);
463 }
464
465 std::unique_ptr<verifier::MethodVerifier> verifier;
466 if (Runtime::Current() != nullptr) {
467 *indent1_os << "VERIFIER TYPE ANALYSIS:\n";
468 verifier.reset(DumpVerifier(*indent2_os, dex_method_idx, &dex_file, class_def, code_item,
469 method_access_flags));
470 }
471
472 uint32_t oat_method_offsets_offset = oat_class.GetOatMethodOffsetsOffset(class_method_index);
473 const OatMethodOffsets* oat_method_offsets = oat_class.GetOatMethodOffsets(class_method_index);
474 const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
475 {
476 *indent1_os << "OatMethodOffsets ";
477 if (options_->absolute_addresses_) {
478 *indent1_os << StringPrintf("%p ", oat_method_offsets);
479 }
480 *indent1_os << StringPrintf("(offset=0x%08x)\n", oat_method_offsets_offset);
481 if (oat_method_offsets_offset > oat_file_.Size()) {
482 *indent1_os << StringPrintf(
483 "WARNING: oat method offsets offset 0x%08x is past end of file 0x%08zx.\n",
484 oat_method_offsets_offset, oat_file_.Size());
485 // If we can't read OatMethodOffsets, the rest of the data is dangerous to read.
486 os << std::flush;
487 return false;
488 }
489
490 uint32_t code_offset = oat_method.GetCodeOffset();
491 *indent2_os << StringPrintf("code_offset: 0x%08x ", code_offset);
492 uint32_t aligned_code_begin = AlignCodeOffset(oat_method.GetCodeOffset());
493 if (aligned_code_begin > oat_file_.Size()) {
494 *indent2_os << StringPrintf("WARNING: "
495 "code offset 0x%08x is past end of file 0x%08zx.\n",
496 aligned_code_begin, oat_file_.Size());
497 success = false;
498 }
499 *indent2_os << "\n";
500
501 *indent2_os << "gc_map: ";
502 if (options_->absolute_addresses_) {
503 *indent2_os << StringPrintf("%p ", oat_method.GetGcMap());
504 }
505 uint32_t gc_map_offset = oat_method.GetGcMapOffset();
506 *indent2_os << StringPrintf("(offset=0x%08x)\n", gc_map_offset);
507 if (gc_map_offset > oat_file_.Size()) {
508 *indent2_os << StringPrintf("WARNING: "
509 "gc map table offset 0x%08x is past end of file 0x%08zx.\n",
510 gc_map_offset, oat_file_.Size());
511 success = false;
512 } else if (options_->dump_raw_gc_map_) {
513 Indenter indent3_filter(indent2_os->rdbuf(), kIndentChar, kIndentBy1Count);
514 std::ostream indent3_os(&indent3_filter);
515 DumpGcMap(indent3_os, oat_method, code_item);
516 }
517 }
518 {
519 *indent1_os << "OatQuickMethodHeader ";
520 uint32_t method_header_offset = oat_method.GetOatQuickMethodHeaderOffset();
521 const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader();
522
523 if (options_->absolute_addresses_) {
524 *indent1_os << StringPrintf("%p ", method_header);
525 }
526 *indent1_os << StringPrintf("(offset=0x%08x)\n", method_header_offset);
527 if (method_header_offset > oat_file_.Size()) {
528 *indent1_os << StringPrintf(
529 "WARNING: oat quick method header offset 0x%08x is past end of file 0x%08zx.\n",
530 method_header_offset, oat_file_.Size());
531 // If we can't read the OatQuickMethodHeader, the rest of the data is dangerous to read.
532 os << std::flush;
533 return false;
534 }
535
536 *indent2_os << "mapping_table: ";
537 if (options_->absolute_addresses_) {
538 *indent2_os << StringPrintf("%p ", oat_method.GetMappingTable());
539 }
540 uint32_t mapping_table_offset = oat_method.GetMappingTableOffset();
541 *indent2_os << StringPrintf("(offset=0x%08x)\n", oat_method.GetMappingTableOffset());
542 if (mapping_table_offset > oat_file_.Size()) {
543 *indent2_os << StringPrintf("WARNING: "
544 "mapping table offset 0x%08x is past end of file 0x%08zx. "
545 "mapping table offset was loaded from offset 0x%08x.\n",
546 mapping_table_offset, oat_file_.Size(),
547 oat_method.GetMappingTableOffsetOffset());
548 success = false;
549 } else if (options_->dump_raw_mapping_table_) {
550 Indenter indent3_filter(indent2_os->rdbuf(), kIndentChar, kIndentBy1Count);
551 std::ostream indent3_os(&indent3_filter);
552 DumpMappingTable(indent3_os, oat_method);
553 }
554
555 *indent2_os << "vmap_table: ";
556 if (options_->absolute_addresses_) {
557 *indent2_os << StringPrintf("%p ", oat_method.GetVmapTable());
558 }
559 uint32_t vmap_table_offset = oat_method.GetVmapTableOffset();
560 *indent2_os << StringPrintf("(offset=0x%08x)\n", vmap_table_offset);
561 if (vmap_table_offset > oat_file_.Size()) {
562 *indent2_os << StringPrintf("WARNING: "
563 "vmap table offset 0x%08x is past end of file 0x%08zx. "
564 "vmap table offset was loaded from offset 0x%08x.\n",
565 vmap_table_offset, oat_file_.Size(),
566 oat_method.GetVmapTableOffsetOffset());
567 success = false;
568 } else if (options_->dump_vmap_) {
569 DumpVmap(*indent2_os, oat_method);
570 }
571 }
572 {
573 *indent1_os << "QuickMethodFrameInfo\n";
574
575 *indent2_os << StringPrintf("frame_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes());
576 *indent2_os << StringPrintf("core_spill_mask: 0x%08x ", oat_method.GetCoreSpillMask());
577 DumpSpillMask(*indent2_os, oat_method.GetCoreSpillMask(), false);
578 *indent2_os << "\n";
579 *indent2_os << StringPrintf("fp_spill_mask: 0x%08x ", oat_method.GetFpSpillMask());
580 DumpSpillMask(*indent2_os, oat_method.GetFpSpillMask(), true);
581 *indent2_os << "\n";
582 }
583 {
584 *indent1_os << "CODE: ";
585 uint32_t code_size_offset = oat_method.GetQuickCodeSizeOffset();
586 if (code_size_offset > oat_file_.Size()) {
587 *indent2_os << StringPrintf("WARNING: "
588 "code size offset 0x%08x is past end of file 0x%08zx.",
589 code_size_offset, oat_file_.Size());
590 success = false;
591 } else {
592 const void* code = oat_method.GetQuickCode();
593 uint32_t code_size = oat_method.GetQuickCodeSize();
594 if (code == nullptr) {
595 code = oat_method.GetPortableCode();
596 code_size = oat_method.GetPortableCodeSize();
597 code_size_offset = 0;
598 }
599 uint32_t code_offset = oat_method.GetCodeOffset();
600 uint32_t aligned_code_begin = AlignCodeOffset(code_offset);
601 uint64_t aligned_code_end = aligned_code_begin + code_size;
602
603 if (options_->absolute_addresses_) {
604 *indent1_os << StringPrintf("%p ", code);
605 }
606 *indent1_os << StringPrintf("(code_offset=0x%08x size_offset=0x%08x size=%u)%s\n",
607 code_offset,
608 code_size_offset,
609 code_size,
610 code != nullptr ? "..." : "");
611
612 if (aligned_code_begin > oat_file_.Size()) {
613 *indent2_os << StringPrintf("WARNING: "
614 "start of code at 0x%08x is past end of file 0x%08zx.",
615 aligned_code_begin, oat_file_.Size());
616 success = false;
617 } else if (aligned_code_end > oat_file_.Size()) {
618 *indent2_os << StringPrintf("WARNING: "
619 "end of code at 0x%08" PRIx64 " is past end of file 0x%08zx. "
620 "code size is 0x%08x loaded from offset 0x%08x.\n",
621 aligned_code_end, oat_file_.Size(),
622 code_size, code_size_offset);
623 success = false;
624 if (options_->disassemble_code_) {
625 if (code_size_offset + kPrologueBytes <= oat_file_.Size()) {
626 DumpCode(*indent2_os, verifier.get(), oat_method, code_item, true, kPrologueBytes);
627 }
628 }
629 } else if (code_size > kMaxCodeSize) {
630 *indent2_os << StringPrintf("WARNING: "
631 "code size %d is bigger than max expected threshold of %d. "
632 "code size is 0x%08x loaded from offset 0x%08x.\n",
633 code_size, kMaxCodeSize,
634 code_size, code_size_offset);
635 success = false;
636 if (options_->disassemble_code_) {
637 if (code_size_offset + kPrologueBytes <= oat_file_.Size()) {
638 DumpCode(*indent2_os, verifier.get(), oat_method, code_item, true, kPrologueBytes);
639 }
640 }
641 } else if (options_->disassemble_code_) {
642 DumpCode(*indent2_os, verifier.get(), oat_method, code_item, !success, 0);
643 }
644 }
645 }
646 os << std::flush;
647 return success;
648 }
649
DumpSpillMask(std::ostream & os,uint32_t spill_mask,bool is_float)650 void DumpSpillMask(std::ostream& os, uint32_t spill_mask, bool is_float) {
651 if (spill_mask == 0) {
652 return;
653 }
654 os << "(";
655 for (size_t i = 0; i < 32; i++) {
656 if ((spill_mask & (1 << i)) != 0) {
657 if (is_float) {
658 os << "fr" << i;
659 } else {
660 os << "r" << i;
661 }
662 spill_mask ^= 1 << i; // clear bit
663 if (spill_mask != 0) {
664 os << ", ";
665 } else {
666 break;
667 }
668 }
669 }
670 os << ")";
671 }
672
DumpVmap(std::ostream & os,const OatFile::OatMethod & oat_method)673 void DumpVmap(std::ostream& os, const OatFile::OatMethod& oat_method) {
674 const uint8_t* raw_table = oat_method.GetVmapTable();
675 if (raw_table != nullptr) {
676 const VmapTable vmap_table(raw_table);
677 bool first = true;
678 bool processing_fp = false;
679 uint32_t spill_mask = oat_method.GetCoreSpillMask();
680 for (size_t i = 0; i < vmap_table.Size(); i++) {
681 uint16_t dex_reg = vmap_table[i];
682 uint32_t cpu_reg = vmap_table.ComputeRegister(spill_mask, i,
683 processing_fp ? kFloatVReg : kIntVReg);
684 os << (first ? "v" : ", v") << dex_reg;
685 if (!processing_fp) {
686 os << "/r" << cpu_reg;
687 } else {
688 os << "/fr" << cpu_reg;
689 }
690 first = false;
691 if (!processing_fp && dex_reg == 0xFFFF) {
692 processing_fp = true;
693 spill_mask = oat_method.GetFpSpillMask();
694 }
695 }
696 os << "\n";
697 }
698 }
699
DescribeVReg(std::ostream & os,const OatFile::OatMethod & oat_method,const DexFile::CodeItem * code_item,size_t reg,VRegKind kind)700 void DescribeVReg(std::ostream& os, const OatFile::OatMethod& oat_method,
701 const DexFile::CodeItem* code_item, size_t reg, VRegKind kind) {
702 const uint8_t* raw_table = oat_method.GetVmapTable();
703 if (raw_table != nullptr) {
704 const VmapTable vmap_table(raw_table);
705 uint32_t vmap_offset;
706 if (vmap_table.IsInContext(reg, kind, &vmap_offset)) {
707 bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
708 uint32_t spill_mask = is_float ? oat_method.GetFpSpillMask()
709 : oat_method.GetCoreSpillMask();
710 os << (is_float ? "fr" : "r") << vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
711 } else {
712 uint32_t offset = StackVisitor::GetVRegOffset(code_item, oat_method.GetCoreSpillMask(),
713 oat_method.GetFpSpillMask(),
714 oat_method.GetFrameSizeInBytes(), reg,
715 GetInstructionSet());
716 os << "[sp + #" << offset << "]";
717 }
718 }
719 }
720
DumpGcMapRegisters(std::ostream & os,const OatFile::OatMethod & oat_method,const DexFile::CodeItem * code_item,size_t num_regs,const uint8_t * reg_bitmap)721 void DumpGcMapRegisters(std::ostream& os, const OatFile::OatMethod& oat_method,
722 const DexFile::CodeItem* code_item,
723 size_t num_regs, const uint8_t* reg_bitmap) {
724 bool first = true;
725 for (size_t reg = 0; reg < num_regs; reg++) {
726 if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) {
727 if (first) {
728 os << " v" << reg << " (";
729 DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
730 os << ")";
731 first = false;
732 } else {
733 os << ", v" << reg << " (";
734 DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
735 os << ")";
736 }
737 }
738 }
739 if (first) {
740 os << "No registers in GC map\n";
741 } else {
742 os << "\n";
743 }
744 }
DumpGcMap(std::ostream & os,const OatFile::OatMethod & oat_method,const DexFile::CodeItem * code_item)745 void DumpGcMap(std::ostream& os, const OatFile::OatMethod& oat_method,
746 const DexFile::CodeItem* code_item) {
747 const uint8_t* gc_map_raw = oat_method.GetGcMap();
748 if (gc_map_raw == nullptr) {
749 return; // No GC map.
750 }
751 const void* quick_code = oat_method.GetQuickCode();
752 if (quick_code != nullptr) {
753 NativePcOffsetToReferenceMap map(gc_map_raw);
754 for (size_t entry = 0; entry < map.NumEntries(); entry++) {
755 const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(quick_code) +
756 map.GetNativePcOffset(entry);
757 os << StringPrintf("%p", native_pc);
758 DumpGcMapRegisters(os, oat_method, code_item, map.RegWidth() * 8, map.GetBitMap(entry));
759 }
760 } else {
761 const void* portable_code = oat_method.GetPortableCode();
762 CHECK(portable_code != nullptr);
763 verifier::DexPcToReferenceMap map(gc_map_raw);
764 for (size_t entry = 0; entry < map.NumEntries(); entry++) {
765 uint32_t dex_pc = map.GetDexPc(entry);
766 os << StringPrintf("0x%08x", dex_pc);
767 DumpGcMapRegisters(os, oat_method, code_item, map.RegWidth() * 8, map.GetBitMap(entry));
768 }
769 }
770 }
771
DumpMappingTable(std::ostream & os,const OatFile::OatMethod & oat_method)772 void DumpMappingTable(std::ostream& os, const OatFile::OatMethod& oat_method) {
773 const void* quick_code = oat_method.GetQuickCode();
774 if (quick_code == nullptr) {
775 return;
776 }
777 MappingTable table(oat_method.GetMappingTable());
778 if (table.TotalSize() != 0) {
779 Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
780 std::ostream indent_os(&indent_filter);
781 if (table.PcToDexSize() != 0) {
782 typedef MappingTable::PcToDexIterator It;
783 os << "suspend point mappings {\n";
784 for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
785 indent_os << StringPrintf("0x%04x -> 0x%04x\n", cur.NativePcOffset(), cur.DexPc());
786 }
787 os << "}\n";
788 }
789 if (table.DexToPcSize() != 0) {
790 typedef MappingTable::DexToPcIterator It;
791 os << "catch entry mappings {\n";
792 for (It cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) {
793 indent_os << StringPrintf("0x%04x -> 0x%04x\n", cur.NativePcOffset(), cur.DexPc());
794 }
795 os << "}\n";
796 }
797 }
798 }
799
DumpMappingAtOffset(std::ostream & os,const OatFile::OatMethod & oat_method,size_t offset,bool suspend_point_mapping)800 uint32_t DumpMappingAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method,
801 size_t offset, bool suspend_point_mapping) {
802 MappingTable table(oat_method.GetMappingTable());
803 if (suspend_point_mapping && table.PcToDexSize() > 0) {
804 typedef MappingTable::PcToDexIterator It;
805 for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
806 if (offset == cur.NativePcOffset()) {
807 os << StringPrintf("suspend point dex PC: 0x%04x\n", cur.DexPc());
808 return cur.DexPc();
809 }
810 }
811 } else if (!suspend_point_mapping && table.DexToPcSize() > 0) {
812 typedef MappingTable::DexToPcIterator It;
813 for (It cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) {
814 if (offset == cur.NativePcOffset()) {
815 os << StringPrintf("catch entry dex PC: 0x%04x\n", cur.DexPc());
816 return cur.DexPc();
817 }
818 }
819 }
820 return DexFile::kDexNoIndex;
821 }
822
DumpGcMapAtNativePcOffset(std::ostream & os,const OatFile::OatMethod & oat_method,const DexFile::CodeItem * code_item,size_t native_pc_offset)823 void DumpGcMapAtNativePcOffset(std::ostream& os, const OatFile::OatMethod& oat_method,
824 const DexFile::CodeItem* code_item, size_t native_pc_offset) {
825 const uint8_t* gc_map_raw = oat_method.GetGcMap();
826 if (gc_map_raw != nullptr) {
827 NativePcOffsetToReferenceMap map(gc_map_raw);
828 if (map.HasEntry(native_pc_offset)) {
829 size_t num_regs = map.RegWidth() * 8;
830 const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
831 bool first = true;
832 for (size_t reg = 0; reg < num_regs; reg++) {
833 if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) {
834 if (first) {
835 os << "GC map objects: v" << reg << " (";
836 DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
837 os << ")";
838 first = false;
839 } else {
840 os << ", v" << reg << " (";
841 DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
842 os << ")";
843 }
844 }
845 }
846 if (!first) {
847 os << "\n";
848 }
849 }
850 }
851 }
852
DumpVRegsAtDexPc(std::ostream & os,verifier::MethodVerifier * verifier,const OatFile::OatMethod & oat_method,const DexFile::CodeItem * code_item,uint32_t dex_pc)853 void DumpVRegsAtDexPc(std::ostream& os, verifier::MethodVerifier* verifier,
854 const OatFile::OatMethod& oat_method,
855 const DexFile::CodeItem* code_item, uint32_t dex_pc) {
856 DCHECK(verifier != nullptr);
857 std::vector<int32_t> kinds = verifier->DescribeVRegs(dex_pc);
858 bool first = true;
859 for (size_t reg = 0; reg < code_item->registers_size_; reg++) {
860 VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2));
861 if (kind != kUndefined) {
862 if (first) {
863 os << "VRegs: v";
864 first = false;
865 } else {
866 os << ", v";
867 }
868 os << reg << " (";
869 switch (kind) {
870 case kImpreciseConstant:
871 os << "Imprecise Constant: " << kinds.at((reg * 2) + 1) << ", ";
872 DescribeVReg(os, oat_method, code_item, reg, kind);
873 break;
874 case kConstant:
875 os << "Constant: " << kinds.at((reg * 2) + 1);
876 break;
877 default:
878 DescribeVReg(os, oat_method, code_item, reg, kind);
879 break;
880 }
881 os << ")";
882 }
883 }
884 if (!first) {
885 os << "\n";
886 }
887 }
888
889
DumpDexCode(std::ostream & os,const DexFile & dex_file,const DexFile::CodeItem * code_item)890 void DumpDexCode(std::ostream& os, const DexFile& dex_file, const DexFile::CodeItem* code_item) {
891 if (code_item != nullptr) {
892 size_t i = 0;
893 while (i < code_item->insns_size_in_code_units_) {
894 const Instruction* instruction = Instruction::At(&code_item->insns_[i]);
895 os << StringPrintf("0x%04zx: %s\n", i, instruction->DumpString(&dex_file).c_str());
896 i += instruction->SizeInCodeUnits();
897 }
898 }
899 }
900
DumpVerifier(std::ostream & os,uint32_t dex_method_idx,const DexFile * dex_file,const DexFile::ClassDef & class_def,const DexFile::CodeItem * code_item,uint32_t method_access_flags)901 verifier::MethodVerifier* DumpVerifier(std::ostream& os, uint32_t dex_method_idx,
902 const DexFile* dex_file,
903 const DexFile::ClassDef& class_def,
904 const DexFile::CodeItem* code_item,
905 uint32_t method_access_flags) {
906 if ((method_access_flags & kAccNative) == 0) {
907 ScopedObjectAccess soa(Thread::Current());
908 StackHandleScope<2> hs(soa.Self());
909 Handle<mirror::DexCache> dex_cache(
910 hs.NewHandle(Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file)));
911 auto class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
912 return verifier::MethodVerifier::VerifyMethodAndDump(os, dex_method_idx, dex_file, dex_cache,
913 class_loader, &class_def, code_item,
914 nullptr, method_access_flags);
915 }
916
917 return nullptr;
918 }
919
DumpCode(std::ostream & os,verifier::MethodVerifier * verifier,const OatFile::OatMethod & oat_method,const DexFile::CodeItem * code_item,bool bad_input,size_t code_size)920 void DumpCode(std::ostream& os, verifier::MethodVerifier* verifier,
921 const OatFile::OatMethod& oat_method, const DexFile::CodeItem* code_item,
922 bool bad_input, size_t code_size) {
923 const void* portable_code = oat_method.GetPortableCode();
924 const void* quick_code = oat_method.GetQuickCode();
925
926 if (code_size == 0) {
927 code_size = oat_method.GetQuickCodeSize();
928 }
929 if ((code_size == 0) || ((portable_code == nullptr) && (quick_code == nullptr))) {
930 os << "NO CODE!\n";
931 return;
932 } else if (quick_code != nullptr) {
933 const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code);
934 size_t offset = 0;
935 while (offset < code_size) {
936 if (!bad_input) {
937 DumpMappingAtOffset(os, oat_method, offset, false);
938 }
939 offset += disassembler_->Dump(os, quick_native_pc + offset);
940 if (!bad_input) {
941 uint32_t dex_pc = DumpMappingAtOffset(os, oat_method, offset, true);
942 if (dex_pc != DexFile::kDexNoIndex) {
943 DumpGcMapAtNativePcOffset(os, oat_method, code_item, offset);
944 if (verifier != nullptr) {
945 DumpVRegsAtDexPc(os, verifier, oat_method, code_item, dex_pc);
946 }
947 }
948 }
949 }
950 } else {
951 CHECK(portable_code != nullptr);
952 CHECK_EQ(code_size, 0U); // TODO: disassembly of portable is currently not supported.
953 }
954 }
955
956 const OatFile& oat_file_;
957 const std::vector<const OatFile::OatDexFile*> oat_dex_files_;
958 const OatDumperOptions* options_;
959 InstructionSet instruction_set_;
960 std::set<uintptr_t> offsets_;
961 Disassembler* disassembler_;
962 };
963
964 class ImageDumper {
965 public:
ImageDumper(std::ostream * os,gc::space::ImageSpace & image_space,const ImageHeader & image_header,OatDumperOptions * oat_dumper_options)966 explicit ImageDumper(std::ostream* os, gc::space::ImageSpace& image_space,
967 const ImageHeader& image_header, OatDumperOptions* oat_dumper_options)
968 : os_(os),
969 image_space_(image_space),
970 image_header_(image_header),
971 oat_dumper_options_(oat_dumper_options) {}
972
Dump()973 bool Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
974 std::ostream& os = *os_;
975 os << "MAGIC: " << image_header_.GetMagic() << "\n\n";
976
977 os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n";
978
979 os << "IMAGE BITMAP OFFSET: " << reinterpret_cast<void*>(image_header_.GetImageBitmapOffset())
980 << " SIZE: " << reinterpret_cast<void*>(image_header_.GetImageBitmapSize()) << "\n\n";
981
982 os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum());
983
984 os << "OAT FILE BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatFileBegin()) << "\n\n";
985
986 os << "OAT DATA BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatDataBegin()) << "\n\n";
987
988 os << "OAT DATA END:" << reinterpret_cast<void*>(image_header_.GetOatDataEnd()) << "\n\n";
989
990 os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n";
991
992 os << "PATCH DELTA:" << image_header_.GetPatchDelta() << "\n\n";
993
994 os << "COMPILE PIC: " << (image_header_.CompilePic() ? "yes" : "no") << "\n\n";
995
996 {
997 os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
998 Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
999 std::ostream indent1_os(&indent1_filter);
1000 CHECK_EQ(arraysize(image_roots_descriptions_), size_t(ImageHeader::kImageRootsMax));
1001 for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
1002 ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
1003 const char* image_root_description = image_roots_descriptions_[i];
1004 mirror::Object* image_root_object = image_header_.GetImageRoot(image_root);
1005 indent1_os << StringPrintf("%s: %p\n", image_root_description, image_root_object);
1006 if (image_root_object->IsObjectArray()) {
1007 Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
1008 std::ostream indent2_os(&indent2_filter);
1009 mirror::ObjectArray<mirror::Object>* image_root_object_array
1010 = image_root_object->AsObjectArray<mirror::Object>();
1011 for (int i = 0; i < image_root_object_array->GetLength(); i++) {
1012 mirror::Object* value = image_root_object_array->Get(i);
1013 size_t run = 0;
1014 for (int32_t j = i + 1; j < image_root_object_array->GetLength(); j++) {
1015 if (value == image_root_object_array->Get(j)) {
1016 run++;
1017 } else {
1018 break;
1019 }
1020 }
1021 if (run == 0) {
1022 indent2_os << StringPrintf("%d: ", i);
1023 } else {
1024 indent2_os << StringPrintf("%d to %zd: ", i, i + run);
1025 i = i + run;
1026 }
1027 if (value != nullptr) {
1028 PrettyObjectValue(indent2_os, value->GetClass(), value);
1029 } else {
1030 indent2_os << i << ": null\n";
1031 }
1032 }
1033 }
1034 }
1035 }
1036 os << "\n";
1037
1038 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
1039 std::string image_filename = image_space_.GetImageFilename();
1040 std::string oat_location = ImageHeader::GetOatLocationFromImageLocation(image_filename);
1041 os << "OAT LOCATION: " << oat_location;
1042 os << "\n";
1043 std::string error_msg;
1044 const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
1045 if (oat_file == nullptr) {
1046 oat_file = OatFile::Open(oat_location, oat_location, nullptr, nullptr, false, &error_msg);
1047 if (oat_file == nullptr) {
1048 os << "NOT FOUND: " << error_msg << "\n";
1049 return false;
1050 }
1051 }
1052 os << "\n";
1053
1054 stats_.oat_file_bytes = oat_file->Size();
1055
1056 oat_dumper_.reset(new OatDumper(*oat_file, oat_dumper_options_.release()));
1057
1058 for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
1059 CHECK(oat_dex_file != nullptr);
1060 stats_.oat_dex_file_sizes.push_back(std::make_pair(oat_dex_file->GetDexFileLocation(),
1061 oat_dex_file->FileSize()));
1062 }
1063
1064 os << "OBJECTS:\n" << std::flush;
1065
1066 // Loop through all the image spaces and dump their objects.
1067 gc::Heap* heap = Runtime::Current()->GetHeap();
1068 const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces();
1069 Thread* self = Thread::Current();
1070 {
1071 {
1072 WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
1073 heap->FlushAllocStack();
1074 }
1075 // Since FlushAllocStack() above resets the (active) allocation
1076 // stack. Need to revoke the thread-local allocation stacks that
1077 // point into it.
1078 {
1079 self->TransitionFromRunnableToSuspended(kNative);
1080 ThreadList* thread_list = Runtime::Current()->GetThreadList();
1081 thread_list->SuspendAll();
1082 heap->RevokeAllThreadLocalAllocationStacks(self);
1083 thread_list->ResumeAll();
1084 self->TransitionFromSuspendedToRunnable();
1085 }
1086 }
1087 {
1088 std::ostream* saved_os = os_;
1089 Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
1090 std::ostream indent_os(&indent_filter);
1091 os_ = &indent_os;
1092 ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
1093 for (const auto& space : spaces) {
1094 if (space->IsImageSpace()) {
1095 gc::space::ImageSpace* image_space = space->AsImageSpace();
1096 image_space->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
1097 indent_os << "\n";
1098 }
1099 }
1100 // Dump the large objects separately.
1101 heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
1102 indent_os << "\n";
1103 os_ = saved_os;
1104 }
1105 os << "STATS:\n" << std::flush;
1106 std::unique_ptr<File> file(OS::OpenFileForReading(image_filename.c_str()));
1107 if (file.get() == nullptr) {
1108 LOG(WARNING) << "Failed to find image in " << image_filename;
1109 }
1110 if (file.get() != nullptr) {
1111 stats_.file_bytes = file->GetLength();
1112 }
1113 size_t header_bytes = sizeof(ImageHeader);
1114 stats_.header_bytes = header_bytes;
1115 size_t alignment_bytes = RoundUp(header_bytes, kObjectAlignment) - header_bytes;
1116 stats_.alignment_bytes += alignment_bytes;
1117 stats_.alignment_bytes += image_header_.GetImageBitmapOffset() - image_header_.GetImageSize();
1118 stats_.bitmap_bytes += image_header_.GetImageBitmapSize();
1119 stats_.Dump(os);
1120 os << "\n";
1121
1122 os << std::flush;
1123
1124 return oat_dumper_->Dump(os);
1125 }
1126
1127 private:
PrettyObjectValue(std::ostream & os,mirror::Class * type,mirror::Object * value)1128 static void PrettyObjectValue(std::ostream& os, mirror::Class* type, mirror::Object* value)
1129 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
1130 CHECK(type != nullptr);
1131 if (value == nullptr) {
1132 os << StringPrintf("null %s\n", PrettyDescriptor(type).c_str());
1133 } else if (type->IsStringClass()) {
1134 mirror::String* string = value->AsString();
1135 os << StringPrintf("%p String: %s\n", string,
1136 PrintableString(string->ToModifiedUtf8().c_str()).c_str());
1137 } else if (type->IsClassClass()) {
1138 mirror::Class* klass = value->AsClass();
1139 os << StringPrintf("%p Class: %s\n", klass, PrettyDescriptor(klass).c_str());
1140 } else if (type->IsArtFieldClass()) {
1141 mirror::ArtField* field = value->AsArtField();
1142 os << StringPrintf("%p Field: %s\n", field, PrettyField(field).c_str());
1143 } else if (type->IsArtMethodClass()) {
1144 mirror::ArtMethod* method = value->AsArtMethod();
1145 os << StringPrintf("%p Method: %s\n", method, PrettyMethod(method).c_str());
1146 } else {
1147 os << StringPrintf("%p %s\n", value, PrettyDescriptor(type).c_str());
1148 }
1149 }
1150
PrintField(std::ostream & os,mirror::ArtField * field,mirror::Object * obj)1151 static void PrintField(std::ostream& os, mirror::ArtField* field, mirror::Object* obj)
1152 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
1153 const char* descriptor = field->GetTypeDescriptor();
1154 os << StringPrintf("%s: ", field->GetName());
1155 if (descriptor[0] != 'L' && descriptor[0] != '[') {
1156 StackHandleScope<1> hs(Thread::Current());
1157 FieldHelper fh(hs.NewHandle(field));
1158 mirror::Class* type = fh.GetType();
1159 if (type->IsPrimitiveLong()) {
1160 os << StringPrintf("%" PRId64 " (0x%" PRIx64 ")\n", field->Get64(obj), field->Get64(obj));
1161 } else if (type->IsPrimitiveDouble()) {
1162 os << StringPrintf("%f (%a)\n", field->GetDouble(obj), field->GetDouble(obj));
1163 } else if (type->IsPrimitiveFloat()) {
1164 os << StringPrintf("%f (%a)\n", field->GetFloat(obj), field->GetFloat(obj));
1165 } else {
1166 DCHECK(type->IsPrimitive());
1167 os << StringPrintf("%d (0x%x)\n", field->Get32(obj), field->Get32(obj));
1168 }
1169 } else {
1170 // Get the value, don't compute the type unless it is non-null as we don't want
1171 // to cause class loading.
1172 mirror::Object* value = field->GetObj(obj);
1173 if (value == nullptr) {
1174 os << StringPrintf("null %s\n", PrettyDescriptor(descriptor).c_str());
1175 } else {
1176 // Grab the field type without causing resolution.
1177 StackHandleScope<1> hs(Thread::Current());
1178 FieldHelper fh(hs.NewHandle(field));
1179 mirror::Class* field_type = fh.GetType(false);
1180 if (field_type != nullptr) {
1181 PrettyObjectValue(os, field_type, value);
1182 } else {
1183 os << StringPrintf("%p %s\n", value, PrettyDescriptor(descriptor).c_str());
1184 }
1185 }
1186 }
1187 }
1188
DumpFields(std::ostream & os,mirror::Object * obj,mirror::Class * klass)1189 static void DumpFields(std::ostream& os, mirror::Object* obj, mirror::Class* klass)
1190 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
1191 mirror::Class* super = klass->GetSuperClass();
1192 if (super != nullptr) {
1193 DumpFields(os, obj, super);
1194 }
1195 mirror::ObjectArray<mirror::ArtField>* fields = klass->GetIFields();
1196 if (fields != nullptr) {
1197 for (int32_t i = 0; i < fields->GetLength(); i++) {
1198 mirror::ArtField* field = fields->Get(i);
1199 PrintField(os, field, obj);
1200 }
1201 }
1202 }
1203
InDumpSpace(const mirror::Object * object)1204 bool InDumpSpace(const mirror::Object* object) {
1205 return image_space_.Contains(object);
1206 }
1207
GetQuickOatCodeBegin(mirror::ArtMethod * m)1208 const void* GetQuickOatCodeBegin(mirror::ArtMethod* m)
1209 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
1210 const void* quick_code = m->GetEntryPointFromQuickCompiledCodePtrSize(
1211 InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet()));
1212 if (quick_code == Runtime::Current()->GetClassLinker()->GetQuickResolutionTrampoline()) {
1213 quick_code = oat_dumper_->GetQuickOatCode(m);
1214 }
1215 if (oat_dumper_->GetInstructionSet() == kThumb2) {
1216 quick_code = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(quick_code) & ~0x1);
1217 }
1218 return quick_code;
1219 }
1220
GetQuickOatCodeSize(mirror::ArtMethod * m)1221 uint32_t GetQuickOatCodeSize(mirror::ArtMethod* m)
1222 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
1223 const uint32_t* oat_code_begin = reinterpret_cast<const uint32_t*>(GetQuickOatCodeBegin(m));
1224 if (oat_code_begin == nullptr) {
1225 return 0;
1226 }
1227 return oat_code_begin[-1];
1228 }
1229
GetQuickOatCodeEnd(mirror::ArtMethod * m)1230 const void* GetQuickOatCodeEnd(mirror::ArtMethod* m)
1231 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
1232 const uint8_t* oat_code_begin = reinterpret_cast<const uint8_t*>(GetQuickOatCodeBegin(m));
1233 if (oat_code_begin == nullptr) {
1234 return nullptr;
1235 }
1236 return oat_code_begin + GetQuickOatCodeSize(m);
1237 }
1238
Callback(mirror::Object * obj,void * arg)1239 static void Callback(mirror::Object* obj, void* arg)
1240 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
1241 DCHECK(obj != nullptr);
1242 DCHECK(arg != nullptr);
1243 ImageDumper* state = reinterpret_cast<ImageDumper*>(arg);
1244 if (!state->InDumpSpace(obj)) {
1245 return;
1246 }
1247
1248 size_t object_bytes = obj->SizeOf();
1249 size_t alignment_bytes = RoundUp(object_bytes, kObjectAlignment) - object_bytes;
1250 state->stats_.object_bytes += object_bytes;
1251 state->stats_.alignment_bytes += alignment_bytes;
1252
1253 std::ostream& os = *state->os_;
1254 mirror::Class* obj_class = obj->GetClass();
1255 if (obj_class->IsArrayClass()) {
1256 os << StringPrintf("%p: %s length:%d\n", obj, PrettyDescriptor(obj_class).c_str(),
1257 obj->AsArray()->GetLength());
1258 } else if (obj->IsClass()) {
1259 mirror::Class* klass = obj->AsClass();
1260 os << StringPrintf("%p: java.lang.Class \"%s\" (", obj, PrettyDescriptor(klass).c_str())
1261 << klass->GetStatus() << ")\n";
1262 } else if (obj->IsArtField()) {
1263 os << StringPrintf("%p: java.lang.reflect.ArtField %s\n", obj,
1264 PrettyField(obj->AsArtField()).c_str());
1265 } else if (obj->IsArtMethod()) {
1266 os << StringPrintf("%p: java.lang.reflect.ArtMethod %s\n", obj,
1267 PrettyMethod(obj->AsArtMethod()).c_str());
1268 } else if (obj_class->IsStringClass()) {
1269 os << StringPrintf("%p: java.lang.String %s\n", obj,
1270 PrintableString(obj->AsString()->ToModifiedUtf8().c_str()).c_str());
1271 } else {
1272 os << StringPrintf("%p: %s\n", obj, PrettyDescriptor(obj_class).c_str());
1273 }
1274 Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
1275 std::ostream indent_os(&indent_filter);
1276 DumpFields(indent_os, obj, obj_class);
1277 if (obj->IsObjectArray()) {
1278 mirror::ObjectArray<mirror::Object>* obj_array = obj->AsObjectArray<mirror::Object>();
1279 int32_t length = obj_array->GetLength();
1280 for (int32_t i = 0; i < length; i++) {
1281 mirror::Object* value = obj_array->Get(i);
1282 size_t run = 0;
1283 for (int32_t j = i + 1; j < length; j++) {
1284 if (value == obj_array->Get(j)) {
1285 run++;
1286 } else {
1287 break;
1288 }
1289 }
1290 if (run == 0) {
1291 indent_os << StringPrintf("%d: ", i);
1292 } else {
1293 indent_os << StringPrintf("%d to %zd: ", i, i + run);
1294 i = i + run;
1295 }
1296 mirror::Class* value_class =
1297 (value == nullptr) ? obj_class->GetComponentType() : value->GetClass();
1298 PrettyObjectValue(indent_os, value_class, value);
1299 }
1300 } else if (obj->IsClass()) {
1301 mirror::ObjectArray<mirror::ArtField>* sfields = obj->AsClass()->GetSFields();
1302 if (sfields != nullptr) {
1303 indent_os << "STATICS:\n";
1304 Indenter indent2_filter(indent_os.rdbuf(), kIndentChar, kIndentBy1Count);
1305 std::ostream indent2_os(&indent2_filter);
1306 for (int32_t i = 0; i < sfields->GetLength(); i++) {
1307 mirror::ArtField* field = sfields->Get(i);
1308 PrintField(indent2_os, field, field->GetDeclaringClass());
1309 }
1310 }
1311 } else if (obj->IsArtMethod()) {
1312 const size_t image_pointer_size = InstructionSetPointerSize(
1313 state->oat_dumper_->GetOatInstructionSet());
1314 mirror::ArtMethod* method = obj->AsArtMethod();
1315 if (method->IsNative()) {
1316 // TODO: portable dumping.
1317 DCHECK(method->GetNativeGcMap(image_pointer_size) == nullptr) << PrettyMethod(method);
1318 DCHECK(method->GetMappingTable(image_pointer_size) == nullptr) << PrettyMethod(method);
1319 bool first_occurrence;
1320 const void* quick_oat_code = state->GetQuickOatCodeBegin(method);
1321 uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method);
1322 state->ComputeOatSize(quick_oat_code, &first_occurrence);
1323 if (first_occurrence) {
1324 state->stats_.native_to_managed_code_bytes += quick_oat_code_size;
1325 }
1326 if (quick_oat_code != method->GetEntryPointFromQuickCompiledCodePtrSize(
1327 image_pointer_size)) {
1328 indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code);
1329 }
1330 } else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
1331 method->IsResolutionMethod() || method->IsImtConflictMethod() ||
1332 method->IsImtUnimplementedMethod() || method->IsClassInitializer()) {
1333 DCHECK(method->GetNativeGcMap(image_pointer_size) == nullptr) << PrettyMethod(method);
1334 DCHECK(method->GetMappingTable(image_pointer_size) == nullptr) << PrettyMethod(method);
1335 } else {
1336 const DexFile::CodeItem* code_item = method->GetCodeItem();
1337 size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
1338 state->stats_.dex_instruction_bytes += dex_instruction_bytes;
1339
1340 bool first_occurrence;
1341 size_t gc_map_bytes =
1342 state->ComputeOatSize(method->GetNativeGcMap(image_pointer_size), &first_occurrence);
1343 if (first_occurrence) {
1344 state->stats_.gc_map_bytes += gc_map_bytes;
1345 }
1346
1347 size_t pc_mapping_table_bytes =
1348 state->ComputeOatSize(method->GetMappingTable(image_pointer_size), &first_occurrence);
1349 if (first_occurrence) {
1350 state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
1351 }
1352
1353 size_t vmap_table_bytes =
1354 state->ComputeOatSize(method->GetVmapTable(image_pointer_size), &first_occurrence);
1355 if (first_occurrence) {
1356 state->stats_.vmap_table_bytes += vmap_table_bytes;
1357 }
1358
1359 // TODO: portable dumping.
1360 const void* quick_oat_code_begin = state->GetQuickOatCodeBegin(method);
1361 const void* quick_oat_code_end = state->GetQuickOatCodeEnd(method);
1362 uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method);
1363 state->ComputeOatSize(quick_oat_code_begin, &first_occurrence);
1364 if (first_occurrence) {
1365 state->stats_.managed_code_bytes += quick_oat_code_size;
1366 if (method->IsConstructor()) {
1367 if (method->IsStatic()) {
1368 state->stats_.class_initializer_code_bytes += quick_oat_code_size;
1369 } else if (dex_instruction_bytes > kLargeConstructorDexBytes) {
1370 state->stats_.large_initializer_code_bytes += quick_oat_code_size;
1371 }
1372 } else if (dex_instruction_bytes > kLargeMethodDexBytes) {
1373 state->stats_.large_method_code_bytes += quick_oat_code_size;
1374 }
1375 }
1376 state->stats_.managed_code_bytes_ignoring_deduplication += quick_oat_code_size;
1377
1378 indent_os << StringPrintf("OAT CODE: %p-%p\n", quick_oat_code_begin, quick_oat_code_end);
1379 indent_os << StringPrintf("SIZE: Dex Instructions=%zd GC=%zd Mapping=%zd\n",
1380 dex_instruction_bytes, gc_map_bytes, pc_mapping_table_bytes);
1381
1382 size_t total_size = dex_instruction_bytes + gc_map_bytes + pc_mapping_table_bytes +
1383 vmap_table_bytes + quick_oat_code_size + object_bytes;
1384
1385 double expansion =
1386 static_cast<double>(quick_oat_code_size) / static_cast<double>(dex_instruction_bytes);
1387 state->stats_.ComputeOutliers(total_size, expansion, method);
1388 }
1389 }
1390 std::string temp;
1391 state->stats_.Update(obj_class->GetDescriptor(&temp), object_bytes);
1392 }
1393
1394 std::set<const void*> already_seen_;
1395 // Compute the size of the given data within the oat file and whether this is the first time
1396 // this data has been requested
ComputeOatSize(const void * oat_data,bool * first_occurrence)1397 size_t ComputeOatSize(const void* oat_data, bool* first_occurrence) {
1398 if (already_seen_.count(oat_data) == 0) {
1399 *first_occurrence = true;
1400 already_seen_.insert(oat_data);
1401 } else {
1402 *first_occurrence = false;
1403 }
1404 return oat_dumper_->ComputeSize(oat_data);
1405 }
1406
1407 public:
1408 struct Stats {
1409 size_t oat_file_bytes;
1410 size_t file_bytes;
1411
1412 size_t header_bytes;
1413 size_t object_bytes;
1414 size_t bitmap_bytes;
1415 size_t alignment_bytes;
1416
1417 size_t managed_code_bytes;
1418 size_t managed_code_bytes_ignoring_deduplication;
1419 size_t managed_to_native_code_bytes;
1420 size_t native_to_managed_code_bytes;
1421 size_t class_initializer_code_bytes;
1422 size_t large_initializer_code_bytes;
1423 size_t large_method_code_bytes;
1424
1425 size_t gc_map_bytes;
1426 size_t pc_mapping_table_bytes;
1427 size_t vmap_table_bytes;
1428
1429 size_t dex_instruction_bytes;
1430
1431 std::vector<mirror::ArtMethod*> method_outlier;
1432 std::vector<size_t> method_outlier_size;
1433 std::vector<double> method_outlier_expansion;
1434 std::vector<std::pair<std::string, size_t>> oat_dex_file_sizes;
1435
Statsart::ImageDumper::Stats1436 explicit Stats()
1437 : oat_file_bytes(0),
1438 file_bytes(0),
1439 header_bytes(0),
1440 object_bytes(0),
1441 bitmap_bytes(0),
1442 alignment_bytes(0),
1443 managed_code_bytes(0),
1444 managed_code_bytes_ignoring_deduplication(0),
1445 managed_to_native_code_bytes(0),
1446 native_to_managed_code_bytes(0),
1447 class_initializer_code_bytes(0),
1448 large_initializer_code_bytes(0),
1449 large_method_code_bytes(0),
1450 gc_map_bytes(0),
1451 pc_mapping_table_bytes(0),
1452 vmap_table_bytes(0),
1453 dex_instruction_bytes(0) {}
1454
1455 struct SizeAndCount {
SizeAndCountart::ImageDumper::Stats::SizeAndCount1456 SizeAndCount(size_t bytes, size_t count) : bytes(bytes), count(count) {}
1457 size_t bytes;
1458 size_t count;
1459 };
1460 typedef SafeMap<std::string, SizeAndCount> SizeAndCountTable;
1461 SizeAndCountTable sizes_and_counts;
1462
Updateart::ImageDumper::Stats1463 void Update(const char* descriptor, size_t object_bytes) {
1464 SizeAndCountTable::iterator it = sizes_and_counts.find(descriptor);
1465 if (it != sizes_and_counts.end()) {
1466 it->second.bytes += object_bytes;
1467 it->second.count += 1;
1468 } else {
1469 sizes_and_counts.Put(descriptor, SizeAndCount(object_bytes, 1));
1470 }
1471 }
1472
PercentOfOatBytesart::ImageDumper::Stats1473 double PercentOfOatBytes(size_t size) {
1474 return (static_cast<double>(size) / static_cast<double>(oat_file_bytes)) * 100;
1475 }
1476
PercentOfFileBytesart::ImageDumper::Stats1477 double PercentOfFileBytes(size_t size) {
1478 return (static_cast<double>(size) / static_cast<double>(file_bytes)) * 100;
1479 }
1480
PercentOfObjectBytesart::ImageDumper::Stats1481 double PercentOfObjectBytes(size_t size) {
1482 return (static_cast<double>(size) / static_cast<double>(object_bytes)) * 100;
1483 }
1484
ComputeOutliersart::ImageDumper::Stats1485 void ComputeOutliers(size_t total_size, double expansion, mirror::ArtMethod* method) {
1486 method_outlier_size.push_back(total_size);
1487 method_outlier_expansion.push_back(expansion);
1488 method_outlier.push_back(method);
1489 }
1490
DumpOutliersart::ImageDumper::Stats1491 void DumpOutliers(std::ostream& os)
1492 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
1493 size_t sum_of_sizes = 0;
1494 size_t sum_of_sizes_squared = 0;
1495 size_t sum_of_expansion = 0;
1496 size_t sum_of_expansion_squared = 0;
1497 size_t n = method_outlier_size.size();
1498 for (size_t i = 0; i < n; i++) {
1499 size_t cur_size = method_outlier_size[i];
1500 sum_of_sizes += cur_size;
1501 sum_of_sizes_squared += cur_size * cur_size;
1502 double cur_expansion = method_outlier_expansion[i];
1503 sum_of_expansion += cur_expansion;
1504 sum_of_expansion_squared += cur_expansion * cur_expansion;
1505 }
1506 size_t size_mean = sum_of_sizes / n;
1507 size_t size_variance = (sum_of_sizes_squared - sum_of_sizes * size_mean) / (n - 1);
1508 double expansion_mean = sum_of_expansion / n;
1509 double expansion_variance =
1510 (sum_of_expansion_squared - sum_of_expansion * expansion_mean) / (n - 1);
1511
1512 // Dump methods whose size is a certain number of standard deviations from the mean
1513 size_t dumped_values = 0;
1514 size_t skipped_values = 0;
1515 for (size_t i = 100; i > 0; i--) { // i is the current number of standard deviations
1516 size_t cur_size_variance = i * i * size_variance;
1517 bool first = true;
1518 for (size_t j = 0; j < n; j++) {
1519 size_t cur_size = method_outlier_size[j];
1520 if (cur_size > size_mean) {
1521 size_t cur_var = cur_size - size_mean;
1522 cur_var = cur_var * cur_var;
1523 if (cur_var > cur_size_variance) {
1524 if (dumped_values > 20) {
1525 if (i == 1) {
1526 skipped_values++;
1527 } else {
1528 i = 2; // jump to counting for 1 standard deviation
1529 break;
1530 }
1531 } else {
1532 if (first) {
1533 os << "\nBig methods (size > " << i << " standard deviations the norm):\n";
1534 first = false;
1535 }
1536 os << PrettyMethod(method_outlier[j]) << " requires storage of "
1537 << PrettySize(cur_size) << "\n";
1538 method_outlier_size[j] = 0; // don't consider this method again
1539 dumped_values++;
1540 }
1541 }
1542 }
1543 }
1544 }
1545 if (skipped_values > 0) {
1546 os << "... skipped " << skipped_values
1547 << " methods with size > 1 standard deviation from the norm\n";
1548 }
1549 os << std::flush;
1550
1551 // Dump methods whose expansion is a certain number of standard deviations from the mean
1552 dumped_values = 0;
1553 skipped_values = 0;
1554 for (size_t i = 10; i > 0; i--) { // i is the current number of standard deviations
1555 double cur_expansion_variance = i * i * expansion_variance;
1556 bool first = true;
1557 for (size_t j = 0; j < n; j++) {
1558 double cur_expansion = method_outlier_expansion[j];
1559 if (cur_expansion > expansion_mean) {
1560 size_t cur_var = cur_expansion - expansion_mean;
1561 cur_var = cur_var * cur_var;
1562 if (cur_var > cur_expansion_variance) {
1563 if (dumped_values > 20) {
1564 if (i == 1) {
1565 skipped_values++;
1566 } else {
1567 i = 2; // jump to counting for 1 standard deviation
1568 break;
1569 }
1570 } else {
1571 if (first) {
1572 os << "\nLarge expansion methods (size > " << i
1573 << " standard deviations the norm):\n";
1574 first = false;
1575 }
1576 os << PrettyMethod(method_outlier[j]) << " expanded code by "
1577 << cur_expansion << "\n";
1578 method_outlier_expansion[j] = 0.0; // don't consider this method again
1579 dumped_values++;
1580 }
1581 }
1582 }
1583 }
1584 }
1585 if (skipped_values > 0) {
1586 os << "... skipped " << skipped_values
1587 << " methods with expansion > 1 standard deviation from the norm\n";
1588 }
1589 os << "\n" << std::flush;
1590 }
1591
Dumpart::ImageDumper::Stats1592 void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
1593 {
1594 os << "art_file_bytes = " << PrettySize(file_bytes) << "\n\n"
1595 << "art_file_bytes = header_bytes + object_bytes + alignment_bytes\n";
1596 Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
1597 std::ostream indent_os(&indent_filter);
1598 indent_os << StringPrintf("header_bytes = %8zd (%2.0f%% of art file bytes)\n"
1599 "object_bytes = %8zd (%2.0f%% of art file bytes)\n"
1600 "bitmap_bytes = %8zd (%2.0f%% of art file bytes)\n"
1601 "alignment_bytes = %8zd (%2.0f%% of art file bytes)\n\n",
1602 header_bytes, PercentOfFileBytes(header_bytes),
1603 object_bytes, PercentOfFileBytes(object_bytes),
1604 bitmap_bytes, PercentOfFileBytes(bitmap_bytes),
1605 alignment_bytes, PercentOfFileBytes(alignment_bytes))
1606 << std::flush;
1607 CHECK_EQ(file_bytes, bitmap_bytes + header_bytes + object_bytes + alignment_bytes);
1608 }
1609
1610 os << "object_bytes breakdown:\n";
1611 size_t object_bytes_total = 0;
1612 for (const auto& sizes_and_count : sizes_and_counts) {
1613 const std::string& descriptor(sizes_and_count.first);
1614 double average = static_cast<double>(sizes_and_count.second.bytes) /
1615 static_cast<double>(sizes_and_count.second.count);
1616 double percent = PercentOfObjectBytes(sizes_and_count.second.bytes);
1617 os << StringPrintf("%32s %8zd bytes %6zd instances "
1618 "(%4.0f bytes/instance) %2.0f%% of object_bytes\n",
1619 descriptor.c_str(), sizes_and_count.second.bytes,
1620 sizes_and_count.second.count, average, percent);
1621 object_bytes_total += sizes_and_count.second.bytes;
1622 }
1623 os << "\n" << std::flush;
1624 CHECK_EQ(object_bytes, object_bytes_total);
1625
1626 os << StringPrintf("oat_file_bytes = %8zd\n"
1627 "managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
1628 "managed_to_native_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
1629 "native_to_managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n"
1630 "class_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
1631 "large_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
1632 "large_method_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n",
1633 oat_file_bytes,
1634 managed_code_bytes,
1635 PercentOfOatBytes(managed_code_bytes),
1636 managed_to_native_code_bytes,
1637 PercentOfOatBytes(managed_to_native_code_bytes),
1638 native_to_managed_code_bytes,
1639 PercentOfOatBytes(native_to_managed_code_bytes),
1640 class_initializer_code_bytes,
1641 PercentOfOatBytes(class_initializer_code_bytes),
1642 large_initializer_code_bytes,
1643 PercentOfOatBytes(large_initializer_code_bytes),
1644 large_method_code_bytes,
1645 PercentOfOatBytes(large_method_code_bytes))
1646 << "DexFile sizes:\n";
1647 for (const std::pair<std::string, size_t>& oat_dex_file_size : oat_dex_file_sizes) {
1648 os << StringPrintf("%s = %zd (%2.0f%% of oat file bytes)\n",
1649 oat_dex_file_size.first.c_str(), oat_dex_file_size.second,
1650 PercentOfOatBytes(oat_dex_file_size.second));
1651 }
1652
1653 os << "\n" << StringPrintf("gc_map_bytes = %7zd (%2.0f%% of oat file bytes)\n"
1654 "pc_mapping_table_bytes = %7zd (%2.0f%% of oat file bytes)\n"
1655 "vmap_table_bytes = %7zd (%2.0f%% of oat file bytes)\n\n",
1656 gc_map_bytes, PercentOfOatBytes(gc_map_bytes),
1657 pc_mapping_table_bytes, PercentOfOatBytes(pc_mapping_table_bytes),
1658 vmap_table_bytes, PercentOfOatBytes(vmap_table_bytes))
1659 << std::flush;
1660
1661 os << StringPrintf("dex_instruction_bytes = %zd\n", dex_instruction_bytes)
1662 << StringPrintf("managed_code_bytes expansion = %.2f (ignoring deduplication %.2f)\n\n",
1663 static_cast<double>(managed_code_bytes) /
1664 static_cast<double>(dex_instruction_bytes),
1665 static_cast<double>(managed_code_bytes_ignoring_deduplication) /
1666 static_cast<double>(dex_instruction_bytes))
1667 << std::flush;
1668
1669 DumpOutliers(os);
1670 }
1671 } stats_;
1672
1673 private:
1674 enum {
1675 // Number of bytes for a constructor to be considered large. Based on the 1000 basic block
1676 // threshold, we assume 2 bytes per instruction and 2 instructions per block.
1677 kLargeConstructorDexBytes = 4000,
1678 // Number of bytes for a method to be considered large. Based on the 4000 basic block
1679 // threshold, we assume 2 bytes per instruction and 2 instructions per block.
1680 kLargeMethodDexBytes = 16000
1681 };
1682 std::ostream* os_;
1683 gc::space::ImageSpace& image_space_;
1684 const ImageHeader& image_header_;
1685 std::unique_ptr<OatDumper> oat_dumper_;
1686 std::unique_ptr<OatDumperOptions> oat_dumper_options_;
1687
1688 DISALLOW_COPY_AND_ASSIGN(ImageDumper);
1689 };
1690
oatdump(int argc,char ** argv)1691 static int oatdump(int argc, char** argv) {
1692 InitLogging(argv);
1693
1694 // Skip over argv[0].
1695 argv++;
1696 argc--;
1697
1698 if (argc == 0) {
1699 fprintf(stderr, "No arguments specified\n");
1700 usage();
1701 }
1702
1703 const char* oat_filename = nullptr;
1704 const char* image_location = nullptr;
1705 const char* boot_image_location = nullptr;
1706 InstructionSet instruction_set = kRuntimeISA;
1707 std::string elf_filename_prefix;
1708 std::ostream* os = &std::cout;
1709 std::unique_ptr<std::ofstream> out;
1710 bool dump_raw_mapping_table = false;
1711 bool dump_raw_gc_map = false;
1712 bool dump_vmap = true;
1713 bool disassemble_code = true;
1714
1715 for (int i = 0; i < argc; i++) {
1716 const StringPiece option(argv[i]);
1717 if (option.starts_with("--oat-file=")) {
1718 oat_filename = option.substr(strlen("--oat-file=")).data();
1719 } else if (option.starts_with("--image=")) {
1720 image_location = option.substr(strlen("--image=")).data();
1721 } else if (option.starts_with("--boot-image=")) {
1722 boot_image_location = option.substr(strlen("--boot-image=")).data();
1723 } else if (option.starts_with("--instruction-set=")) {
1724 StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
1725 if (instruction_set_str == "arm") {
1726 instruction_set = kThumb2;
1727 } else if (instruction_set_str == "arm64") {
1728 instruction_set = kArm64;
1729 } else if (instruction_set_str == "mips") {
1730 instruction_set = kMips;
1731 } else if (instruction_set_str == "x86") {
1732 instruction_set = kX86;
1733 } else if (instruction_set_str == "x86_64") {
1734 instruction_set = kX86_64;
1735 }
1736 } else if (option =="--dump:raw_mapping_table") {
1737 dump_raw_mapping_table = true;
1738 } else if (option == "--dump:raw_gc_map") {
1739 dump_raw_gc_map = true;
1740 } else if (option == "--no-dump:vmap") {
1741 dump_vmap = false;
1742 } else if (option == "--no-disassemble") {
1743 disassemble_code = false;
1744 } else if (option.starts_with("--output=")) {
1745 const char* filename = option.substr(strlen("--output=")).data();
1746 out.reset(new std::ofstream(filename));
1747 if (!out->good()) {
1748 fprintf(stderr, "Failed to open output filename %s\n", filename);
1749 usage();
1750 }
1751 os = out.get();
1752 } else {
1753 fprintf(stderr, "Unknown argument %s\n", option.data());
1754 usage();
1755 }
1756 }
1757
1758 if (image_location == nullptr && oat_filename == nullptr) {
1759 fprintf(stderr, "Either --image or --oat must be specified\n");
1760 return EXIT_FAILURE;
1761 }
1762
1763 if (image_location != nullptr && oat_filename != nullptr) {
1764 fprintf(stderr, "Either --image or --oat must be specified but not both\n");
1765 return EXIT_FAILURE;
1766 }
1767
1768 // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
1769 bool absolute_addresses = (oat_filename == nullptr);
1770 std::unique_ptr<OatDumperOptions> oat_dumper_options(new OatDumperOptions(dump_raw_mapping_table,
1771 dump_raw_gc_map,
1772 dump_vmap,
1773 disassemble_code,
1774 absolute_addresses));
1775 MemMap::Init();
1776 if (oat_filename != nullptr) {
1777 std::string error_msg;
1778 OatFile* oat_file =
1779 OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false, &error_msg);
1780 if (oat_file == nullptr) {
1781 fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
1782 return EXIT_FAILURE;
1783 }
1784 OatDumper oat_dumper(*oat_file, oat_dumper_options.release());
1785 bool success = oat_dumper.Dump(*os);
1786 return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
1787 }
1788
1789 RuntimeOptions options;
1790 std::string image_option;
1791 std::string oat_option;
1792 std::string boot_image_option;
1793 std::string boot_oat_option;
1794
1795 // We are more like a compiler than a run-time. We don't want to execute code.
1796 NoopCompilerCallbacks callbacks;
1797 options.push_back(std::make_pair("compilercallbacks", &callbacks));
1798
1799 if (boot_image_location != nullptr) {
1800 boot_image_option += "-Ximage:";
1801 boot_image_option += boot_image_location;
1802 options.push_back(std::make_pair(boot_image_option.c_str(), nullptr));
1803 }
1804 if (image_location != nullptr) {
1805 image_option += "-Ximage:";
1806 image_option += image_location;
1807 options.push_back(std::make_pair(image_option.c_str(), nullptr));
1808 }
1809 options.push_back(
1810 std::make_pair("imageinstructionset",
1811 reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
1812
1813 if (!Runtime::Create(options, false)) {
1814 fprintf(stderr, "Failed to create runtime\n");
1815 return EXIT_FAILURE;
1816 }
1817 std::unique_ptr<Runtime> runtime(Runtime::Current());
1818 // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
1819 // give it away now and then switch to a more manageable ScopedObjectAccess.
1820 Thread::Current()->TransitionFromRunnableToSuspended(kNative);
1821 ScopedObjectAccess soa(Thread::Current());
1822 gc::Heap* heap = Runtime::Current()->GetHeap();
1823 gc::space::ImageSpace* image_space = heap->GetImageSpace();
1824 CHECK(image_space != nullptr);
1825 const ImageHeader& image_header = image_space->GetImageHeader();
1826 if (!image_header.IsValid()) {
1827 fprintf(stderr, "Invalid image header %s\n", image_location);
1828 return EXIT_FAILURE;
1829 }
1830 ImageDumper image_dumper(os, *image_space, image_header, oat_dumper_options.release());
1831 bool success = image_dumper.Dump();
1832 return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
1833 }
1834
1835 } // namespace art
1836
main(int argc,char ** argv)1837 int main(int argc, char** argv) {
1838 return art::oatdump(argc, argv);
1839 }
1840