• 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 "gc_map.h"
33 #include "gc/space/image_space.h"
34 #include "gc/space/large_object_space.h"
35 #include "gc/space/space-inl.h"
36 #include "image.h"
37 #include "indenter.h"
38 #include "mapping_table.h"
39 #include "mirror/art_field-inl.h"
40 #include "mirror/art_method-inl.h"
41 #include "mirror/array-inl.h"
42 #include "mirror/class-inl.h"
43 #include "mirror/object-inl.h"
44 #include "mirror/object_array-inl.h"
45 #include "oat.h"
46 #include "object_utils.h"
47 #include "os.h"
48 #include "runtime.h"
49 #include "safe_map.h"
50 #include "scoped_thread_state_change.h"
51 #include "verifier/method_verifier.h"
52 #include "vmap_table.h"
53 
54 namespace art {
55 
usage()56 static void usage() {
57   fprintf(stderr,
58           "Usage: oatdump [options] ...\n"
59           "    Example: oatdump --image=$ANDROID_PRODUCT_OUT/system/framework/boot.art --host-prefix=$ANDROID_PRODUCT_OUT\n"
60           "    Example: adb shell oatdump --image=/system/framework/boot.art\n"
61           "\n");
62   fprintf(stderr,
63           "  --oat-file=<file.oat>: specifies an input oat filename.\n"
64           "      Example: --oat-file=/system/framework/boot.oat\n"
65           "\n");
66   fprintf(stderr,
67           "  --image=<file.art>: specifies an input image filename.\n"
68           "      Example: --image=/system/framework/boot.art\n"
69           "\n");
70   fprintf(stderr,
71           "  --boot-image=<file.art>: provide the image file for the boot class path.\n"
72           "      Example: --boot-image=/system/framework/boot.art\n"
73           "\n");
74   fprintf(stderr,
75           "  --host-prefix may be used to translate host paths to target paths during\n"
76           "      cross compilation.\n"
77           "      Example: --host-prefix=out/target/product/crespo\n"
78           "      Default: $ANDROID_PRODUCT_OUT\n"
79           "\n");
80   fprintf(stderr,
81           "  --output=<file> may be used to send the output to a file.\n"
82           "      Example: --output=/tmp/oatdump.txt\n"
83           "\n");
84   exit(EXIT_FAILURE);
85 }
86 
87 const char* image_roots_descriptions_[] = {
88   "kResolutionMethod",
89   "kCalleeSaveMethod",
90   "kRefsOnlySaveMethod",
91   "kRefsAndArgsSaveMethod",
92   "kOatLocation",
93   "kDexCaches",
94   "kClassRoots",
95 };
96 
97 class OatDumper {
98  public:
OatDumper(const std::string & host_prefix,const OatFile & oat_file)99   explicit OatDumper(const std::string& host_prefix, const OatFile& oat_file)
100     : host_prefix_(host_prefix),
101       oat_file_(oat_file),
102       oat_dex_files_(oat_file.GetOatDexFiles()),
103       disassembler_(Disassembler::Create(oat_file_.GetOatHeader().GetInstructionSet())) {
104     AddAllOffsets();
105   }
106 
Dump(std::ostream & os)107   void Dump(std::ostream& os) {
108     const OatHeader& oat_header = oat_file_.GetOatHeader();
109 
110     os << "MAGIC:\n";
111     os << oat_header.GetMagic() << "\n\n";
112 
113     os << "CHECKSUM:\n";
114     os << StringPrintf("0x%08x\n\n", oat_header.GetChecksum());
115 
116     os << "INSTRUCTION SET:\n";
117     os << oat_header.GetInstructionSet() << "\n\n";
118 
119     os << "DEX FILE COUNT:\n";
120     os << oat_header.GetDexFileCount() << "\n\n";
121 
122     os << "EXECUTABLE OFFSET:\n";
123     os << StringPrintf("0x%08x\n\n", oat_header.GetExecutableOffset());
124 
125     os << "IMAGE FILE LOCATION OAT CHECKSUM:\n";
126     os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum());
127 
128     os << "IMAGE FILE LOCATION OAT BEGIN:\n";
129     os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatDataBegin());
130 
131     os << "IMAGE FILE LOCATION:\n";
132     const std::string image_file_location(oat_header.GetImageFileLocation());
133     os << image_file_location;
134     if (!image_file_location.empty() && !host_prefix_.empty()) {
135       os << " (" << host_prefix_ << image_file_location << ")";
136     }
137     os << "\n\n";
138 
139     os << "BEGIN:\n";
140     os << reinterpret_cast<const void*>(oat_file_.Begin()) << "\n\n";
141 
142     os << "END:\n";
143     os << reinterpret_cast<const void*>(oat_file_.End()) << "\n\n";
144 
145     os << std::flush;
146 
147     for (size_t i = 0; i < oat_dex_files_.size(); i++) {
148       const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
149       CHECK(oat_dex_file != NULL);
150       DumpOatDexFile(os, *oat_dex_file);
151     }
152   }
153 
ComputeSize(const void * oat_data)154   size_t ComputeSize(const void* oat_data) {
155     if (reinterpret_cast<const byte*>(oat_data) < oat_file_.Begin() ||
156         reinterpret_cast<const byte*>(oat_data) > oat_file_.End()) {
157       return 0;  // Address not in oat file
158     }
159     uint32_t begin_offset = reinterpret_cast<size_t>(oat_data) -
160                             reinterpret_cast<size_t>(oat_file_.Begin());
161     typedef std::set<uint32_t>::iterator It;
162     It it = offsets_.upper_bound(begin_offset);
163     CHECK(it != offsets_.end());
164     uint32_t end_offset = *it;
165     return end_offset - begin_offset;
166   }
167 
GetInstructionSet()168   InstructionSet GetInstructionSet() {
169     return oat_file_.GetOatHeader().GetInstructionSet();
170   }
171 
GetOatCode(mirror::ArtMethod * m)172   const void* GetOatCode(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
173     MethodHelper mh(m);
174     for (size_t i = 0; i < oat_dex_files_.size(); i++) {
175       const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
176       CHECK(oat_dex_file != NULL);
177       UniquePtr<const DexFile> dex_file(oat_dex_file->OpenDexFile());
178       if (dex_file.get() != NULL) {
179         const DexFile::ClassDef* class_def =
180             dex_file->FindClassDef(mh.GetDeclaringClassDescriptor());
181         if (class_def != NULL) {
182           uint16_t class_def_index = dex_file->GetIndexForClassDef(*class_def);
183           const OatFile::OatClass* oat_class = oat_dex_file->GetOatClass(class_def_index);
184           CHECK(oat_class != NULL);
185           size_t method_index = m->GetMethodIndex();
186           return oat_class->GetOatMethod(method_index).GetCode();
187         }
188       }
189     }
190     return NULL;
191   }
192 
193  private:
AddAllOffsets()194   void AddAllOffsets() {
195     // We don't know the length of the code for each method, but we need to know where to stop
196     // when disassembling. What we do know is that a region of code will be followed by some other
197     // region, so if we keep a sorted sequence of the start of each region, we can infer the length
198     // of a piece of code by using upper_bound to find the start of the next region.
199     for (size_t i = 0; i < oat_dex_files_.size(); i++) {
200       const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
201       CHECK(oat_dex_file != NULL);
202       UniquePtr<const DexFile> dex_file(oat_dex_file->OpenDexFile());
203       if (dex_file.get() == NULL) {
204         continue;
205       }
206       offsets_.insert(reinterpret_cast<uint32_t>(&dex_file->GetHeader()));
207       for (size_t class_def_index = 0; class_def_index < dex_file->NumClassDefs(); class_def_index++) {
208         const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
209         UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(class_def_index));
210         const byte* class_data = dex_file->GetClassData(class_def);
211         if (class_data != NULL) {
212           ClassDataItemIterator it(*dex_file, class_data);
213           SkipAllFields(it);
214           uint32_t class_method_index = 0;
215           while (it.HasNextDirectMethod()) {
216             AddOffsets(oat_class->GetOatMethod(class_method_index++));
217             it.Next();
218           }
219           while (it.HasNextVirtualMethod()) {
220             AddOffsets(oat_class->GetOatMethod(class_method_index++));
221             it.Next();
222           }
223         }
224       }
225     }
226 
227     // If the last thing in the file is code for a method, there won't be an offset for the "next"
228     // thing. Instead of having a special case in the upper_bound code, let's just add an entry
229     // for the end of the file.
230     offsets_.insert(static_cast<uint32_t>(oat_file_.Size()));
231   }
232 
AddOffsets(const OatFile::OatMethod & oat_method)233   void AddOffsets(const OatFile::OatMethod& oat_method) {
234     uint32_t code_offset = oat_method.GetCodeOffset();
235     if (oat_file_.GetOatHeader().GetInstructionSet() == kThumb2) {
236       code_offset &= ~0x1;
237     }
238     offsets_.insert(code_offset);
239     offsets_.insert(oat_method.GetMappingTableOffset());
240     offsets_.insert(oat_method.GetVmapTableOffset());
241     offsets_.insert(oat_method.GetNativeGcMapOffset());
242   }
243 
DumpOatDexFile(std::ostream & os,const OatFile::OatDexFile & oat_dex_file)244   void DumpOatDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) {
245     os << "OAT DEX FILE:\n";
246     os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str());
247     os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum());
248     UniquePtr<const DexFile> dex_file(oat_dex_file.OpenDexFile());
249     if (dex_file.get() == NULL) {
250       os << "NOT FOUND\n\n";
251       return;
252     }
253     for (size_t class_def_index = 0; class_def_index < dex_file->NumClassDefs(); class_def_index++) {
254       const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
255       const char* descriptor = dex_file->GetClassDescriptor(class_def);
256       UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file.GetOatClass(class_def_index));
257       CHECK(oat_class.get() != NULL);
258       os << StringPrintf("%zd: %s (type_idx=%d) (", class_def_index, descriptor, class_def.class_idx_)
259          << oat_class->GetStatus() << ")\n";
260       Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
261       std::ostream indented_os(&indent_filter);
262       DumpOatClass(indented_os, *oat_class.get(), *(dex_file.get()), class_def);
263     }
264 
265     os << std::flush;
266   }
267 
SkipAllFields(ClassDataItemIterator & it)268   static void SkipAllFields(ClassDataItemIterator& it) {
269     while (it.HasNextStaticField()) {
270       it.Next();
271     }
272     while (it.HasNextInstanceField()) {
273       it.Next();
274     }
275   }
276 
DumpOatClass(std::ostream & os,const OatFile::OatClass & oat_class,const DexFile & dex_file,const DexFile::ClassDef & class_def)277   void DumpOatClass(std::ostream& os, const OatFile::OatClass& oat_class, const DexFile& dex_file,
278                     const DexFile::ClassDef& class_def) {
279     const byte* class_data = dex_file.GetClassData(class_def);
280     if (class_data == NULL) {  // empty class such as a marker interface?
281       return;
282     }
283     ClassDataItemIterator it(dex_file, class_data);
284     SkipAllFields(it);
285     uint32_t class_method_idx = 0;
286     while (it.HasNextDirectMethod()) {
287       const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
288       DumpOatMethod(os, class_def, class_method_idx, oat_method, dex_file,
289                     it.GetMemberIndex(), it.GetMethodCodeItem(), it.GetMemberAccessFlags());
290       class_method_idx++;
291       it.Next();
292     }
293     while (it.HasNextVirtualMethod()) {
294       const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
295       DumpOatMethod(os, class_def, class_method_idx, oat_method, dex_file,
296                     it.GetMemberIndex(), it.GetMethodCodeItem(), it.GetMemberAccessFlags());
297       class_method_idx++;
298       it.Next();
299     }
300     DCHECK(!it.HasNext());
301     os << std::flush;
302   }
303 
DumpOatMethod(std::ostream & os,const DexFile::ClassDef & class_def,uint32_t class_method_index,const OatFile::OatMethod & oat_method,const DexFile & dex_file,uint32_t dex_method_idx,const DexFile::CodeItem * code_item,uint32_t method_access_flags)304   void DumpOatMethod(std::ostream& os, const DexFile::ClassDef& class_def,
305                      uint32_t class_method_index,
306                      const OatFile::OatMethod& oat_method, const DexFile& dex_file,
307                      uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
308                      uint32_t method_access_flags) {
309     os << StringPrintf("%d: %s (dex_method_idx=%d)\n",
310                        class_method_index, PrettyMethod(dex_method_idx, dex_file, true).c_str(),
311                        dex_method_idx);
312     Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
313     std::ostream indent1_os(&indent1_filter);
314     {
315       indent1_os << "DEX CODE:\n";
316       Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
317       std::ostream indent2_os(&indent2_filter);
318       DumpDexCode(indent2_os, dex_file, code_item);
319     }
320     if (Runtime::Current() != NULL) {
321       indent1_os << "VERIFIER TYPE ANALYSIS:\n";
322       Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
323       std::ostream indent2_os(&indent2_filter);
324       DumpVerifier(indent2_os, dex_method_idx, &dex_file, class_def, code_item,
325                    method_access_flags);
326     }
327     {
328       indent1_os << "OAT DATA:\n";
329       Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
330       std::ostream indent2_os(&indent2_filter);
331 
332       indent2_os << StringPrintf("frame_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes());
333       indent2_os << StringPrintf("core_spill_mask: 0x%08x ", oat_method.GetCoreSpillMask());
334       DumpSpillMask(indent2_os, oat_method.GetCoreSpillMask(), false);
335       indent2_os << StringPrintf("\nfp_spill_mask: 0x%08x ", oat_method.GetFpSpillMask());
336       DumpSpillMask(indent2_os, oat_method.GetFpSpillMask(), true);
337       indent2_os << StringPrintf("\nvmap_table: %p (offset=0x%08x)\n",
338                                  oat_method.GetVmapTable(), oat_method.GetVmapTableOffset());
339       DumpVmap(indent2_os, oat_method);
340       indent2_os << StringPrintf("mapping_table: %p (offset=0x%08x)\n",
341                                  oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
342       const bool kDumpRawMappingTable = false;
343       if (kDumpRawMappingTable) {
344         Indenter indent3_filter(indent2_os.rdbuf(), kIndentChar, kIndentBy1Count);
345         std::ostream indent3_os(&indent3_filter);
346         DumpMappingTable(indent3_os, oat_method);
347       }
348       indent2_os << StringPrintf("gc_map: %p (offset=0x%08x)\n",
349                                  oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset());
350       const bool kDumpRawGcMap = false;
351       if (kDumpRawGcMap) {
352         Indenter indent3_filter(indent2_os.rdbuf(), kIndentChar, kIndentBy1Count);
353         std::ostream indent3_os(&indent3_filter);
354         DumpGcMap(indent3_os, oat_method, code_item);
355       }
356     }
357     {
358       indent1_os << StringPrintf("CODE: %p (offset=0x%08x size=%d)%s\n",
359                                  oat_method.GetCode(),
360                                  oat_method.GetCodeOffset(),
361                                  oat_method.GetCodeSize(),
362                                  oat_method.GetCode() != NULL ? "..." : "");
363       Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
364       std::ostream indent2_os(&indent2_filter);
365       DumpCode(indent2_os, oat_method, dex_method_idx, &dex_file, class_def, code_item,
366                method_access_flags);
367     }
368   }
369 
DumpSpillMask(std::ostream & os,uint32_t spill_mask,bool is_float)370   void DumpSpillMask(std::ostream& os, uint32_t spill_mask, bool is_float) {
371     if (spill_mask == 0) {
372       return;
373     }
374     os << "(";
375     for (size_t i = 0; i < 32; i++) {
376       if ((spill_mask & (1 << i)) != 0) {
377         if (is_float) {
378           os << "fr" << i;
379         } else {
380           os << "r" << i;
381         }
382         spill_mask ^= 1 << i;  // clear bit
383         if (spill_mask != 0) {
384           os << ", ";
385         } else {
386           break;
387         }
388       }
389     }
390     os << ")";
391   }
392 
DumpVmap(std::ostream & os,const OatFile::OatMethod & oat_method)393   void DumpVmap(std::ostream& os, const OatFile::OatMethod& oat_method) {
394     const uint8_t* raw_table = oat_method.GetVmapTable();
395     if (raw_table != NULL) {
396       const VmapTable vmap_table(raw_table);
397       bool first = true;
398       bool processing_fp = false;
399       uint32_t spill_mask = oat_method.GetCoreSpillMask();
400       for (size_t i = 0; i < vmap_table.Size(); i++) {
401         uint16_t dex_reg = vmap_table[i];
402         uint32_t cpu_reg = vmap_table.ComputeRegister(spill_mask, i,
403                                                       processing_fp ? kFloatVReg : kIntVReg);
404         os << (first ? "v" : ", v")  << dex_reg;
405         if (!processing_fp) {
406           os << "/r" << cpu_reg;
407         } else {
408           os << "/fr" << cpu_reg;
409         }
410         first = false;
411         if (!processing_fp && dex_reg == 0xFFFF) {
412           processing_fp = true;
413           spill_mask = oat_method.GetFpSpillMask();
414         }
415       }
416       os << "\n";
417     }
418   }
419 
DescribeVReg(std::ostream & os,const OatFile::OatMethod & oat_method,const DexFile::CodeItem * code_item,size_t reg,VRegKind kind)420   void DescribeVReg(std::ostream& os, const OatFile::OatMethod& oat_method,
421                     const DexFile::CodeItem* code_item, size_t reg, VRegKind kind) {
422     const uint8_t* raw_table = oat_method.GetVmapTable();
423     if (raw_table != NULL) {
424       const VmapTable vmap_table(raw_table);
425       uint32_t vmap_offset;
426       if (vmap_table.IsInContext(reg, kind, &vmap_offset)) {
427         bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
428         uint32_t spill_mask = is_float ? oat_method.GetFpSpillMask()
429                                        : oat_method.GetCoreSpillMask();
430         os << (is_float ? "fr" : "r") << vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
431       } else {
432         uint32_t offset = StackVisitor::GetVRegOffset(code_item, oat_method.GetCoreSpillMask(),
433                                                       oat_method.GetFpSpillMask(),
434                                                       oat_method.GetFrameSizeInBytes(), reg);
435         os << "[sp + #" << offset << "]";
436       }
437     }
438   }
439 
DumpGcMap(std::ostream & os,const OatFile::OatMethod & oat_method,const DexFile::CodeItem * code_item)440   void DumpGcMap(std::ostream& os, const OatFile::OatMethod& oat_method,
441                  const DexFile::CodeItem* code_item) {
442     const uint8_t* gc_map_raw = oat_method.GetNativeGcMap();
443     if (gc_map_raw == NULL) {
444       return;
445     }
446     NativePcOffsetToReferenceMap map(gc_map_raw);
447     const void* code = oat_method.GetCode();
448     for (size_t entry = 0; entry < map.NumEntries(); entry++) {
449       const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code) +
450                                  map.GetNativePcOffset(entry);
451       os << StringPrintf("%p", native_pc);
452       size_t num_regs = map.RegWidth() * 8;
453       const uint8_t* reg_bitmap = map.GetBitMap(entry);
454       bool first = true;
455       for (size_t reg = 0; reg < num_regs; reg++) {
456         if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) {
457           if (first) {
458             os << "  v" << reg << " (";
459             DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
460             os << ")";
461             first = false;
462           } else {
463             os << ", v" << reg << " (";
464             DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
465             os << ")";
466           }
467         }
468       }
469       os << "\n";
470     }
471   }
472 
DumpMappingTable(std::ostream & os,const OatFile::OatMethod & oat_method)473   void DumpMappingTable(std::ostream& os, const OatFile::OatMethod& oat_method) {
474     const void* code = oat_method.GetCode();
475     if (code == NULL) {
476       return;
477     }
478     MappingTable table(oat_method.GetMappingTable());
479     if (table.TotalSize() != 0) {
480       Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
481       std::ostream indent_os(&indent_filter);
482       if (table.PcToDexSize() != 0) {
483         typedef MappingTable::PcToDexIterator It;
484         os << "suspend point mappings {\n";
485         for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
486           indent_os << StringPrintf("0x%04x -> 0x%04x\n", cur.NativePcOffset(), cur.DexPc());
487         }
488         os << "}\n";
489       }
490       if (table.DexToPcSize() != 0) {
491         typedef MappingTable::DexToPcIterator It;
492         os << "catch entry mappings {\n";
493         for (It cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) {
494           indent_os << StringPrintf("0x%04x -> 0x%04x\n", cur.NativePcOffset(), cur.DexPc());
495         }
496         os << "}\n";
497       }
498     }
499   }
500 
DumpMappingAtOffset(std::ostream & os,const OatFile::OatMethod & oat_method,size_t offset,bool suspend_point_mapping)501   uint32_t DumpMappingAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method,
502                                size_t offset, bool suspend_point_mapping) {
503     MappingTable table(oat_method.GetMappingTable());
504     if (suspend_point_mapping && table.PcToDexSize() > 0) {
505       typedef MappingTable::PcToDexIterator It;
506       for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
507         if (offset == cur.NativePcOffset()) {
508           os << StringPrintf("suspend point dex PC: 0x%04x\n", cur.DexPc());
509           return cur.DexPc();
510         }
511       }
512     } else if (!suspend_point_mapping && table.DexToPcSize() > 0) {
513       typedef MappingTable::DexToPcIterator It;
514       for (It cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) {
515         if (offset == cur.NativePcOffset()) {
516           os << StringPrintf("catch entry dex PC: 0x%04x\n", cur.DexPc());
517           return cur.DexPc();
518         }
519       }
520     }
521     return DexFile::kDexNoIndex;
522   }
523 
DumpGcMapAtNativePcOffset(std::ostream & os,const OatFile::OatMethod & oat_method,const DexFile::CodeItem * code_item,size_t native_pc_offset)524   void DumpGcMapAtNativePcOffset(std::ostream& os, const OatFile::OatMethod& oat_method,
525                                  const DexFile::CodeItem* code_item, size_t native_pc_offset) {
526     const uint8_t* gc_map_raw = oat_method.GetNativeGcMap();
527     if (gc_map_raw != NULL) {
528       NativePcOffsetToReferenceMap map(gc_map_raw);
529       if (map.HasEntry(native_pc_offset)) {
530         size_t num_regs = map.RegWidth() * 8;
531         const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
532         bool first = true;
533         for (size_t reg = 0; reg < num_regs; reg++) {
534           if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) {
535             if (first) {
536               os << "GC map objects:  v" << reg << " (";
537               DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
538               os << ")";
539               first = false;
540             } else {
541               os << ", v" << reg << " (";
542               DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
543               os << ")";
544             }
545           }
546         }
547         if (!first) {
548           os << "\n";
549         }
550       }
551     }
552   }
553 
DumpVRegsAtDexPc(std::ostream & os,const OatFile::OatMethod & oat_method,uint32_t dex_method_idx,const DexFile * dex_file,const DexFile::ClassDef & class_def,const DexFile::CodeItem * code_item,uint32_t method_access_flags,uint32_t dex_pc)554   void DumpVRegsAtDexPc(std::ostream& os,  const OatFile::OatMethod& oat_method,
555                         uint32_t dex_method_idx, const DexFile* dex_file,
556                         const DexFile::ClassDef& class_def, const DexFile::CodeItem* code_item,
557                         uint32_t method_access_flags, uint32_t dex_pc) {
558     static UniquePtr<verifier::MethodVerifier> verifier;
559     static const DexFile* verified_dex_file = NULL;
560     static uint32_t verified_dex_method_idx = DexFile::kDexNoIndex;
561     if (dex_file != verified_dex_file || verified_dex_method_idx != dex_method_idx) {
562       ScopedObjectAccess soa(Thread::Current());
563       mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file);
564       mirror::ClassLoader* class_loader = NULL;
565       verifier.reset(new verifier::MethodVerifier(dex_file, dex_cache, class_loader, &class_def,
566                                                   code_item, dex_method_idx, NULL,
567                                                   method_access_flags, true, true));
568       verifier->Verify();
569       verified_dex_file = dex_file;
570       verified_dex_method_idx = dex_method_idx;
571     }
572     std::vector<int32_t> kinds = verifier->DescribeVRegs(dex_pc);
573     bool first = true;
574     for (size_t reg = 0; reg < code_item->registers_size_; reg++) {
575       VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2));
576       if (kind != kUndefined) {
577         if (first) {
578           os << "VRegs:  v";
579           first = false;
580         } else {
581           os << ", v";
582         }
583         os << reg << " (";
584         switch (kind) {
585           case kImpreciseConstant:
586             os << "Imprecise Constant: " << kinds.at((reg * 2) + 1) << ", ";
587             DescribeVReg(os, oat_method, code_item, reg, kind);
588             break;
589           case kConstant:
590             os << "Constant: " << kinds.at((reg * 2) + 1);
591             break;
592           default:
593             DescribeVReg(os, oat_method, code_item, reg, kind);
594             break;
595         }
596         os << ")";
597       }
598     }
599     if (!first) {
600       os << "\n";
601     }
602   }
603 
604 
DumpDexCode(std::ostream & os,const DexFile & dex_file,const DexFile::CodeItem * code_item)605   void DumpDexCode(std::ostream& os, const DexFile& dex_file, const DexFile::CodeItem* code_item) {
606     if (code_item != NULL) {
607       size_t i = 0;
608       while (i < code_item->insns_size_in_code_units_) {
609         const Instruction* instruction = Instruction::At(&code_item->insns_[i]);
610         os << StringPrintf("0x%04zx: %s\n", i, instruction->DumpString(&dex_file).c_str());
611         i += instruction->SizeInCodeUnits();
612       }
613     }
614   }
615 
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)616   void DumpVerifier(std::ostream& os, uint32_t dex_method_idx, const DexFile* dex_file,
617                     const DexFile::ClassDef& class_def, const DexFile::CodeItem* code_item,
618                     uint32_t method_access_flags) {
619     if ((method_access_flags & kAccNative) == 0) {
620       ScopedObjectAccess soa(Thread::Current());
621       mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file);
622       mirror::ClassLoader* class_loader = NULL;
623       verifier::MethodVerifier::VerifyMethodAndDump(os, dex_method_idx, dex_file, dex_cache,
624                                                     class_loader, &class_def, code_item, NULL,
625                                                     method_access_flags);
626     }
627   }
628 
DumpCode(std::ostream & os,const OatFile::OatMethod & oat_method,uint32_t dex_method_idx,const DexFile * dex_file,const DexFile::ClassDef & class_def,const DexFile::CodeItem * code_item,uint32_t method_access_flags)629   void DumpCode(std::ostream& os,  const OatFile::OatMethod& oat_method,
630                 uint32_t dex_method_idx, const DexFile* dex_file,
631                 const DexFile::ClassDef& class_def, const DexFile::CodeItem* code_item,
632                 uint32_t method_access_flags) {
633     const void* code = oat_method.GetCode();
634     size_t code_size = oat_method.GetCodeSize();
635     if (code == NULL || code_size == 0) {
636       os << "NO CODE!\n";
637       return;
638     }
639     const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code);
640     size_t offset = 0;
641     const bool kDumpVRegs = (Runtime::Current() != NULL);
642     while (offset < code_size) {
643       DumpMappingAtOffset(os, oat_method, offset, false);
644       offset += disassembler_->Dump(os, native_pc + offset);
645       uint32_t dex_pc = DumpMappingAtOffset(os, oat_method, offset, true);
646       if (dex_pc != DexFile::kDexNoIndex) {
647         DumpGcMapAtNativePcOffset(os, oat_method, code_item, offset);
648         if (kDumpVRegs) {
649           DumpVRegsAtDexPc(os, oat_method, dex_method_idx, dex_file, class_def, code_item,
650                            method_access_flags, dex_pc);
651         }
652       }
653     }
654   }
655 
656   const std::string host_prefix_;
657   const OatFile& oat_file_;
658   std::vector<const OatFile::OatDexFile*> oat_dex_files_;
659   std::set<uint32_t> offsets_;
660   UniquePtr<Disassembler> disassembler_;
661 };
662 
663 class ImageDumper {
664  public:
ImageDumper(std::ostream * os,const std::string & image_filename,const std::string & host_prefix,gc::space::ImageSpace & image_space,const ImageHeader & image_header)665   explicit ImageDumper(std::ostream* os, const std::string& image_filename,
666                        const std::string& host_prefix, gc::space::ImageSpace& image_space,
667                        const ImageHeader& image_header)
668       : os_(os), image_filename_(image_filename), host_prefix_(host_prefix),
669         image_space_(image_space), image_header_(image_header) {}
670 
Dump()671   void Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
672     std::ostream& os = *os_;
673     os << "MAGIC: " << image_header_.GetMagic() << "\n\n";
674 
675     os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n";
676 
677     os << "IMAGE BITMAP OFFSET: " << reinterpret_cast<void*>(image_header_.GetImageBitmapOffset())
678        << " SIZE: " << reinterpret_cast<void*>(image_header_.GetImageBitmapSize()) << "\n\n";
679 
680     os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum());
681 
682     os << "OAT FILE BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatFileBegin()) << "\n\n";
683 
684     os << "OAT DATA BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatDataBegin()) << "\n\n";
685 
686     os << "OAT DATA END:" << reinterpret_cast<void*>(image_header_.GetOatDataEnd()) << "\n\n";
687 
688     os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n";
689 
690     {
691       os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
692       Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
693       std::ostream indent1_os(&indent1_filter);
694       CHECK_EQ(arraysize(image_roots_descriptions_), size_t(ImageHeader::kImageRootsMax));
695       for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
696         ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
697         const char* image_root_description = image_roots_descriptions_[i];
698         mirror::Object* image_root_object = image_header_.GetImageRoot(image_root);
699         indent1_os << StringPrintf("%s: %p\n", image_root_description, image_root_object);
700         if (image_root_object->IsObjectArray()) {
701           Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
702           std::ostream indent2_os(&indent2_filter);
703           // TODO: replace down_cast with AsObjectArray (g++ currently has a problem with this)
704           mirror::ObjectArray<mirror::Object>* image_root_object_array
705               = down_cast<mirror::ObjectArray<mirror::Object>*>(image_root_object);
706           //  = image_root_object->AsObjectArray<Object>();
707           for (int i = 0; i < image_root_object_array->GetLength(); i++) {
708             mirror::Object* value = image_root_object_array->Get(i);
709             if (value != NULL) {
710               indent2_os << i << ": ";
711               PrettyObjectValue(indent2_os, value->GetClass(), value);
712             } else {
713               indent2_os << i << ": null\n";
714             }
715           }
716         }
717       }
718     }
719     os << "\n";
720 
721     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
722     mirror::Object* oat_location_object = image_header_.GetImageRoot(ImageHeader::kOatLocation);
723     std::string oat_location(oat_location_object->AsString()->ToModifiedUtf8());
724     os << "OAT LOCATION: " << oat_location;
725     if (!host_prefix_.empty()) {
726       oat_location = host_prefix_ + oat_location;
727       os << " (" << oat_location << ")";
728     }
729     os << "\n";
730     const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location);
731     if (oat_file == NULL) {
732       os << "NOT FOUND\n";
733       return;
734     }
735     os << "\n";
736 
737     stats_.oat_file_bytes = oat_file->Size();
738 
739     oat_dumper_.reset(new OatDumper(host_prefix_, *oat_file));
740 
741     for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
742       CHECK(oat_dex_file != NULL);
743       stats_.oat_dex_file_sizes.push_back(std::make_pair(oat_dex_file->GetDexFileLocation(),
744                                                          oat_dex_file->FileSize()));
745     }
746 
747     os << "OBJECTS:\n" << std::flush;
748 
749     // Loop through all the image spaces and dump their objects.
750     gc::Heap* heap = Runtime::Current()->GetHeap();
751     const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces();
752     Thread* self = Thread::Current();
753     {
754       WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
755       heap->FlushAllocStack();
756     }
757     {
758       std::ostream* saved_os = os_;
759       Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
760       std::ostream indent_os(&indent_filter);
761       os_ = &indent_os;
762       ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
763       for (const auto& space : spaces) {
764         if (space->IsImageSpace()) {
765           gc::space::ImageSpace* image_space = space->AsImageSpace();
766           image_space->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
767           indent_os << "\n";
768         }
769       }
770       // Dump the large objects separately.
771       heap->GetLargeObjectsSpace()->GetLiveObjects()->Walk(ImageDumper::Callback, this);
772       indent_os << "\n";
773       os_ = saved_os;
774     }
775     os << "STATS:\n" << std::flush;
776     UniquePtr<File> file(OS::OpenFileForReading(image_filename_.c_str()));
777     if (file.get() == NULL) {
778       std::string cache_location(GetDalvikCacheFilenameOrDie(image_filename_));
779       file.reset(OS::OpenFileForReading(cache_location.c_str()));
780       if (file.get() == NULL) {
781           LOG(WARNING) << "Failed to find image in " << image_filename_
782                        << " and " << cache_location;
783       }
784     }
785     if (file.get() != NULL) {
786         stats_.file_bytes = file->GetLength();
787     }
788     size_t header_bytes = sizeof(ImageHeader);
789     stats_.header_bytes = header_bytes;
790     size_t alignment_bytes = RoundUp(header_bytes, kObjectAlignment) - header_bytes;
791     stats_.alignment_bytes += alignment_bytes;
792     stats_.alignment_bytes += image_header_.GetImageBitmapOffset() - image_header_.GetImageSize();
793     stats_.bitmap_bytes += image_header_.GetImageBitmapSize();
794     stats_.Dump(os);
795     os << "\n";
796 
797     os << std::flush;
798 
799     oat_dumper_->Dump(os);
800   }
801 
802  private:
PrettyObjectValue(std::ostream & os,mirror::Class * type,mirror::Object * value)803   static void PrettyObjectValue(std::ostream& os, mirror::Class* type, mirror::Object* value)
804       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
805     CHECK(type != NULL);
806     if (value == NULL) {
807       os << StringPrintf("null   %s\n", PrettyDescriptor(type).c_str());
808     } else if (type->IsStringClass()) {
809       mirror::String* string = value->AsString();
810       os << StringPrintf("%p   String: %s\n", string,
811                          PrintableString(string->ToModifiedUtf8()).c_str());
812     } else if (type->IsClassClass()) {
813       mirror::Class* klass = value->AsClass();
814       os << StringPrintf("%p   Class: %s\n", klass, PrettyDescriptor(klass).c_str());
815     } else if (type->IsArtFieldClass()) {
816       mirror::ArtField* field = value->AsArtField();
817       os << StringPrintf("%p   Field: %s\n", field, PrettyField(field).c_str());
818     } else if (type->IsArtMethodClass()) {
819       mirror::ArtMethod* method = value->AsArtMethod();
820       os << StringPrintf("%p   Method: %s\n", method, PrettyMethod(method).c_str());
821     } else {
822       os << StringPrintf("%p   %s\n", value, PrettyDescriptor(type).c_str());
823     }
824   }
825 
PrintField(std::ostream & os,mirror::ArtField * field,mirror::Object * obj)826   static void PrintField(std::ostream& os, mirror::ArtField* field, mirror::Object* obj)
827       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
828     FieldHelper fh(field);
829     const char* descriptor = fh.GetTypeDescriptor();
830     os << StringPrintf("%s: ", fh.GetName());
831     if (descriptor[0] != 'L' && descriptor[0] != '[') {
832       mirror::Class* type = fh.GetType();
833       if (type->IsPrimitiveLong()) {
834         os << StringPrintf("%lld (0x%llx)\n", field->Get64(obj), field->Get64(obj));
835       } else if (type->IsPrimitiveDouble()) {
836         os << StringPrintf("%f (%a)\n", field->GetDouble(obj), field->GetDouble(obj));
837       } else if (type->IsPrimitiveFloat()) {
838         os << StringPrintf("%f (%a)\n", field->GetFloat(obj), field->GetFloat(obj));
839       } else {
840         DCHECK(type->IsPrimitive());
841         os << StringPrintf("%d (0x%x)\n", field->Get32(obj), field->Get32(obj));
842       }
843     } else {
844       // Get the value, don't compute the type unless it is non-null as we don't want
845       // to cause class loading.
846       mirror::Object* value = field->GetObj(obj);
847       if (value == NULL) {
848         os << StringPrintf("null   %s\n", PrettyDescriptor(descriptor).c_str());
849       } else {
850         // Grab the field type without causing resolution.
851         mirror::Class* field_type = fh.GetType(false);
852         if (field_type != NULL) {
853           PrettyObjectValue(os, field_type, value);
854         } else {
855           os << StringPrintf("%p   %s\n", value, PrettyDescriptor(descriptor).c_str());
856         }
857       }
858     }
859   }
860 
DumpFields(std::ostream & os,mirror::Object * obj,mirror::Class * klass)861   static void DumpFields(std::ostream& os, mirror::Object* obj, mirror::Class* klass)
862       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
863     mirror::Class* super = klass->GetSuperClass();
864     if (super != NULL) {
865       DumpFields(os, obj, super);
866     }
867     mirror::ObjectArray<mirror::ArtField>* fields = klass->GetIFields();
868     if (fields != NULL) {
869       for (int32_t i = 0; i < fields->GetLength(); i++) {
870         mirror::ArtField* field = fields->Get(i);
871         PrintField(os, field, obj);
872       }
873     }
874   }
875 
InDumpSpace(const mirror::Object * object)876   bool InDumpSpace(const mirror::Object* object) {
877     return image_space_.Contains(object);
878   }
879 
GetOatCodeBegin(mirror::ArtMethod * m)880   const void* GetOatCodeBegin(mirror::ArtMethod* m)
881       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
882     const void* code = m->GetEntryPointFromCompiledCode();
883     if (code == GetResolutionTrampoline(Runtime::Current()->GetClassLinker())) {
884       code = oat_dumper_->GetOatCode(m);
885     }
886     if (oat_dumper_->GetInstructionSet() == kThumb2) {
887       code = reinterpret_cast<void*>(reinterpret_cast<uint32_t>(code) & ~0x1);
888     }
889     return code;
890   }
891 
GetOatCodeSize(mirror::ArtMethod * m)892   uint32_t GetOatCodeSize(mirror::ArtMethod* m)
893       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
894     const uint32_t* oat_code_begin = reinterpret_cast<const uint32_t*>(GetOatCodeBegin(m));
895     if (oat_code_begin == NULL) {
896       return 0;
897     }
898     return oat_code_begin[-1];
899   }
900 
GetOatCodeEnd(mirror::ArtMethod * m)901   const void* GetOatCodeEnd(mirror::ArtMethod* m)
902       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
903     const uint8_t* oat_code_begin = reinterpret_cast<const uint8_t*>(GetOatCodeBegin(m));
904     if (oat_code_begin == NULL) {
905       return NULL;
906     }
907     return oat_code_begin + GetOatCodeSize(m);
908   }
909 
Callback(mirror::Object * obj,void * arg)910   static void Callback(mirror::Object* obj, void* arg)
911       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
912     DCHECK(obj != NULL);
913     DCHECK(arg != NULL);
914     ImageDumper* state = reinterpret_cast<ImageDumper*>(arg);
915     if (!state->InDumpSpace(obj)) {
916       return;
917     }
918 
919     size_t object_bytes = obj->SizeOf();
920     size_t alignment_bytes = RoundUp(object_bytes, kObjectAlignment) - object_bytes;
921     state->stats_.object_bytes += object_bytes;
922     state->stats_.alignment_bytes += alignment_bytes;
923 
924     std::ostream& os = *state->os_;
925     mirror::Class* obj_class = obj->GetClass();
926     if (obj_class->IsArrayClass()) {
927       os << StringPrintf("%p: %s length:%d\n", obj, PrettyDescriptor(obj_class).c_str(),
928                          obj->AsArray()->GetLength());
929     } else if (obj->IsClass()) {
930       mirror::Class* klass = obj->AsClass();
931       os << StringPrintf("%p: java.lang.Class \"%s\" (", obj, PrettyDescriptor(klass).c_str())
932          << klass->GetStatus() << ")\n";
933     } else if (obj->IsArtField()) {
934       os << StringPrintf("%p: java.lang.reflect.ArtField %s\n", obj,
935                          PrettyField(obj->AsArtField()).c_str());
936     } else if (obj->IsArtMethod()) {
937       os << StringPrintf("%p: java.lang.reflect.ArtMethod %s\n", obj,
938                          PrettyMethod(obj->AsArtMethod()).c_str());
939     } else if (obj_class->IsStringClass()) {
940       os << StringPrintf("%p: java.lang.String %s\n", obj,
941                          PrintableString(obj->AsString()->ToModifiedUtf8()).c_str());
942     } else {
943       os << StringPrintf("%p: %s\n", obj, PrettyDescriptor(obj_class).c_str());
944     }
945     Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
946     std::ostream indent_os(&indent_filter);
947     DumpFields(indent_os, obj, obj_class);
948     if (obj->IsObjectArray()) {
949       mirror::ObjectArray<mirror::Object>* obj_array = obj->AsObjectArray<mirror::Object>();
950       int32_t length = obj_array->GetLength();
951       for (int32_t i = 0; i < length; i++) {
952         mirror::Object* value = obj_array->Get(i);
953         size_t run = 0;
954         for (int32_t j = i + 1; j < length; j++) {
955           if (value == obj_array->Get(j)) {
956             run++;
957           } else {
958             break;
959           }
960         }
961         if (run == 0) {
962           indent_os << StringPrintf("%d: ", i);
963         } else {
964           indent_os << StringPrintf("%d to %zd: ", i, i + run);
965           i = i + run;
966         }
967         mirror::Class* value_class = value == NULL ? obj_class->GetComponentType() : value->GetClass();
968         PrettyObjectValue(indent_os, value_class, value);
969       }
970     } else if (obj->IsClass()) {
971       mirror::ObjectArray<mirror::ArtField>* sfields = obj->AsClass()->GetSFields();
972       if (sfields != NULL) {
973         indent_os << "STATICS:\n";
974         Indenter indent2_filter(indent_os.rdbuf(), kIndentChar, kIndentBy1Count);
975         std::ostream indent2_os(&indent2_filter);
976         for (int32_t i = 0; i < sfields->GetLength(); i++) {
977           mirror::ArtField* field = sfields->Get(i);
978           PrintField(indent2_os, field, field->GetDeclaringClass());
979         }
980       }
981     } else if (obj->IsArtMethod()) {
982       mirror::ArtMethod* method = obj->AsArtMethod();
983       if (method->IsNative()) {
984         DCHECK(method->GetNativeGcMap() == NULL) << PrettyMethod(method);
985         DCHECK(method->GetMappingTable() == NULL) << PrettyMethod(method);
986         bool first_occurrence;
987         const void* oat_code = state->GetOatCodeBegin(method);
988         uint32_t oat_code_size = state->GetOatCodeSize(method);
989         state->ComputeOatSize(oat_code, &first_occurrence);
990         if (first_occurrence) {
991           state->stats_.native_to_managed_code_bytes += oat_code_size;
992         }
993         if (oat_code != method->GetEntryPointFromCompiledCode()) {
994           indent_os << StringPrintf("OAT CODE: %p\n", oat_code);
995         }
996       } else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
997           method->IsResolutionMethod() || MethodHelper(method).IsClassInitializer()) {
998         DCHECK(method->GetNativeGcMap() == NULL) << PrettyMethod(method);
999         DCHECK(method->GetMappingTable() == NULL) << PrettyMethod(method);
1000       } else {
1001         // TODO: we check there is a GC map here, we may not have a GC map if the code is pointing
1002         //       to the quick/portable to interpreter bridge.
1003         CHECK(method->GetNativeGcMap() != NULL) << PrettyMethod(method);
1004 
1005         const DexFile::CodeItem* code_item = MethodHelper(method).GetCodeItem();
1006         size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
1007         state->stats_.dex_instruction_bytes += dex_instruction_bytes;
1008 
1009         bool first_occurrence;
1010         size_t gc_map_bytes = state->ComputeOatSize(method->GetNativeGcMap(), &first_occurrence);
1011         if (first_occurrence) {
1012           state->stats_.gc_map_bytes += gc_map_bytes;
1013         }
1014 
1015         size_t pc_mapping_table_bytes =
1016             state->ComputeOatSize(method->GetMappingTable(), &first_occurrence);
1017         if (first_occurrence) {
1018           state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
1019         }
1020 
1021         size_t vmap_table_bytes =
1022             state->ComputeOatSize(method->GetVmapTable(), &first_occurrence);
1023         if (first_occurrence) {
1024           state->stats_.vmap_table_bytes += vmap_table_bytes;
1025         }
1026 
1027         const void* oat_code_begin = state->GetOatCodeBegin(method);
1028         const void* oat_code_end = state->GetOatCodeEnd(method);
1029         uint32_t oat_code_size = state->GetOatCodeSize(method);
1030         state->ComputeOatSize(oat_code_begin, &first_occurrence);
1031         if (first_occurrence) {
1032           state->stats_.managed_code_bytes += oat_code_size;
1033           if (method->IsConstructor()) {
1034             if (method->IsStatic()) {
1035               state->stats_.class_initializer_code_bytes += oat_code_size;
1036             } else if (dex_instruction_bytes > kLargeConstructorDexBytes) {
1037               state->stats_.large_initializer_code_bytes += oat_code_size;
1038             }
1039           } else if (dex_instruction_bytes > kLargeMethodDexBytes) {
1040             state->stats_.large_method_code_bytes += oat_code_size;
1041           }
1042         }
1043         state->stats_.managed_code_bytes_ignoring_deduplication += oat_code_size;
1044 
1045         indent_os << StringPrintf("OAT CODE: %p-%p\n", oat_code_begin, oat_code_end);
1046         indent_os << StringPrintf("SIZE: Dex Instructions=%zd GC=%zd Mapping=%zd\n",
1047                                   dex_instruction_bytes, gc_map_bytes, pc_mapping_table_bytes);
1048 
1049         size_t total_size = dex_instruction_bytes + gc_map_bytes + pc_mapping_table_bytes +
1050             vmap_table_bytes + oat_code_size + object_bytes;
1051 
1052         double expansion =
1053             static_cast<double>(oat_code_size) / static_cast<double>(dex_instruction_bytes);
1054         state->stats_.ComputeOutliers(total_size, expansion, method);
1055       }
1056     }
1057     state->stats_.Update(ClassHelper(obj_class).GetDescriptor(), object_bytes);
1058   }
1059 
1060   std::set<const void*> already_seen_;
1061   // Compute the size of the given data within the oat file and whether this is the first time
1062   // this data has been requested
ComputeOatSize(const void * oat_data,bool * first_occurrence)1063   size_t ComputeOatSize(const void* oat_data, bool* first_occurrence) {
1064     if (already_seen_.count(oat_data) == 0) {
1065       *first_occurrence = true;
1066       already_seen_.insert(oat_data);
1067     } else {
1068       *first_occurrence = false;
1069     }
1070     return oat_dumper_->ComputeSize(oat_data);
1071   }
1072 
1073  public:
1074   struct Stats {
1075     size_t oat_file_bytes;
1076     size_t file_bytes;
1077 
1078     size_t header_bytes;
1079     size_t object_bytes;
1080     size_t bitmap_bytes;
1081     size_t alignment_bytes;
1082 
1083     size_t managed_code_bytes;
1084     size_t managed_code_bytes_ignoring_deduplication;
1085     size_t managed_to_native_code_bytes;
1086     size_t native_to_managed_code_bytes;
1087     size_t class_initializer_code_bytes;
1088     size_t large_initializer_code_bytes;
1089     size_t large_method_code_bytes;
1090 
1091     size_t gc_map_bytes;
1092     size_t pc_mapping_table_bytes;
1093     size_t vmap_table_bytes;
1094 
1095     size_t dex_instruction_bytes;
1096 
1097     std::vector<mirror::ArtMethod*> method_outlier;
1098     std::vector<size_t> method_outlier_size;
1099     std::vector<double> method_outlier_expansion;
1100     std::vector<std::pair<std::string, size_t> > oat_dex_file_sizes;
1101 
Statsart::ImageDumper::Stats1102     explicit Stats()
1103         : oat_file_bytes(0),
1104           file_bytes(0),
1105           header_bytes(0),
1106           object_bytes(0),
1107           bitmap_bytes(0),
1108           alignment_bytes(0),
1109           managed_code_bytes(0),
1110           managed_code_bytes_ignoring_deduplication(0),
1111           managed_to_native_code_bytes(0),
1112           native_to_managed_code_bytes(0),
1113           class_initializer_code_bytes(0),
1114           large_initializer_code_bytes(0),
1115           large_method_code_bytes(0),
1116           gc_map_bytes(0),
1117           pc_mapping_table_bytes(0),
1118           vmap_table_bytes(0),
1119           dex_instruction_bytes(0) {}
1120 
1121     struct SizeAndCount {
SizeAndCountart::ImageDumper::Stats::SizeAndCount1122       SizeAndCount(size_t bytes, size_t count) : bytes(bytes), count(count) {}
1123       size_t bytes;
1124       size_t count;
1125     };
1126     typedef SafeMap<std::string, SizeAndCount> SizeAndCountTable;
1127     SizeAndCountTable sizes_and_counts;
1128 
Updateart::ImageDumper::Stats1129     void Update(const std::string& descriptor, size_t object_bytes) {
1130       SizeAndCountTable::iterator it = sizes_and_counts.find(descriptor);
1131       if (it != sizes_and_counts.end()) {
1132         it->second.bytes += object_bytes;
1133         it->second.count += 1;
1134       } else {
1135         sizes_and_counts.Put(descriptor, SizeAndCount(object_bytes, 1));
1136       }
1137     }
1138 
PercentOfOatBytesart::ImageDumper::Stats1139     double PercentOfOatBytes(size_t size) {
1140       return (static_cast<double>(size) / static_cast<double>(oat_file_bytes)) * 100;
1141     }
1142 
PercentOfFileBytesart::ImageDumper::Stats1143     double PercentOfFileBytes(size_t size) {
1144       return (static_cast<double>(size) / static_cast<double>(file_bytes)) * 100;
1145     }
1146 
PercentOfObjectBytesart::ImageDumper::Stats1147     double PercentOfObjectBytes(size_t size) {
1148       return (static_cast<double>(size) / static_cast<double>(object_bytes)) * 100;
1149     }
1150 
ComputeOutliersart::ImageDumper::Stats1151     void ComputeOutliers(size_t total_size, double expansion, mirror::ArtMethod* method) {
1152       method_outlier_size.push_back(total_size);
1153       method_outlier_expansion.push_back(expansion);
1154       method_outlier.push_back(method);
1155     }
1156 
DumpOutliersart::ImageDumper::Stats1157     void DumpOutliers(std::ostream& os)
1158         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
1159       size_t sum_of_sizes = 0;
1160       size_t sum_of_sizes_squared = 0;
1161       size_t sum_of_expansion = 0;
1162       size_t sum_of_expansion_squared = 0;
1163       size_t n = method_outlier_size.size();
1164       for (size_t i = 0; i < n; i++) {
1165         size_t cur_size = method_outlier_size[i];
1166         sum_of_sizes += cur_size;
1167         sum_of_sizes_squared += cur_size * cur_size;
1168         double cur_expansion = method_outlier_expansion[i];
1169         sum_of_expansion += cur_expansion;
1170         sum_of_expansion_squared += cur_expansion * cur_expansion;
1171       }
1172       size_t size_mean = sum_of_sizes / n;
1173       size_t size_variance = (sum_of_sizes_squared - sum_of_sizes * size_mean) / (n - 1);
1174       double expansion_mean = sum_of_expansion / n;
1175       double expansion_variance =
1176           (sum_of_expansion_squared - sum_of_expansion * expansion_mean) / (n - 1);
1177 
1178       // Dump methods whose size is a certain number of standard deviations from the mean
1179       size_t dumped_values = 0;
1180       size_t skipped_values = 0;
1181       for (size_t i = 100; i > 0; i--) {  // i is the current number of standard deviations
1182         size_t cur_size_variance = i * i * size_variance;
1183         bool first = true;
1184         for (size_t j = 0; j < n; j++) {
1185           size_t cur_size = method_outlier_size[j];
1186           if (cur_size > size_mean) {
1187             size_t cur_var = cur_size - size_mean;
1188             cur_var = cur_var * cur_var;
1189             if (cur_var > cur_size_variance) {
1190               if (dumped_values > 20) {
1191                 if (i == 1) {
1192                   skipped_values++;
1193                 } else {
1194                   i = 2;  // jump to counting for 1 standard deviation
1195                   break;
1196                 }
1197               } else {
1198                 if (first) {
1199                   os << "\nBig methods (size > " << i << " standard deviations the norm):\n";
1200                   first = false;
1201                 }
1202                 os << PrettyMethod(method_outlier[j]) << " requires storage of "
1203                     << PrettySize(cur_size) << "\n";
1204                 method_outlier_size[j] = 0;  // don't consider this method again
1205                 dumped_values++;
1206               }
1207             }
1208           }
1209         }
1210       }
1211       if (skipped_values > 0) {
1212         os << "... skipped " << skipped_values
1213            << " methods with size > 1 standard deviation from the norm\n";
1214       }
1215       os << std::flush;
1216 
1217       // Dump methods whose expansion is a certain number of standard deviations from the mean
1218       dumped_values = 0;
1219       skipped_values = 0;
1220       for (size_t i = 10; i > 0; i--) {  // i is the current number of standard deviations
1221         double cur_expansion_variance = i * i * expansion_variance;
1222         bool first = true;
1223         for (size_t j = 0; j < n; j++) {
1224           double cur_expansion = method_outlier_expansion[j];
1225           if (cur_expansion > expansion_mean) {
1226             size_t cur_var = cur_expansion - expansion_mean;
1227             cur_var = cur_var * cur_var;
1228             if (cur_var > cur_expansion_variance) {
1229               if (dumped_values > 20) {
1230                 if (i == 1) {
1231                   skipped_values++;
1232                 } else {
1233                   i = 2;  // jump to counting for 1 standard deviation
1234                   break;
1235                 }
1236               } else {
1237                 if (first) {
1238                   os << "\nLarge expansion methods (size > " << i
1239                       << " standard deviations the norm):\n";
1240                   first = false;
1241                 }
1242                 os << PrettyMethod(method_outlier[j]) << " expanded code by "
1243                    << cur_expansion << "\n";
1244                 method_outlier_expansion[j] = 0.0;  // don't consider this method again
1245                 dumped_values++;
1246               }
1247             }
1248           }
1249         }
1250       }
1251       if (skipped_values > 0) {
1252         os << "... skipped " << skipped_values
1253            << " methods with expansion > 1 standard deviation from the norm\n";
1254       }
1255       os << "\n" << std::flush;
1256     }
1257 
Dumpart::ImageDumper::Stats1258     void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
1259       {
1260         os << "art_file_bytes = " << PrettySize(file_bytes) << "\n\n"
1261            << "art_file_bytes = header_bytes + object_bytes + alignment_bytes\n";
1262         Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
1263         std::ostream indent_os(&indent_filter);
1264         indent_os << StringPrintf("header_bytes    =  %8zd (%2.0f%% of art file bytes)\n"
1265                                   "object_bytes    =  %8zd (%2.0f%% of art file bytes)\n"
1266                                   "bitmap_bytes    =  %8zd (%2.0f%% of art file bytes)\n"
1267                                   "alignment_bytes =  %8zd (%2.0f%% of art file bytes)\n\n",
1268                                   header_bytes, PercentOfFileBytes(header_bytes),
1269                                   object_bytes, PercentOfFileBytes(object_bytes),
1270                                   bitmap_bytes, PercentOfFileBytes(bitmap_bytes),
1271                                   alignment_bytes, PercentOfFileBytes(alignment_bytes))
1272             << std::flush;
1273         CHECK_EQ(file_bytes, bitmap_bytes + header_bytes + object_bytes + alignment_bytes);
1274       }
1275 
1276       os << "object_bytes breakdown:\n";
1277       size_t object_bytes_total = 0;
1278       for (const auto& sizes_and_count : sizes_and_counts) {
1279         const std::string& descriptor(sizes_and_count.first);
1280         double average = static_cast<double>(sizes_and_count.second.bytes) /
1281             static_cast<double>(sizes_and_count.second.count);
1282         double percent = PercentOfObjectBytes(sizes_and_count.second.bytes);
1283         os << StringPrintf("%32s %8zd bytes %6zd instances "
1284                            "(%4.0f bytes/instance) %2.0f%% of object_bytes\n",
1285                            descriptor.c_str(), sizes_and_count.second.bytes,
1286                            sizes_and_count.second.count, average, percent);
1287         object_bytes_total += sizes_and_count.second.bytes;
1288       }
1289       os << "\n" << std::flush;
1290       CHECK_EQ(object_bytes, object_bytes_total);
1291 
1292       os << StringPrintf("oat_file_bytes               = %8zd\n"
1293                          "managed_code_bytes           = %8zd (%2.0f%% of oat file bytes)\n"
1294                          "managed_to_native_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
1295                          "native_to_managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n"
1296                          "class_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
1297                          "large_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
1298                          "large_method_code_bytes      = %8zd (%2.0f%% of oat file bytes)\n\n",
1299                          oat_file_bytes,
1300                          managed_code_bytes, PercentOfOatBytes(managed_code_bytes),
1301                          managed_to_native_code_bytes, PercentOfOatBytes(managed_to_native_code_bytes),
1302                          native_to_managed_code_bytes, PercentOfOatBytes(native_to_managed_code_bytes),
1303                          class_initializer_code_bytes, PercentOfOatBytes(class_initializer_code_bytes),
1304                          large_initializer_code_bytes, PercentOfOatBytes(large_initializer_code_bytes),
1305                          large_method_code_bytes, PercentOfOatBytes(large_method_code_bytes))
1306             << "DexFile sizes:\n";
1307       for (const std::pair<std::string, size_t>& oat_dex_file_size : oat_dex_file_sizes) {
1308         os << StringPrintf("%s = %zd (%2.0f%% of oat file bytes)\n",
1309                            oat_dex_file_size.first.c_str(), oat_dex_file_size.second,
1310                            PercentOfOatBytes(oat_dex_file_size.second));
1311       }
1312 
1313       os << "\n" << StringPrintf("gc_map_bytes           = %7zd (%2.0f%% of oat file bytes)\n"
1314                                  "pc_mapping_table_bytes = %7zd (%2.0f%% of oat file bytes)\n"
1315                                  "vmap_table_bytes       = %7zd (%2.0f%% of oat file bytes)\n\n",
1316                                  gc_map_bytes, PercentOfOatBytes(gc_map_bytes),
1317                                  pc_mapping_table_bytes, PercentOfOatBytes(pc_mapping_table_bytes),
1318                                  vmap_table_bytes, PercentOfOatBytes(vmap_table_bytes))
1319          << std::flush;
1320 
1321       os << StringPrintf("dex_instruction_bytes = %zd\n", dex_instruction_bytes)
1322          << StringPrintf("managed_code_bytes expansion = %.2f (ignoring deduplication %.2f)\n\n",
1323                          static_cast<double>(managed_code_bytes) / static_cast<double>(dex_instruction_bytes),
1324                          static_cast<double>(managed_code_bytes_ignoring_deduplication) /
1325                              static_cast<double>(dex_instruction_bytes))
1326          << std::flush;
1327 
1328       DumpOutliers(os);
1329     }
1330   } stats_;
1331 
1332  private:
1333   enum {
1334     // Number of bytes for a constructor to be considered large. Based on the 1000 basic block
1335     // threshold, we assume 2 bytes per instruction and 2 instructions per block.
1336     kLargeConstructorDexBytes = 4000,
1337     // Number of bytes for a method to be considered large. Based on the 4000 basic block
1338     // threshold, we assume 2 bytes per instruction and 2 instructions per block.
1339     kLargeMethodDexBytes = 16000
1340   };
1341   UniquePtr<OatDumper> oat_dumper_;
1342   std::ostream* os_;
1343   const std::string image_filename_;
1344   const std::string host_prefix_;
1345   gc::space::ImageSpace& image_space_;
1346   const ImageHeader& image_header_;
1347 
1348   DISALLOW_COPY_AND_ASSIGN(ImageDumper);
1349 };
1350 
oatdump(int argc,char ** argv)1351 static int oatdump(int argc, char** argv) {
1352   InitLogging(argv);
1353 
1354   // Skip over argv[0].
1355   argv++;
1356   argc--;
1357 
1358   if (argc == 0) {
1359     fprintf(stderr, "No arguments specified\n");
1360     usage();
1361   }
1362 
1363   const char* oat_filename = NULL;
1364   const char* image_filename = NULL;
1365   const char* boot_image_filename = NULL;
1366   std::string elf_filename_prefix;
1367   UniquePtr<std::string> host_prefix;
1368   std::ostream* os = &std::cout;
1369   UniquePtr<std::ofstream> out;
1370 
1371   for (int i = 0; i < argc; i++) {
1372     const StringPiece option(argv[i]);
1373     if (option.starts_with("--oat-file=")) {
1374       oat_filename = option.substr(strlen("--oat-file=")).data();
1375     } else if (option.starts_with("--image=")) {
1376       image_filename = option.substr(strlen("--image=")).data();
1377     } else if (option.starts_with("--boot-image=")) {
1378       boot_image_filename = option.substr(strlen("--boot-image=")).data();
1379     } else if (option.starts_with("--host-prefix=")) {
1380       host_prefix.reset(new std::string(option.substr(strlen("--host-prefix=")).data()));
1381     } else if (option.starts_with("--output=")) {
1382       const char* filename = option.substr(strlen("--output=")).data();
1383       out.reset(new std::ofstream(filename));
1384       if (!out->good()) {
1385         fprintf(stderr, "Failed to open output filename %s\n", filename);
1386         usage();
1387       }
1388       os = out.get();
1389     } else {
1390       fprintf(stderr, "Unknown argument %s\n", option.data());
1391       usage();
1392     }
1393   }
1394 
1395   if (image_filename == NULL && oat_filename == NULL) {
1396     fprintf(stderr, "Either --image or --oat must be specified\n");
1397     return EXIT_FAILURE;
1398   }
1399 
1400   if (image_filename != NULL && oat_filename != NULL) {
1401     fprintf(stderr, "Either --image or --oat must be specified but not both\n");
1402     return EXIT_FAILURE;
1403   }
1404 
1405   if (host_prefix.get() == NULL) {
1406     const char* android_product_out = getenv("ANDROID_PRODUCT_OUT");
1407     if (android_product_out != NULL) {
1408         host_prefix.reset(new std::string(android_product_out));
1409     } else {
1410         host_prefix.reset(new std::string(""));
1411     }
1412   }
1413 
1414   if (oat_filename != NULL) {
1415     OatFile* oat_file =
1416         OatFile::Open(oat_filename, oat_filename, NULL, false);
1417     if (oat_file == NULL) {
1418       fprintf(stderr, "Failed to open oat file from %s\n", oat_filename);
1419       return EXIT_FAILURE;
1420     }
1421     OatDumper oat_dumper(*host_prefix.get(), *oat_file);
1422     oat_dumper.Dump(*os);
1423     return EXIT_SUCCESS;
1424   }
1425 
1426   Runtime::Options options;
1427   std::string image_option;
1428   std::string oat_option;
1429   std::string boot_image_option;
1430   std::string boot_oat_option;
1431 
1432   // We are more like a compiler than a run-time. We don't want to execute code.
1433   options.push_back(std::make_pair("compiler", reinterpret_cast<void*>(NULL)));
1434 
1435   if (boot_image_filename != NULL) {
1436     boot_image_option += "-Ximage:";
1437     boot_image_option += boot_image_filename;
1438     options.push_back(std::make_pair(boot_image_option.c_str(), reinterpret_cast<void*>(NULL)));
1439   }
1440   if (image_filename != NULL) {
1441     image_option += "-Ximage:";
1442     image_option += image_filename;
1443     options.push_back(std::make_pair(image_option.c_str(), reinterpret_cast<void*>(NULL)));
1444   }
1445 
1446   if (!host_prefix->empty()) {
1447     options.push_back(std::make_pair("host-prefix", host_prefix->c_str()));
1448   }
1449 
1450   if (!Runtime::Create(options, false)) {
1451     fprintf(stderr, "Failed to create runtime\n");
1452     return EXIT_FAILURE;
1453   }
1454   UniquePtr<Runtime> runtime(Runtime::Current());
1455   // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
1456   // give it away now and then switch to a more managable ScopedObjectAccess.
1457   Thread::Current()->TransitionFromRunnableToSuspended(kNative);
1458   ScopedObjectAccess soa(Thread::Current());
1459 
1460   gc::Heap* heap = Runtime::Current()->GetHeap();
1461   gc::space::ImageSpace* image_space = heap->GetImageSpace();
1462   CHECK(image_space != NULL);
1463   const ImageHeader& image_header = image_space->GetImageHeader();
1464   if (!image_header.IsValid()) {
1465     fprintf(stderr, "Invalid image header %s\n", image_filename);
1466     return EXIT_FAILURE;
1467   }
1468   ImageDumper image_dumper(os, image_filename, *host_prefix.get(), *image_space, image_header);
1469   image_dumper.Dump();
1470   return EXIT_SUCCESS;
1471 }
1472 
1473 }  // namespace art
1474 
main(int argc,char ** argv)1475 int main(int argc, char** argv) {
1476   return art::oatdump(argc, argv);
1477 }
1478