blob: d4e3f1f597b6191439bf239fe4a03c289d45d621 [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 {
35
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -070036static inline bool IsRange(Instruction::Code code) {
37 return code == Instruction::INVOKE_VIRTUAL_RANGE ||
38 code == Instruction::INVOKE_DIRECT_RANGE ||
39 code == Instruction::INVOKE_SUPER_RANGE ||
40 code == Instruction::INVOKE_STATIC_RANGE ||
41 code == Instruction::INVOKE_INTERFACE_RANGE;
42}
43
44static inline uint16_t NumberOfArgs(const Instruction& inst) {
45 return IsRange(inst.Opcode()) ? inst.VRegA_3rc() : inst.VRegA_35c();
46}
47
48static inline uint16_t DexMethodIndex(const Instruction& inst) {
49 return IsRange(inst.Opcode()) ? inst.VRegB_3rc() : inst.VRegB_35c();
50}
51
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -070052std::string Percent(uint64_t value, uint64_t max) {
53 if (max == 0) {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -070054 return "0";
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -070055 }
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -070056 return android::base::StringPrintf(
57 "%" PRId64 "(%.2f%%)",
58 value,
59 static_cast<double>(value * 100) / static_cast<double>(max));
60}
61
62std::string PercentDivide(uint64_t value, uint64_t max) {
63 if (max == 0) {
64 return "0";
65 }
66 return android::base::StringPrintf(
67 "%" PRId64 "/%" PRId64 "(%.2f%%)",
68 value,
69 max,
70 static_cast<double>(value * 100) / static_cast<double>(max));
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -070071}
72
73static size_t PrefixLen(const std::string& a, const std::string& b) {
74 size_t len = 0;
75 for (; len < a.length() && len < b.length() && a[len] == b[len]; ++len) {}
76 return len;
77}
78
Mathieu Chartier48de7232018-06-01 17:30:29 -070079void Experiment::ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
80 for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
81 ProcessDexFile(*dex_file);
82 }
83}
84
85void AnalyzeDebugInfo::ProcessDexFiles(
86 const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
Mathieu Chartier31380c72018-05-31 18:12:27 -070087 std::set<const uint8_t*> seen;
88 std::vector<size_t> counts(256, 0u);
89 std::vector<size_t> opcode_counts(256, 0u);
90 std::set<std::vector<uint8_t>> unique_non_header;
Mathieu Chartier48de7232018-06-01 17:30:29 -070091 for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
92 for (ClassAccessor accessor : dex_file->GetClasses()) {
93 for (const ClassAccessor::Method& method : accessor.GetMethods()) {
94 CodeItemDebugInfoAccessor code_item(*dex_file, method.GetCodeItem(), method.GetIndex());
95 const uint8_t* debug_info = dex_file->GetDebugInfoStream(code_item.DebugInfoOffset());
96 if (debug_info != nullptr && seen.insert(debug_info).second) {
97 const uint8_t* stream = debug_info;
98 DecodeUnsignedLeb128(&stream); // line_start
99 uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
100 for (uint32_t i = 0; i < parameters_size; ++i) {
101 DecodeUnsignedLeb128P1(&stream); // Parameter name.
102 }
103 bool done = false;
104 const uint8_t* after_header_start = stream;
105 while (!done) {
106 const uint8_t* const op_start = stream;
107 uint8_t opcode = *stream++;
108 ++opcode_counts[opcode];
109 ++total_opcode_bytes_;
110 switch (opcode) {
111 case DexFile::DBG_END_SEQUENCE:
112 ++total_end_seq_bytes_;
113 done = true;
114 break;
115 case DexFile::DBG_ADVANCE_PC:
116 DecodeUnsignedLeb128(&stream); // addr_diff
117 total_advance_pc_bytes_ += stream - op_start;
118 break;
119 case DexFile::DBG_ADVANCE_LINE:
120 DecodeSignedLeb128(&stream); // line_diff
121 total_advance_line_bytes_ += stream - op_start;
122 break;
123 case DexFile::DBG_START_LOCAL:
124 DecodeUnsignedLeb128(&stream); // register_num
125 DecodeUnsignedLeb128P1(&stream); // name_idx
126 DecodeUnsignedLeb128P1(&stream); // type_idx
127 total_start_local_bytes_ += stream - op_start;
128 break;
129 case DexFile::DBG_START_LOCAL_EXTENDED:
130 DecodeUnsignedLeb128(&stream); // register_num
131 DecodeUnsignedLeb128P1(&stream); // name_idx
132 DecodeUnsignedLeb128P1(&stream); // type_idx
133 DecodeUnsignedLeb128P1(&stream); // sig_idx
134 total_start_local_extended_bytes_ += stream - op_start;
135 break;
136 case DexFile::DBG_END_LOCAL:
137 DecodeUnsignedLeb128(&stream); // register_num
138 total_end_local_bytes_ += stream - op_start;
139 break;
140 case DexFile::DBG_RESTART_LOCAL:
141 DecodeUnsignedLeb128(&stream); // register_num
142 total_restart_local_bytes_ += stream - op_start;
143 break;
144 case DexFile::DBG_SET_PROLOGUE_END:
145 case DexFile::DBG_SET_EPILOGUE_BEGIN:
146 total_epilogue_bytes_ += stream - op_start;
147 break;
148 case DexFile::DBG_SET_FILE: {
149 DecodeUnsignedLeb128P1(&stream); // name_idx
150 total_set_file_bytes_ += stream - op_start;
151 break;
152 }
153 default: {
154 total_other_bytes_ += stream - op_start;
155 break;
156 }
Mathieu Chartier31380c72018-05-31 18:12:27 -0700157 }
158 }
Mathieu Chartier48de7232018-06-01 17:30:29 -0700159 const size_t bytes = stream - debug_info;
160 total_bytes_ += bytes;
161 total_non_header_bytes_ += stream - after_header_start;
162 if (unique_non_header.insert(std::vector<uint8_t>(after_header_start, stream)).second) {
163 total_unique_non_header_bytes_ += stream - after_header_start;
164 }
165 for (size_t i = 0; i < bytes; ++i) {
166 ++counts[debug_info[i]];
167 }
Mathieu Chartier31380c72018-05-31 18:12:27 -0700168 }
169 }
170 }
171 }
172 auto calc_entropy = [](std::vector<size_t> data) {
173 size_t total = std::accumulate(data.begin(), data.end(), 0u);
174 double avg_entropy = 0.0;
175 for (size_t c : data) {
176 if (c > 0) {
177 double ratio = static_cast<double>(c) / static_cast<double>(total);
178 avg_entropy -= ratio * log(ratio) / log(256.0);
179 }
180 }
181 return avg_entropy * total;
182 };
183 total_entropy_ += calc_entropy(counts);
184 total_opcode_entropy_ += calc_entropy(opcode_counts);
185}
186
187void AnalyzeDebugInfo::Dump(std::ostream& os, uint64_t total_size) const {
188 os << "Debug info bytes " << Percent(total_bytes_, total_size) << "\n";
189
190 os << " DBG_END_SEQUENCE: " << Percent(total_end_seq_bytes_, total_size) << "\n";
191 os << " DBG_ADVANCE_PC: " << Percent(total_advance_pc_bytes_, total_size) << "\n";
192 os << " DBG_ADVANCE_LINE: " << Percent(total_advance_line_bytes_, total_size) << "\n";
193 os << " DBG_START_LOCAL: " << Percent(total_start_local_bytes_, total_size) << "\n";
194 os << " DBG_START_LOCAL_EXTENDED: "
195 << Percent(total_start_local_extended_bytes_, total_size) << "\n";
196 os << " DBG_END_LOCAL: " << Percent(total_end_local_bytes_, total_size) << "\n";
197 os << " DBG_RESTART_LOCAL: " << Percent(total_restart_local_bytes_, total_size) << "\n";
198 os << " DBG_SET_PROLOGUE bytes " << Percent(total_epilogue_bytes_, total_size) << "\n";
199 os << " DBG_SET_FILE bytes " << Percent(total_set_file_bytes_, total_size) << "\n";
200 os << " special: "
201 << Percent(total_other_bytes_, total_size) << "\n";
202 os << "Debug info entropy " << Percent(total_entropy_, total_size) << "\n";
203 os << "Debug info opcode bytes " << Percent(total_opcode_bytes_, total_size) << "\n";
204 os << "Debug info opcode entropy " << Percent(total_opcode_entropy_, total_size) << "\n";
205 os << "Debug info non header bytes " << Percent(total_non_header_bytes_, total_size) << "\n";
206 os << "Debug info deduped non header bytes "
207 << Percent(total_unique_non_header_bytes_, total_size) << "\n";
208}
209
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -0700210void AnalyzeStrings::ProcessDexFile(const DexFile& dex_file) {
211 std::vector<std::string> strings;
212 for (size_t i = 0; i < dex_file.NumStringIds(); ++i) {
213 uint32_t length = 0;
Mathieu Chartierf2759792018-05-18 13:16:54 -0700214 const char* data = dex_file.StringDataAndUtf16LengthByIdx(dex::StringIndex(i), &length);
215 // Analyze if the string has any UTF16 chars.
216 bool have_wide_char = false;
217 const char* ptr = data;
218 for (size_t j = 0; j < length; ++j) {
219 have_wide_char = have_wide_char || GetUtf16FromUtf8(&ptr) >= 0x100;
220 }
221 if (have_wide_char) {
222 wide_string_bytes_ += 2 * length;
223 } else {
224 ascii_string_bytes_ += length;
225 }
226 string_data_bytes_ += ptr - data;
227
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -0700228 strings.push_back(data);
229 }
230 // Note that the strings are probably already sorted.
231 std::sort(strings.begin(), strings.end());
232
233 // Tunable parameters.
234 static const size_t kMinPrefixLen = 3;
235 static const size_t kPrefixConstantCost = 5;
236 static const size_t kPrefixIndexCost = 2;
237
238 // Calculate total shared prefix.
239 std::vector<size_t> shared_len;
240 std::set<std::string> prefixes;
241 for (size_t i = 0; i < strings.size(); ++i) {
242 size_t best_len = 0;
243 if (i > 0) {
244 best_len = std::max(best_len, PrefixLen(strings[i], strings[i - 1]));
245 }
246 if (i < strings.size() - 1) {
247 best_len = std::max(best_len, PrefixLen(strings[i], strings[i + 1]));
248 }
249 std::string prefix;
250 if (best_len >= kMinPrefixLen) {
251 prefix = strings[i].substr(0, best_len);
252 prefixes.insert(prefix);
253 total_prefix_savings_ += prefix.length();
254 }
255 total_prefix_index_cost_ += kPrefixIndexCost;
256 }
257 total_num_prefixes_ += prefixes.size();
258 for (const std::string& s : prefixes) {
259 // 4 bytes for an offset, one for length.
260 total_prefix_dict_ += s.length();
261 total_prefix_table_ += kPrefixConstantCost;
262 }
263}
264
265void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const {
Mathieu Chartierf2759792018-05-18 13:16:54 -0700266 os << "Total string data bytes " << Percent(string_data_bytes_, total_size) << "\n";
267 os << "UTF-16 string data bytes " << Percent(wide_string_bytes_, total_size) << "\n";
268 os << "ASCII string data bytes " << Percent(ascii_string_bytes_, total_size) << "\n";
269
270 // Prefix based strings.
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -0700271 os << "Total shared prefix bytes " << Percent(total_prefix_savings_, total_size) << "\n";
272 os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n";
273 os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n";
274 os << "Prefix index cost " << Percent(total_prefix_index_cost_, total_size) << "\n";
275 int64_t net_savings = total_prefix_savings_;
276 net_savings -= total_prefix_dict_;
277 net_savings -= total_prefix_table_;
278 net_savings -= total_prefix_index_cost_;
279 os << "Prefix net savings " << Percent(net_savings, total_size) << "\n";
280 os << "Prefix dictionary elements " << total_num_prefixes_ << "\n";
281}
282
Mathieu Chartier5840c842018-06-15 09:07:05 -0700283void CountDexIndices::ProcessDexFiles(
284 const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
285 std::set<std::string> unique_field_names;
286 std::set<std::string> unique_method_names;
287 std::set<std::string> unique_type_names;
288 std::set<std::string> unique_mf_names;
289 for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
290 for (size_t i = 0; i < dex_file->NumTypeIds(); ++i) {
291 unique_type_names.insert(
292 dex_file->StringDataByIdx(dex_file->GetTypeId(dex::TypeIndex(i)).descriptor_idx_));
293 }
294 for (size_t i = 0; i < dex_file->NumFieldIds(); ++i) {
295 unique_field_names.insert(dex_file->StringDataByIdx(dex_file->GetFieldId(i).name_idx_));
296 }
297 for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) {
298 unique_method_names.insert(dex_file->StringDataByIdx(dex_file->GetMethodId(i).name_idx_));
299 }
300 ProcessDexFile(*dex_file);
301 }
302 total_unique_method_names_ += unique_method_names.size();
303 total_unique_field_names_ += unique_field_names.size();
304 total_unique_type_names_ += unique_type_names.size();
305 unique_mf_names = unique_field_names;
306 unique_mf_names.insert(unique_method_names.begin(), unique_method_names.end());
307 total_unique_mf_names_ += unique_mf_names.size();
308}
309
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700310void CountDexIndices::ProcessDexFile(const DexFile& dex_file) {
311 num_string_ids_ += dex_file.NumStringIds();
312 num_method_ids_ += dex_file.NumMethodIds();
313 num_field_ids_ += dex_file.NumFieldIds();
314 num_type_ids_ += dex_file.NumTypeIds();
315 num_class_defs_ += dex_file.NumClassDefs();
Mathieu Chartier7c3a8c12018-05-23 18:09:45 -0700316 std::set<size_t> unique_code_items;
Mathieu Chartier5840c842018-06-15 09:07:05 -0700317
Mathieu Chartier05dc23e2018-05-22 11:56:14 -0700318 for (ClassAccessor accessor : dex_file.GetClasses()) {
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700319 std::set<size_t> unique_method_ids;
320 std::set<size_t> unique_string_ids;
Mathieu Chartier5d161542018-05-31 11:02:39 -0700321 // Types accessed and count.
322 std::map<size_t, size_t> types_accessed;
323
324 // Map from dex field index -> class field index.
325 std::map<uint32_t, uint32_t> field_index_map_;
326 size_t current_idx = 0u;
327 for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) {
328 field_index_map_[field.GetIndex()] = current_idx++;
329 }
330
Mathieu Chartier0d896bd2018-05-25 00:20:27 -0700331 for (const ClassAccessor::Method& method : accessor.GetMethods()) {
Mathieu Chartier5d161542018-05-31 11:02:39 -0700332 CodeItemDataAccessor code_item(dex_file, method.GetCodeItem());
333 dex_code_bytes_ += code_item.InsnsSizeInBytes();
Mathieu Chartier7c3a8c12018-05-23 18:09:45 -0700334 unique_code_items.insert(method.GetCodeItemOffset());
Mathieu Chartier5d161542018-05-31 11:02:39 -0700335 for (const DexInstructionPcPair& inst : code_item) {
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700336 switch (inst->Opcode()) {
337 case Instruction::CONST_STRING: {
338 const dex::StringIndex string_index(inst->VRegB_21c());
339 unique_string_ids.insert(string_index.index_);
340 ++num_string_ids_from_code_;
341 break;
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700342 }
Mathieu Chartier5d161542018-05-31 11:02:39 -0700343 case Instruction::IGET:
344 case Instruction::IGET_WIDE:
345 case Instruction::IGET_OBJECT:
346 case Instruction::IGET_BOOLEAN:
347 case Instruction::IGET_BYTE:
348 case Instruction::IGET_CHAR:
349 case Instruction::IGET_SHORT:
350 case Instruction::IPUT:
351 case Instruction::IPUT_WIDE:
352 case Instruction::IPUT_OBJECT:
353 case Instruction::IPUT_BOOLEAN:
354 case Instruction::IPUT_BYTE:
355 case Instruction::IPUT_CHAR:
356 case Instruction::IPUT_SHORT: {
357 const uint32_t receiver = inst->VRegB_22c();
358 const uint32_t dex_field_idx = inst->VRegC_22c();
359 const uint32_t first_arg_reg = code_item.RegistersSize() - code_item.InsSize();
360 ++field_receiver_[(receiver - first_arg_reg) & 0xF];
361 ++types_accessed[dex_file.GetFieldId(dex_field_idx).class_idx_.index_];
362 if (first_arg_reg == receiver) {
363 auto it = field_index_map_.find(dex_field_idx);
364 if (it != field_index_map_.end() && it->second < kMaxFieldIndex) {
365 ++field_index_[it->second];
366 } else {
367 ++field_index_other_;
368 }
369 }
370 ++field_output_[inst->VRegA_22c()];
371 break;
372 }
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700373 case Instruction::CONST_STRING_JUMBO: {
374 const dex::StringIndex string_index(inst->VRegB_31c());
375 unique_string_ids.insert(string_index.index_);
376 ++num_string_ids_from_code_;
377 break;
378 }
379 // Invoke cases.
380 case Instruction::INVOKE_VIRTUAL:
381 case Instruction::INVOKE_VIRTUAL_RANGE: {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700382 uint32_t method_idx = DexMethodIndex(inst.Inst());
Mathieu Chartier5d161542018-05-31 11:02:39 -0700383 ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
Mathieu Chartierc8c8d5f2018-05-22 11:56:14 -0700384 if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700385 ++same_class_virtual_;
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700386 }
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700387 ++total_virtual_;
388 unique_method_ids.insert(method_idx);
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700389 break;
390 }
391 case Instruction::INVOKE_DIRECT:
392 case Instruction::INVOKE_DIRECT_RANGE: {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700393 uint32_t method_idx = DexMethodIndex(inst.Inst());
Mathieu Chartier5d161542018-05-31 11:02:39 -0700394 ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
Mathieu Chartierc8c8d5f2018-05-22 11:56:14 -0700395 if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700396 ++same_class_direct_;
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700397 }
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700398 ++total_direct_;
399 unique_method_ids.insert(method_idx);
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700400 break;
401 }
402 case Instruction::INVOKE_STATIC:
403 case Instruction::INVOKE_STATIC_RANGE: {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700404 uint32_t method_idx = DexMethodIndex(inst.Inst());
Mathieu Chartier5d161542018-05-31 11:02:39 -0700405 ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
Mathieu Chartierc8c8d5f2018-05-22 11:56:14 -0700406 if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700407 ++same_class_static_;
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700408 }
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700409 ++total_static_;
410 unique_method_ids.insert(method_idx);
411 break;
412 }
413 case Instruction::INVOKE_INTERFACE:
414 case Instruction::INVOKE_INTERFACE_RANGE: {
415 uint32_t method_idx = DexMethodIndex(inst.Inst());
Mathieu Chartier5d161542018-05-31 11:02:39 -0700416 ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700417 if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
418 ++same_class_interface_;
419 }
420 ++total_interface_;
421 unique_method_ids.insert(method_idx);
422 break;
423 }
424 case Instruction::INVOKE_SUPER:
425 case Instruction::INVOKE_SUPER_RANGE: {
426 uint32_t method_idx = DexMethodIndex(inst.Inst());
Mathieu Chartier5d161542018-05-31 11:02:39 -0700427 ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700428 if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
429 ++same_class_super_;
430 }
431 ++total_super_;
432 unique_method_ids.insert(method_idx);
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700433 break;
434 }
Mathieu Chartier5d161542018-05-31 11:02:39 -0700435 case Instruction::NEW_ARRAY: {
436 ++types_accessed[inst->VRegC_22c()];
437 break;
438 }
439 case Instruction::FILLED_NEW_ARRAY: {
440 ++types_accessed[inst->VRegB_35c()];
441 break;
442 }
443 case Instruction::FILLED_NEW_ARRAY_RANGE: {
444 ++types_accessed[inst->VRegB_3rc()];
445 break;
446 }
447 case Instruction::CONST_CLASS:
448 case Instruction::CHECK_CAST:
449 case Instruction::NEW_INSTANCE: {
450 ++types_accessed[inst->VRegB_21c()];
451 break;
452 }
453 case Instruction::INSTANCE_OF: {
454 ++types_accessed[inst->VRegB_21c()];
455 break;
456 }
Mathieu Chartierc2b4db62018-05-18 13:58:12 -0700457 default:
458 break;
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700459 }
460 }
Mathieu Chartier0d896bd2018-05-25 00:20:27 -0700461 }
Mathieu Chartier5d161542018-05-31 11:02:39 -0700462 // Count uses of top 16n.
463 std::vector<size_t> uses;
464 for (auto&& p : types_accessed) {
465 uses.push_back(p.second);
466 }
467 std::sort(uses.rbegin(), uses.rend());
468 for (size_t i = 0; i < uses.size(); ++i) {
469 if (i < 16) {
470 uses_top_types_ += uses[i];
471 }
472 uses_all_types_ += uses[i];
473 }
474 total_unique_types_ += types_accessed.size();
475 total_unique_method_ids_ += unique_method_ids.size();
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700476 total_unique_string_ids_ += unique_string_ids.size();
477 }
Mathieu Chartier7c3a8c12018-05-23 18:09:45 -0700478 total_unique_code_items_ += unique_code_items.size();
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700479}
480
Mathieu Chartier35ddc6f2018-05-09 11:34:07 -0700481void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const {
Mathieu Chartier5d161542018-05-31 11:02:39 -0700482 const uint64_t fields_total = std::accumulate(field_receiver_, field_receiver_ + 16u, 0u);
483 for (size_t i = 0; i < 16; ++i) {
484 os << "receiver_reg=" << i << ": " << Percent(field_receiver_[i], fields_total) << "\n";
485 }
486 for (size_t i = 0; i < 16; ++i) {
487 os << "output_reg=" << i << ": " << Percent(field_output_[i], fields_total) << "\n";
488 }
489 const uint64_t fields_idx_total = std::accumulate(field_index_,
490 field_index_ + kMaxFieldIndex,
491 0u) + field_index_other_;
492 for (size_t i = 0; i < kMaxFieldIndex; ++i) {
493 os << "field_idx=" << i << ": " << Percent(field_index_[i], fields_idx_total) << "\n";
494 }
495 os << "field_idx=other: " << Percent(field_index_other_, fields_idx_total) << "\n";
496 os << "field_idx_savings=" << Percent((fields_idx_total - field_index_other_) * 2, total_size)
497 << "\n";
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700498 os << "Num string ids: " << num_string_ids_ << "\n";
499 os << "Num method ids: " << num_method_ids_ << "\n";
500 os << "Num field ids: " << num_field_ids_ << "\n";
501 os << "Num type ids: " << num_type_ids_ << "\n";
502 os << "Num class defs: " << num_class_defs_ << "\n";
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700503 os << "Direct same class: " << PercentDivide(same_class_direct_, total_direct_) << "\n";
504 os << "Virtual same class: " << PercentDivide(same_class_virtual_, total_virtual_) << "\n";
505 os << "Static same class: " << PercentDivide(same_class_static_, total_static_) << "\n";
506 os << "Interface same class: " << PercentDivide(same_class_interface_, total_interface_) << "\n";
507 os << "Super same class: " << PercentDivide(same_class_super_, total_super_) << "\n";
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700508 os << "Num strings accessed from code: " << num_string_ids_from_code_ << "\n";
Mathieu Chartier5d161542018-05-31 11:02:39 -0700509 os << "Avg unique methods accessed per class: "
510 << double(total_unique_method_ids_) / double(num_class_defs_) << "\n";
511 os << "Avg unique strings accessed per class: "
512 << double(total_unique_string_ids_) / double(num_class_defs_) << "\n";
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700513 const size_t same_class_total =
514 same_class_direct_ +
515 same_class_virtual_ +
516 same_class_static_ +
517 same_class_interface_ +
518 same_class_super_;
519 const size_t other_class_total =
520 total_direct_ +
521 total_virtual_ +
522 total_static_ +
523 total_interface_ +
524 total_super_;
Mathieu Chartier5840c842018-06-15 09:07:05 -0700525 os << "Unique method names: " << Percent(total_unique_method_names_, num_field_ids_) << "\n";
526 os << "Unique field names: " << Percent(total_unique_field_names_, num_method_ids_) << "\n";
527 os << "Unique type names: " << Percent(total_unique_type_names_, num_type_ids_) << "\n";
528 os << "Unique method/field names: "
529 << Percent(total_unique_mf_names_, num_field_ids_ + num_method_ids_) << "\n";
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700530 os << "Same class invokes: " << PercentDivide(same_class_total, other_class_total) << "\n";
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700531 os << "Invokes from code: " << (same_class_total + other_class_total) << "\n";
Mathieu Chartier5d161542018-05-31 11:02:39 -0700532 os << "Type uses on top types: " << PercentDivide(uses_top_types_, uses_all_types_) << "\n";
533 os << "Type uses 1b savings: " << PercentDivide(uses_top_types_, total_size) << "\n";
534 os << "Total unique types accessed per class " << total_unique_types_ << "\n";
Mathieu Chartier7c3a8c12018-05-23 18:09:45 -0700535 os << "Total Dex code bytes: " << Percent(dex_code_bytes_, total_size) << "\n";
536 os << "Total unique code items: " << total_unique_code_items_ << "\n";
537 os << "Total Dex size: " << total_size << "\n";
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700538}
539
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700540void CodeMetrics::ProcessDexFile(const DexFile& dex_file) {
541 for (ClassAccessor accessor : dex_file.GetClasses()) {
542 for (const ClassAccessor::Method& method : accessor.GetMethods()) {
543 bool space_for_out_arg = false;
544 for (const DexInstructionPcPair& inst : method.GetInstructions()) {
545 switch (inst->Opcode()) {
546 case Instruction::INVOKE_VIRTUAL:
547 case Instruction::INVOKE_DIRECT:
548 case Instruction::INVOKE_SUPER:
549 case Instruction::INVOKE_INTERFACE:
550 case Instruction::INVOKE_STATIC: {
551 const uint32_t args = NumberOfArgs(inst.Inst());
552 CHECK_LT(args, kMaxArgCount);
553 ++arg_counts_[args];
554 space_for_out_arg = args < kMaxArgCount - 1;
555 break;
556 }
557 case Instruction::MOVE_RESULT:
558 case Instruction::MOVE_RESULT_OBJECT: {
Mathieu Chartier5d161542018-05-31 11:02:39 -0700559 if (space_for_out_arg && inst->VRegA_11x() < 16) {
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700560 move_result_savings_ += inst->SizeInCodeUnits() * 2;
561 }
562 break;
563 }
564 default:
565 space_for_out_arg = false;
566 break;
567 }
568 }
569 }
570 }
571}
Mathieu Chartieref2fa262018-04-12 15:14:07 -0700572
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700573void CodeMetrics::Dump(std::ostream& os, uint64_t total_size) const {
574 const uint64_t total = std::accumulate(arg_counts_, arg_counts_ + kMaxArgCount, 0u);
575 for (size_t i = 0; i < kMaxArgCount; ++i) {
576 os << "args=" << i << ": " << Percent(arg_counts_[i], total) << "\n";
577 }
578 os << "Move result savings: " << Percent(move_result_savings_, total_size) << "\n";
579 os << "One byte invoke savings: " << Percent(total, total_size) << "\n";
Mathieu Chartier5d161542018-05-31 11:02:39 -0700580 const uint64_t low_arg_total = std::accumulate(arg_counts_, arg_counts_ + 2, 0u);
Mathieu Chartierb7ae0b12018-05-29 09:25:57 -0700581 os << "Low arg savings: " << Percent(low_arg_total * 2, total_size) << "\n";
582}
583
584} // namespace art