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