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