1 /*
2  * Copyright (C) 2018 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 "dexanalyze_experiments.h"
18 
19 #include <algorithm>
20 #include <stdint.h>
21 #include <inttypes.h>
22 #include <iostream>
23 #include <map>
24 #include <vector>
25 
26 #include "android-base/stringprintf.h"
27 #include "dex/class_accessor-inl.h"
28 #include "dex/class_iterator.h"
29 #include "dex/code_item_accessors-inl.h"
30 #include "dex/dex_instruction-inl.h"
31 #include "dex/standard_dex_file.h"
32 #include "dex/utf-inl.h"
33 
34 namespace art {
35 namespace dexanalyze {
36 
IsRange(Instruction::Code code)37 bool IsRange(Instruction::Code code) {
38   return code == Instruction::INVOKE_VIRTUAL_RANGE ||
39       code == Instruction::INVOKE_DIRECT_RANGE ||
40       code == Instruction::INVOKE_SUPER_RANGE ||
41       code == Instruction::INVOKE_STATIC_RANGE ||
42       code == Instruction::INVOKE_INTERFACE_RANGE;
43 }
44 
NumberOfArgs(const Instruction & inst)45 uint16_t NumberOfArgs(const Instruction& inst) {
46   return IsRange(inst.Opcode()) ? inst.VRegA_3rc() : inst.VRegA_35c();
47 }
48 
DexMethodIndex(const Instruction & inst)49 uint16_t DexMethodIndex(const Instruction& inst) {
50   return IsRange(inst.Opcode()) ? inst.VRegB_3rc() : inst.VRegB_35c();
51 }
52 
Percent(uint64_t value,uint64_t max)53 std::string Percent(uint64_t value, uint64_t max) {
54   if (max == 0) {
55     return "0";
56   }
57   return android::base::StringPrintf(
58       "%" PRId64 "(%.2f%%)",
59       value,
60       static_cast<double>(value * 100) / static_cast<double>(max));
61 }
62 
PercentDivide(uint64_t value,uint64_t max)63 std::string PercentDivide(uint64_t value, uint64_t max) {
64   if (max == 0) {
65     return "0";
66   }
67   return android::base::StringPrintf(
68       "%" PRId64 "/%" PRId64 "(%.2f%%)",
69       value,
70       max,
71       static_cast<double>(value * 100) / static_cast<double>(max));
72 }
73 
PrefixLen(const std::string & a,const std::string & b)74 size_t PrefixLen(const std::string& a, const std::string& b) {
75   size_t len = 0;
76   for (; len < a.length() && len < b.length() && a[len] == b[len]; ++len) {}
77   return len;
78 }
79 
ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>> & dex_files)80 void Experiment::ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
81   for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
82     ProcessDexFile(*dex_file);
83   }
84 }
85 
ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>> & dex_files)86 void AnalyzeDebugInfo::ProcessDexFiles(
87     const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
88   std::set<const uint8_t*> seen;
89   std::vector<size_t> counts(256, 0u);
90   std::vector<size_t> opcode_counts(256, 0u);
91   std::set<std::vector<uint8_t>> unique_non_header;
92   for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
93     for (ClassAccessor accessor : dex_file->GetClasses()) {
94       for (const ClassAccessor::Method& method : accessor.GetMethods()) {
95         CodeItemDebugInfoAccessor code_item(*dex_file, method.GetCodeItem(), method.GetIndex());
96         const uint8_t* debug_info = dex_file->GetDebugInfoStream(code_item.DebugInfoOffset());
97         if (debug_info != nullptr && seen.insert(debug_info).second) {
98           const uint8_t* stream = debug_info;
99           DecodeUnsignedLeb128(&stream);  // line_start
100           uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
101           for (uint32_t i = 0; i < parameters_size; ++i) {
102             DecodeUnsignedLeb128P1(&stream);  // Parameter name.
103           }
104           bool done = false;
105           const uint8_t* after_header_start = stream;
106           while (!done) {
107             const uint8_t* const op_start = stream;
108             uint8_t opcode = *stream++;
109             ++opcode_counts[opcode];
110             ++total_opcode_bytes_;
111             switch (opcode) {
112               case DexFile::DBG_END_SEQUENCE:
113                 ++total_end_seq_bytes_;
114                 done = true;
115                 break;
116               case DexFile::DBG_ADVANCE_PC:
117                 DecodeUnsignedLeb128(&stream);  // addr_diff
118                 total_advance_pc_bytes_ += stream - op_start;
119                 break;
120               case DexFile::DBG_ADVANCE_LINE:
121                 DecodeSignedLeb128(&stream);  // line_diff
122                 total_advance_line_bytes_ += stream - op_start;
123                 break;
124               case DexFile::DBG_START_LOCAL:
125                 DecodeUnsignedLeb128(&stream);  // register_num
126                 DecodeUnsignedLeb128P1(&stream);  // name_idx
127                 DecodeUnsignedLeb128P1(&stream);  // type_idx
128                 total_start_local_bytes_ += stream - op_start;
129                 break;
130               case DexFile::DBG_START_LOCAL_EXTENDED:
131                 DecodeUnsignedLeb128(&stream);  // register_num
132                 DecodeUnsignedLeb128P1(&stream);  // name_idx
133                 DecodeUnsignedLeb128P1(&stream);  // type_idx
134                 DecodeUnsignedLeb128P1(&stream);  // sig_idx
135                 total_start_local_extended_bytes_ += stream - op_start;
136                 break;
137               case DexFile::DBG_END_LOCAL:
138                 DecodeUnsignedLeb128(&stream);  // register_num
139                 total_end_local_bytes_ += stream - op_start;
140                 break;
141               case DexFile::DBG_RESTART_LOCAL:
142                 DecodeUnsignedLeb128(&stream);  // register_num
143                 total_restart_local_bytes_ += stream - op_start;
144                 break;
145               case DexFile::DBG_SET_PROLOGUE_END:
146               case DexFile::DBG_SET_EPILOGUE_BEGIN:
147                 total_epilogue_bytes_ += stream - op_start;
148                 break;
149               case DexFile::DBG_SET_FILE: {
150                 DecodeUnsignedLeb128P1(&stream);  // name_idx
151                 total_set_file_bytes_ += stream - op_start;
152                 break;
153               }
154               default: {
155                 total_other_bytes_ += stream - op_start;
156                 break;
157               }
158             }
159           }
160           const size_t bytes = stream - debug_info;
161           total_bytes_ += bytes;
162           total_non_header_bytes_ += stream - after_header_start;
163           if (unique_non_header.insert(std::vector<uint8_t>(after_header_start, stream)).second) {
164             total_unique_non_header_bytes_ += stream - after_header_start;
165           }
166           for (size_t i = 0; i < bytes; ++i) {
167             ++counts[debug_info[i]];
168           }
169         }
170       }
171     }
172   }
173   auto calc_entropy = [](std::vector<size_t> data) {
174     size_t total = std::accumulate(data.begin(), data.end(), 0u);
175     double avg_entropy = 0.0;
176     for (size_t c : data) {
177       if (c > 0) {
178         double ratio = static_cast<double>(c) / static_cast<double>(total);
179         avg_entropy -= ratio * log(ratio) / log(256.0);
180       }
181     }
182     return avg_entropy * total;
183   };
184   total_entropy_ += calc_entropy(counts);
185   total_opcode_entropy_ += calc_entropy(opcode_counts);
186 }
187 
Dump(std::ostream & os,uint64_t total_size) const188 void AnalyzeDebugInfo::Dump(std::ostream& os, uint64_t total_size) const {
189   os << "Debug info bytes " << Percent(total_bytes_, total_size) << "\n";
190 
191   os << "  DBG_END_SEQUENCE: " << Percent(total_end_seq_bytes_, total_size) << "\n";
192   os << "  DBG_ADVANCE_PC: " << Percent(total_advance_pc_bytes_, total_size) << "\n";
193   os << "  DBG_ADVANCE_LINE: " << Percent(total_advance_line_bytes_, total_size) << "\n";
194   os << "  DBG_START_LOCAL: " << Percent(total_start_local_bytes_, total_size) << "\n";
195   os << "  DBG_START_LOCAL_EXTENDED: "
196      << Percent(total_start_local_extended_bytes_, total_size) << "\n";
197   os << "  DBG_END_LOCAL: " << Percent(total_end_local_bytes_, total_size) << "\n";
198   os << "  DBG_RESTART_LOCAL: " << Percent(total_restart_local_bytes_, total_size) << "\n";
199   os << "  DBG_SET_PROLOGUE bytes " << Percent(total_epilogue_bytes_, total_size) << "\n";
200   os << "  DBG_SET_FILE bytes " << Percent(total_set_file_bytes_, total_size) << "\n";
201   os << "  special: "
202       << Percent(total_other_bytes_, total_size) << "\n";
203   os << "Debug info entropy " << Percent(total_entropy_, total_size) << "\n";
204   os << "Debug info opcode bytes " << Percent(total_opcode_bytes_, total_size) << "\n";
205   os << "Debug info opcode entropy " << Percent(total_opcode_entropy_, total_size) << "\n";
206   os << "Debug info non header bytes " << Percent(total_non_header_bytes_, total_size) << "\n";
207   os << "Debug info deduped non header bytes "
208      << Percent(total_unique_non_header_bytes_, total_size) << "\n";
209 }
210 
ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>> & dex_files)211 void CountDexIndices::ProcessDexFiles(
212     const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
213   std::set<std::string> unique_field_names;
214   std::set<std::string> unique_method_names;
215   std::set<std::string> unique_type_names;
216   std::set<std::string> unique_mf_names;
217   for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
218     for (size_t i = 0; i < dex_file->NumTypeIds(); ++i) {
219       unique_type_names.insert(
220           dex_file->StringDataByIdx(dex_file->GetTypeId(dex::TypeIndex(i)).descriptor_idx_));
221     }
222     for (size_t i = 0; i < dex_file->NumFieldIds(); ++i) {
223       unique_field_names.insert(dex_file->StringDataByIdx(dex_file->GetFieldId(i).name_idx_));
224     }
225     for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) {
226       unique_method_names.insert(dex_file->StringDataByIdx(dex_file->GetMethodId(i).name_idx_));
227     }
228     ProcessDexFile(*dex_file);
229   }
230   total_unique_method_names_ += unique_method_names.size();
231   total_unique_field_names_ += unique_field_names.size();
232   total_unique_type_names_ += unique_type_names.size();
233   unique_mf_names = unique_field_names;
234   unique_mf_names.insert(unique_method_names.begin(), unique_method_names.end());
235   total_unique_mf_names_ += unique_mf_names.size();
236 }
237 
ProcessDexFile(const DexFile & dex_file)238 void CountDexIndices::ProcessDexFile(const DexFile& dex_file) {
239   num_string_ids_ += dex_file.NumStringIds();
240   num_method_ids_ += dex_file.NumMethodIds();
241   num_field_ids_ += dex_file.NumFieldIds();
242   num_type_ids_ += dex_file.NumTypeIds();
243   num_class_defs_ += dex_file.NumClassDefs();
244   std::set<size_t> unique_code_items;
245 
246   for (ClassAccessor accessor : dex_file.GetClasses()) {
247     std::set<size_t> unique_method_ids;
248     std::set<size_t> unique_string_ids;
249     // Types accessed and count.
250     std::map<size_t, size_t> types_accessed;
251 
252     // Maps from dex field index -> class field index (static or instance).
253     std::map<uint32_t, uint32_t> static_field_index_map_;
254     size_t current_idx = 0u;
255     for (const ClassAccessor::Field& field : accessor.GetStaticFields()) {
256       static_field_index_map_[field.GetIndex()] = current_idx++;
257     }
258     std::map<uint32_t, uint32_t> instance_field_index_map_;
259     current_idx = 0u;
260     for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) {
261       instance_field_index_map_[field.GetIndex()] = current_idx++;
262     }
263     auto ProcessFieldIndex = [&](uint32_t dex_field_idx,
264                                  uint32_t inout,
265                                  const std::map<uint32_t, uint32_t>& index_map,
266                                  /*inout*/ FieldAccessStats* stats) {
267       auto it = index_map.find(dex_field_idx);
268       if (it != index_map.end()) {
269         if (it->second < FieldAccessStats::kMaxFieldIndex) {
270           ++stats->field_index_[it->second];
271         } else {
272           ++stats->field_index_other_;
273         }
274       } else {
275         ++stats->field_index_other_class_;
276       }
277       if (it != index_map.end() &&
278           it->second < FieldAccessStats::kShortBytecodeFieldIndexOutCutOff &&
279           inout < FieldAccessStats::kShortBytecodeInOutCutOff) {
280         ++stats->short_bytecode_;
281       }
282     };
283     auto ProcessInstanceField = [&](const Instruction& inst,
284                                     uint32_t first_arg_reg,
285                                     const std::map<uint32_t, uint32_t>& index_map,
286                                     /*inout*/ InstanceFieldAccessStats* stats) {
287       const uint32_t dex_field_idx = inst.VRegC_22c();
288       ++types_accessed[dex_file.GetFieldId(dex_field_idx).class_idx_.index_];
289       uint32_t input = inst.VRegA_22c();
290       ++stats->inout_[input];
291       const uint32_t receiver = inst.VRegB_22c();
292       // FIXME: This is weird if receiver < first_arg_reg.
293       ++stats->receiver_[(receiver - first_arg_reg) & 0xF];
294       if (first_arg_reg == receiver) {
295         ProcessFieldIndex(dex_field_idx, input, index_map, stats);
296       }
297     };
298     auto ProcessStaticField = [&](const Instruction& inst,
299                                   const std::map<uint32_t, uint32_t>& index_map,
300                                   /*inout*/ StaticFieldAccessStats* stats) {
301       const uint32_t dex_field_idx = inst.VRegB_21c();
302       ++types_accessed[dex_file.GetFieldId(dex_field_idx).class_idx_.index_];
303       uint8_t output = inst.VRegA_21c();
304       if (output < 16u) {
305         ++stats->inout_[output];
306       } else {
307         ++stats->inout_other_;
308       }
309       ProcessFieldIndex(dex_field_idx, output, index_map, stats);
310     };
311 
312     for (const ClassAccessor::Method& method : accessor.GetMethods()) {
313       CodeItemDataAccessor code_item(dex_file, method.GetCodeItem());
314       const uint32_t first_arg_reg =
315           ((method.GetAccessFlags() & kAccStatic) == 0)
316           ? code_item.RegistersSize() - code_item.InsSize()
317           : static_cast<uint32_t>(-1);
318 
319       dex_code_bytes_ += code_item.InsnsSizeInBytes();
320       unique_code_items.insert(method.GetCodeItemOffset());
321       for (const DexInstructionPcPair& inst : code_item) {
322         switch (inst->Opcode()) {
323           case Instruction::CONST_STRING: {
324             const dex::StringIndex string_index(inst->VRegB_21c());
325             unique_string_ids.insert(string_index.index_);
326             ++num_string_ids_from_code_;
327             break;
328           }
329           case Instruction::IGET:
330           case Instruction::IGET_WIDE:
331           case Instruction::IGET_OBJECT:
332           case Instruction::IGET_BOOLEAN:
333           case Instruction::IGET_BYTE:
334           case Instruction::IGET_CHAR:
335           case Instruction::IGET_SHORT: {
336             ProcessInstanceField(
337                 inst.Inst(), first_arg_reg, instance_field_index_map_, &iget_stats_);
338             break;
339           }
340           case Instruction::IPUT:
341           case Instruction::IPUT_WIDE:
342           case Instruction::IPUT_OBJECT:
343           case Instruction::IPUT_BOOLEAN:
344           case Instruction::IPUT_BYTE:
345           case Instruction::IPUT_CHAR:
346           case Instruction::IPUT_SHORT: {
347             ProcessInstanceField(
348                 inst.Inst(), first_arg_reg, instance_field_index_map_, &iput_stats_);
349             break;
350           }
351           case Instruction::SGET:
352           case Instruction::SGET_WIDE:
353           case Instruction::SGET_OBJECT:
354           case Instruction::SGET_BOOLEAN:
355           case Instruction::SGET_BYTE:
356           case Instruction::SGET_CHAR:
357           case Instruction::SGET_SHORT: {
358             ProcessStaticField(inst.Inst(), static_field_index_map_, &sget_stats_);
359             break;
360           }
361           case Instruction::SPUT:
362           case Instruction::SPUT_WIDE:
363           case Instruction::SPUT_OBJECT:
364           case Instruction::SPUT_BOOLEAN:
365           case Instruction::SPUT_BYTE:
366           case Instruction::SPUT_CHAR:
367           case Instruction::SPUT_SHORT: {
368             ProcessStaticField(inst.Inst(), static_field_index_map_, &sput_stats_);
369             break;
370           }
371           case Instruction::CONST_STRING_JUMBO: {
372             const dex::StringIndex string_index(inst->VRegB_31c());
373             unique_string_ids.insert(string_index.index_);
374             ++num_string_ids_from_code_;
375             break;
376           }
377           // Invoke cases.
378           case Instruction::INVOKE_VIRTUAL:
379           case Instruction::INVOKE_VIRTUAL_RANGE: {
380             uint32_t method_idx = DexMethodIndex(inst.Inst());
381             ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
382             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
383               ++same_class_virtual_;
384             }
385             ++total_virtual_;
386             unique_method_ids.insert(method_idx);
387             break;
388           }
389           case Instruction::INVOKE_DIRECT:
390           case Instruction::INVOKE_DIRECT_RANGE: {
391             uint32_t method_idx = DexMethodIndex(inst.Inst());
392             ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
393             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
394               ++same_class_direct_;
395             }
396             ++total_direct_;
397             unique_method_ids.insert(method_idx);
398             break;
399           }
400           case Instruction::INVOKE_STATIC:
401           case Instruction::INVOKE_STATIC_RANGE: {
402             uint32_t method_idx = DexMethodIndex(inst.Inst());
403             ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
404             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
405               ++same_class_static_;
406             }
407             ++total_static_;
408             unique_method_ids.insert(method_idx);
409             break;
410           }
411           case Instruction::INVOKE_INTERFACE:
412           case Instruction::INVOKE_INTERFACE_RANGE: {
413             uint32_t method_idx = DexMethodIndex(inst.Inst());
414             ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
415             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
416               ++same_class_interface_;
417             }
418             ++total_interface_;
419             unique_method_ids.insert(method_idx);
420             break;
421           }
422           case Instruction::INVOKE_SUPER:
423           case Instruction::INVOKE_SUPER_RANGE: {
424             uint32_t method_idx = DexMethodIndex(inst.Inst());
425             ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
426             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
427               ++same_class_super_;
428             }
429             ++total_super_;
430             unique_method_ids.insert(method_idx);
431             break;
432           }
433           case Instruction::NEW_ARRAY: {
434             ++types_accessed[inst->VRegC_22c()];
435             break;
436           }
437           case Instruction::FILLED_NEW_ARRAY: {
438             ++types_accessed[inst->VRegB_35c()];
439             break;
440           }
441           case Instruction::FILLED_NEW_ARRAY_RANGE: {
442             ++types_accessed[inst->VRegB_3rc()];
443             break;
444           }
445           case Instruction::CONST_CLASS:
446           case Instruction::CHECK_CAST:
447           case Instruction::NEW_INSTANCE: {
448             ++types_accessed[inst->VRegB_21c()];
449             break;
450           }
451           case Instruction::INSTANCE_OF: {
452             ++types_accessed[inst->VRegB_21c()];
453             break;
454           }
455           default:
456             break;
457         }
458       }
459     }
460     // Count uses of top 16n.
461     std::vector<size_t> uses;
462     for (auto&& p : types_accessed) {
463       uses.push_back(p.second);
464     }
465     std::sort(uses.rbegin(), uses.rend());
466     for (size_t i = 0; i < uses.size(); ++i) {
467       if (i < 16) {
468         uses_top_types_ += uses[i];
469       }
470       uses_all_types_ += uses[i];
471     }
472     total_unique_types_ += types_accessed.size();
473     total_unique_method_ids_ += unique_method_ids.size();
474     total_unique_string_ids_ += unique_string_ids.size();
475   }
476   total_unique_code_items_ += unique_code_items.size();
477 }
478 
Dump(std::ostream & os,uint64_t total_size) const479 void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const {
480   auto DumpFieldIndexes = [&](const FieldAccessStats& stats) {
481     const uint64_t fields_idx_total = std::accumulate(
482         stats.field_index_,
483         stats.field_index_ + FieldAccessStats::kMaxFieldIndex,
484         stats.field_index_other_ + stats.field_index_other_class_);
485     for (size_t i = 0; i < FieldAccessStats::kMaxFieldIndex; ++i) {
486       os << "  field_idx=" << i << ": " << Percent(stats.field_index_[i], fields_idx_total) << "\n";
487     }
488     os << "  field_idx=other: " << Percent(stats.field_index_other_, fields_idx_total) << "\n";
489     os << "  field_idx=other_class: " << Percent(stats.field_index_other_class_, fields_idx_total)
490        << "\n";
491   };
492   auto DumpInstanceFieldStats = [&](const char* tag, const InstanceFieldAccessStats& stats) {
493     const uint64_t fields_total = std::accumulate(stats.inout_, stats.inout_ + 16u, 0u);
494     os << tag << "\n";
495     for (size_t i = 0; i < 16; ++i) {
496       os << "  receiver_reg=" << i << ": " << Percent(stats.receiver_[i], fields_total) << "\n";
497     }
498     DCHECK(tag[1] == 'G' || tag[1] == 'P');
499     const char* inout_tag = (tag[1] == 'G') ? "output_reg" : "input_reg";
500     for (size_t i = 0; i < 16; ++i) {
501       os << "  " << inout_tag << "=" << i << ": " << Percent(stats.inout_[i], fields_total) << "\n";
502     }
503     DumpFieldIndexes(stats);
504     os << "  short_bytecode: " << Percent(stats.short_bytecode_, fields_total) << "\n";
505     os << "  short_bytecode_savings=" << Percent(stats.short_bytecode_ * 2, total_size) << "\n";
506   };
507   DumpInstanceFieldStats("IGET", iget_stats_);
508   DumpInstanceFieldStats("IPUT", iput_stats_);
509 
510   auto DumpStaticFieldStats = [&](const char* tag, const StaticFieldAccessStats& stats) {
511     const uint64_t fields_total =
512         std::accumulate(stats.inout_, stats.inout_ + 16u, stats.inout_other_);
513     os << tag << "\n";
514     DCHECK(tag[1] == 'G' || tag[1] == 'P');
515     const char* inout_tag = (tag[1] == 'G') ? "output_reg" : "input_reg";
516     for (size_t i = 0; i < 16; ++i) {
517       os << "  " << inout_tag << "=" << i << ": " << Percent(stats.inout_[i], fields_total) << "\n";
518     }
519     os << "  " << inout_tag << "=other: " << Percent(stats.inout_other_, fields_total) << "\n";
520     DumpFieldIndexes(stats);
521     os << "  short_bytecode: " << Percent(stats.short_bytecode_, fields_total) << "\n";
522     os << "  short_bytecode_savings=" << Percent(stats.short_bytecode_ * 2, total_size) << "\n";
523   };
524   DumpStaticFieldStats("SGET", sget_stats_);
525   DumpStaticFieldStats("SPUT", sput_stats_);
526 
527   os << "Num string ids: " << num_string_ids_ << "\n";
528   os << "Num method ids: " << num_method_ids_ << "\n";
529   os << "Num field ids: " << num_field_ids_ << "\n";
530   os << "Num type ids: " << num_type_ids_ << "\n";
531   os << "Num class defs: " << num_class_defs_ << "\n";
532   os << "Direct same class: " << PercentDivide(same_class_direct_, total_direct_) << "\n";
533   os << "Virtual same class: " << PercentDivide(same_class_virtual_, total_virtual_) << "\n";
534   os << "Static same class: " << PercentDivide(same_class_static_, total_static_) << "\n";
535   os << "Interface same class: " << PercentDivide(same_class_interface_, total_interface_) << "\n";
536   os << "Super same class: " << PercentDivide(same_class_super_, total_super_) << "\n";
537   os << "Num strings accessed from code: " << num_string_ids_from_code_ << "\n";
538   os << "Avg unique methods accessed per class: "
539      << static_cast<double>(total_unique_method_ids_) / static_cast<double>(num_class_defs_) << "\n";
540   os << "Avg unique strings accessed per class: "
541      << static_cast<double>(total_unique_string_ids_) / static_cast<double>(num_class_defs_) << "\n";
542   os << "Avg unique types accessed per class " <<
543         static_cast<double>(total_unique_types_) / static_cast<double>(num_class_defs_) << "\n";
544   os << "Total unique methods accessed per class: "
545      << Percent(total_unique_method_ids_, total_size) << "\n";
546   os << "Total unique strings accessed per class: "
547      << Percent(total_unique_string_ids_, total_size) << "\n";
548   os << "Total unique types accessed per class: "
549      << Percent(total_unique_types_, total_size) << "\n";
550   const size_t same_class_total =
551       same_class_direct_ +
552       same_class_virtual_ +
553       same_class_static_ +
554       same_class_interface_ +
555       same_class_super_;
556   const size_t other_class_total =
557       total_direct_ +
558       total_virtual_ +
559       total_static_ +
560       total_interface_ +
561       total_super_;
562   os << "Unique method names: " << Percent(total_unique_method_names_, num_field_ids_) << "\n";
563   os << "Unique field names: " << Percent(total_unique_field_names_, num_method_ids_) << "\n";
564   os << "Unique type names: " << Percent(total_unique_type_names_, num_type_ids_) << "\n";
565   os << "Unique method/field names: "
566      << Percent(total_unique_mf_names_, num_field_ids_ + num_method_ids_) << "\n";
567   os << "Same class invokes: " << PercentDivide(same_class_total, other_class_total) << "\n";
568   os << "Invokes from code: " << (same_class_total + other_class_total) << "\n";
569   os << "Type uses on top types: " << PercentDivide(uses_top_types_, uses_all_types_) << "\n";
570   os << "Type uses 1b savings: " << PercentDivide(uses_top_types_, total_size) << "\n";
571   os << "Total Dex code bytes: " << Percent(dex_code_bytes_, total_size) << "\n";
572   os << "Total unique code items: " << total_unique_code_items_ << "\n";
573   os << "Total Dex size: " << total_size << "\n";
574 }
575 
ProcessDexFile(const DexFile & dex_file)576 void CodeMetrics::ProcessDexFile(const DexFile& dex_file) {
577   for (ClassAccessor accessor : dex_file.GetClasses()) {
578     for (const ClassAccessor::Method& method : accessor.GetMethods()) {
579       bool space_for_out_arg = false;
580       for (const DexInstructionPcPair& inst : method.GetInstructions()) {
581         switch (inst->Opcode()) {
582           case Instruction::INVOKE_VIRTUAL:
583           case Instruction::INVOKE_DIRECT:
584           case Instruction::INVOKE_SUPER:
585           case Instruction::INVOKE_INTERFACE:
586           case Instruction::INVOKE_STATIC: {
587             const uint32_t args = NumberOfArgs(inst.Inst());
588             CHECK_LT(args, kMaxArgCount);
589             ++arg_counts_[args];
590             space_for_out_arg = args < kMaxArgCount - 1;
591             break;
592           }
593           case Instruction::MOVE_RESULT:
594           case Instruction::MOVE_RESULT_OBJECT: {
595             if (space_for_out_arg && inst->VRegA_11x() < 16) {
596               move_result_savings_ += inst->SizeInCodeUnits() * 2;
597             }
598             break;
599           }
600           default:
601             space_for_out_arg = false;
602             break;
603         }
604       }
605     }
606   }
607 }
608 
Dump(std::ostream & os,uint64_t total_size) const609 void CodeMetrics::Dump(std::ostream& os, uint64_t total_size) const {
610   const uint64_t total = std::accumulate(arg_counts_, arg_counts_ + kMaxArgCount, 0u);
611   for (size_t i = 0; i < kMaxArgCount; ++i) {
612     os << "args=" << i << ": " << Percent(arg_counts_[i], total) << "\n";
613   }
614   os << "Move result savings: " << Percent(move_result_savings_, total_size) << "\n";
615   os << "One byte invoke savings: " << Percent(total, total_size) << "\n";
616   const uint64_t low_arg_total = std::accumulate(arg_counts_, arg_counts_ + 2, 0u);
617   os << "Low arg savings: " << Percent(low_arg_total * 2, total_size) << "\n";
618 }
619 
620 }  // namespace dexanalyze
621 }  // namespace art
622