• 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   "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