blob: b124f433b3eacb95158cca7b17e17190625e7114 [file] [log] [blame]
Mathieu Chartieref2fa262018-04-12 15:14:07 -07001/*
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"
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -070018
Mathieu Chartier5d161542018-05-31 11:02:39 -070019#include <algorithm>
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -070020#include <stdint.h>
21#include <inttypes.h>
22#include <iostream>
23#include <map>
24#include <vector>
25
26#include "android-base/stringprintf.h"
Mathieu Chartierc2b4db62018-05-18 13:58:12 -070027#include "dex/class_accessor-inl.h"
Mathieu Chartier05dc23e2018-05-22 11:56:14 -070028#include "dex/class_iterator.h"
Mathieu Chartieref2fa262018-04-12 15:14:07 -070029#include "dex/code_item_accessors-inl.h"
30#include "dex/dex_instruction-inl.h"
31#include "dex/standard_dex_file.h"
Mathieu Chartierf2759792018-05-18 13:16:54 -070032#include "dex/utf-inl.h"
Mathieu Chartieref2fa262018-04-12 15:14:07 -070033
34namespace art {
Mathieu Chartier12dd8a92018-06-12 11:17:22 -070035namespace dexanalyze {
Mathieu Chartieref2fa262018-04-12 15:14:07 -070036
Mathieu Chartier12dd8a92018-06-12 11:17:22 -070037bool IsRange(Instruction::Code code) {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -070038 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
Mathieu Chartier12dd8a92018-06-12 11:17:22 -070045uint16_t NumberOfArgs(const Instruction& inst) {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -070046 return IsRange(inst.Opcode()) ? inst.VRegA_3rc() : inst.VRegA_35c();
47}
48
Mathieu Chartier12dd8a92018-06-12 11:17:22 -070049uint16_t DexMethodIndex(const Instruction& inst) {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -070050 return IsRange(inst.Opcode()) ? inst.VRegB_3rc() : inst.VRegB_35c();
51}
52
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -070053std::string Percent(uint64_t value, uint64_t max) {
54 if (max == 0) {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -070055 return "0";
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -070056 }
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -070057 return android::base::StringPrintf(
58 "%" PRId64 "(%.2f%%)",
59 value,
60 static_cast<double>(value * 100) / static_cast<double>(max));
61}
62
63std::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));
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -070072}
73
Mathieu Chartier12dd8a92018-06-12 11:17:22 -070074size_t PrefixLen(const std::string& a, const std::string& b) {
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -070075 size_t len = 0;
76 for (; len < a.length() && len < b.length() && a[len] == b[len]; ++len) {}
77 return len;
78}
79
Mathieu Chartier48de7232018-06-01 17:30:29 -070080void 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
86void AnalyzeDebugInfo::ProcessDexFiles(
87 const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
Mathieu Chartier31380c72018-05-31 18:12:27 -070088 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;
Mathieu Chartier48de7232018-06-01 17:30:29 -070092 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 }
Mathieu Chartier31380c72018-05-31 18:12:27 -0700158 }
159 }
Mathieu Chartier48de7232018-06-01 17:30:29 -0700160 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 }
Mathieu Chartier31380c72018-05-31 18:12:27 -0700169 }
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
188void 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
Mathieu Chartier5840c842018-06-15 09:07:05 -0700211void 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
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700238void 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();
Mathieu Chartier7c3a8c12018-05-23 18:09:45 -0700244 std::set<size_t> unique_code_items;
Mathieu Chartier5840c842018-06-15 09:07:05 -0700245
Mathieu Chartier05dc23e2018-05-22 11:56:14 -0700246 for (ClassAccessor accessor : dex_file.GetClasses()) {
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700247 std::set<size_t> unique_method_ids;
248 std::set<size_t> unique_string_ids;
Mathieu Chartier5d161542018-05-31 11:02:39 -0700249 // Types accessed and count.
250 std::map<size_t, size_t> types_accessed;
251
Vladimir Marko93bffcc2018-06-26 15:49:16 +0100252 // Maps from dex field index -> class field index (static or instance).
253 std::map<uint32_t, uint32_t> static_field_index_map_;
Mathieu Chartier5d161542018-05-31 11:02:39 -0700254 size_t current_idx = 0u;
Vladimir Marko93bffcc2018-06-26 15:49:16 +0100255 for (const ClassAccessor::Field& field : accessor.GetStaticFields()) {
256 static_field_index_map_[field.GetIndex()] = current_idx++;
Mathieu Chartier5d161542018-05-31 11:02:39 -0700257 }
Vladimir Marko93bffcc2018-06-26 15:49:16 +0100258 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 }
Vladimir Marko6e75bee2018-06-27 10:53:29 +0100263 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 };
Vladimir Marko93bffcc2018-06-26 15:49:16 +0100283 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) {
Vladimir Marko6e75bee2018-06-27 10:53:29 +0100295 ProcessFieldIndex(dex_field_idx, input, index_map, stats);
Vladimir Marko93bffcc2018-06-26 15:49:16 +0100296 }
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 }
Vladimir Marko6e75bee2018-06-27 10:53:29 +0100309 ProcessFieldIndex(dex_field_idx, output, index_map, stats);
Vladimir Marko93bffcc2018-06-26 15:49:16 +0100310 };
Mathieu Chartier5d161542018-05-31 11:02:39 -0700311
Mathieu Chartier0d896bd2018-05-25 00:20:27 -0700312 for (const ClassAccessor::Method& method : accessor.GetMethods()) {
Mathieu Chartier5d161542018-05-31 11:02:39 -0700313 CodeItemDataAccessor code_item(dex_file, method.GetCodeItem());
Vladimir Marko93bffcc2018-06-26 15:49:16 +0100314 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
Mathieu Chartier5d161542018-05-31 11:02:39 -0700319 dex_code_bytes_ += code_item.InsnsSizeInBytes();
Mathieu Chartier7c3a8c12018-05-23 18:09:45 -0700320 unique_code_items.insert(method.GetCodeItemOffset());
Mathieu Chartier5d161542018-05-31 11:02:39 -0700321 for (const DexInstructionPcPair& inst : code_item) {
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700322 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;
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700328 }
Mathieu Chartier5d161542018-05-31 11:02:39 -0700329 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:
Vladimir Marko93bffcc2018-06-26 15:49:16 +0100335 case Instruction::IGET_SHORT: {
336 ProcessInstanceField(
337 inst.Inst(), first_arg_reg, instance_field_index_map_, &iget_stats_);
338 break;
339 }
Mathieu Chartier5d161542018-05-31 11:02:39 -0700340 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: {
Vladimir Marko93bffcc2018-06-26 15:49:16 +0100347 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_);
Mathieu Chartier5d161542018-05-31 11:02:39 -0700369 break;
370 }
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700371 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: {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700380 uint32_t method_idx = DexMethodIndex(inst.Inst());
Mathieu Chartier5d161542018-05-31 11:02:39 -0700381 ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
Mathieu Chartierc8c8d5f2018-05-22 11:56:14 -0700382 if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700383 ++same_class_virtual_;
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700384 }
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700385 ++total_virtual_;
386 unique_method_ids.insert(method_idx);
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700387 break;
388 }
389 case Instruction::INVOKE_DIRECT:
390 case Instruction::INVOKE_DIRECT_RANGE: {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700391 uint32_t method_idx = DexMethodIndex(inst.Inst());
Mathieu Chartier5d161542018-05-31 11:02:39 -0700392 ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
Mathieu Chartierc8c8d5f2018-05-22 11:56:14 -0700393 if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700394 ++same_class_direct_;
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700395 }
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700396 ++total_direct_;
397 unique_method_ids.insert(method_idx);
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700398 break;
399 }
400 case Instruction::INVOKE_STATIC:
401 case Instruction::INVOKE_STATIC_RANGE: {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700402 uint32_t method_idx = DexMethodIndex(inst.Inst());
Mathieu Chartier5d161542018-05-31 11:02:39 -0700403 ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
Mathieu Chartierc8c8d5f2018-05-22 11:56:14 -0700404 if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700405 ++same_class_static_;
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700406 }
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700407 ++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());
Mathieu Chartier5d161542018-05-31 11:02:39 -0700414 ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700415 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());
Mathieu Chartier5d161542018-05-31 11:02:39 -0700425 ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700426 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);
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700431 break;
432 }
Mathieu Chartier5d161542018-05-31 11:02:39 -0700433 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 }
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700455 default:
456 break;
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700457 }
458 }
Mathieu Chartier0d896bd2018-05-25 00:20:27 -0700459 }
Mathieu Chartier5d161542018-05-31 11:02:39 -0700460 // 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();
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700474 total_unique_string_ids_ += unique_string_ids.size();
475 }
Mathieu Chartier7c3a8c12018-05-23 18:09:45 -0700476 total_unique_code_items_ += unique_code_items.size();
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700477}
478
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -0700479void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const {
Vladimir Marko6e75bee2018-06-27 10:53:29 +0100480 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 };
Vladimir Marko93bffcc2018-06-26 15:49:16 +0100492 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 }
Vladimir Marko6e75bee2018-06-27 10:53:29 +0100503 DumpFieldIndexes(stats);
Vladimir Marko93bffcc2018-06-26 15:49:16 +0100504 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";
Vladimir Marko6e75bee2018-06-27 10:53:29 +0100520 DumpFieldIndexes(stats);
Vladimir Marko93bffcc2018-06-26 15:49:16 +0100521 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
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700527 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";
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700532 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";
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700537 os << "Num strings accessed from code: " << num_string_ids_from_code_ << "\n";
Mathieu Chartier5d161542018-05-31 11:02:39 -0700538 os << "Avg unique methods accessed per class: "
Mathieu Chartier12dd8a92018-06-12 11:17:22 -0700539 << static_cast<double>(total_unique_method_ids_) / static_cast<double>(num_class_defs_) << "\n";
Mathieu Chartier5d161542018-05-31 11:02:39 -0700540 os << "Avg unique strings accessed per class: "
Mathieu Chartier12dd8a92018-06-12 11:17:22 -0700541 << 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";
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700550 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_;
Mathieu Chartier5840c842018-06-15 09:07:05 -0700562 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";
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700567 os << "Same class invokes: " << PercentDivide(same_class_total, other_class_total) << "\n";
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700568 os << "Invokes from code: " << (same_class_total + other_class_total) << "\n";
Mathieu Chartier5d161542018-05-31 11:02:39 -0700569 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";
Mathieu Chartier7c3a8c12018-05-23 18:09:45 -0700571 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";
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700574}
575
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700576void 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: {
Mathieu Chartier5d161542018-05-31 11:02:39 -0700595 if (space_for_out_arg && inst->VRegA_11x() < 16) {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700596 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}
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700608
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700609void 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";
Mathieu Chartier5d161542018-05-31 11:02:39 -0700616 const uint64_t low_arg_total = std::accumulate(arg_counts_, arg_counts_ + 2, 0u);
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700617 os << "Low arg savings: " << Percent(low_arg_total * 2, total_size) << "\n";
618}
619
Mathieu Chartier12dd8a92018-06-12 11:17:22 -0700620} // namespace dexanalyze
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700621} // namespace art