blob: de5c34ecf47c91d9534a72223051155aafa8a1f3 [file] [log] [blame]
Mathieu Chartier0ddf6262018-08-23 17:37:46 -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_strings.h"
18
19#include <algorithm>
20#include <iomanip>
21#include <iostream>
Mathieu Chartier49ca8692018-08-24 09:34:49 -070022#include <queue>
Mathieu Chartier0ddf6262018-08-23 17:37:46 -070023
Mathieu Chartier5eae4c52018-08-30 15:52:15 -070024#include "base/time_utils.h"
Mathieu Chartier0ddf6262018-08-23 17:37:46 -070025#include "dex/class_accessor-inl.h"
26#include "dex/code_item_accessors-inl.h"
27#include "dex/dex_instruction-inl.h"
28
29namespace art {
30namespace dexanalyze {
31
Mathieu Chartier49ca8692018-08-24 09:34:49 -070032// Tunable parameters.
33static const size_t kMinPrefixLen = 1;
34static const size_t kMaxPrefixLen = 255;
35static const size_t kPrefixConstantCost = 4;
36static const size_t kPrefixIndexCost = 2;
37
Mathieu Chartier5eae4c52018-08-30 15:52:15 -070038class PrefixDictionary {
39 public:
40 // Add prefix data and return the offset to the start of the added data.
41 size_t AddPrefixData(const uint8_t* data, size_t len) {
42 const size_t offset = prefix_data_.size();
43 prefix_data_.insert(prefix_data_.end(), data, data + len);
44 return offset;
45 }
46
47 static constexpr size_t kLengthBits = 8;
48 static constexpr size_t kLengthMask = (1u << kLengthBits) - 1;
49
50 // Return the prefix offset and length.
51 ALWAYS_INLINE void GetOffset(uint32_t prefix_index, uint32_t* offset, uint32_t* length) const {
52 CHECK_LT(prefix_index, offsets_.size());
53 const uint32_t data = offsets_[prefix_index];
54 *length = data & kLengthMask;
55 *offset = data >> kLengthBits;
56 }
57
58 uint32_t AddOffset(uint32_t offset, uint32_t length) {
59 CHECK_LE(length, kLengthMask);
60 offsets_.push_back((offset << kLengthBits) | length);
61 return offsets_.size() - 1;
62 }
63
64 public:
65 std::vector<uint32_t> offsets_;
66 std::vector<uint8_t> prefix_data_;
67};
68
69class PrefixStrings {
70 public:
71 class Builder {
72 public:
73 explicit Builder(PrefixStrings* output) : output_(output) {}
74 void Build(const std::vector<std::string>& strings);
75
76 private:
77 PrefixStrings* const output_;
78 };
79
80 // Return the string index that was added.
81 size_t AddString(uint16_t prefix, const std::string& str) {
82 const size_t string_offset = chars_.size();
83 chars_.push_back(static_cast<uint8_t>(prefix >> 8));
84 chars_.push_back(static_cast<uint8_t>(prefix >> 0));
85 EncodeUnsignedLeb128(&chars_, str.length());
86 const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&str[0]);
87 chars_.insert(chars_.end(), ptr, ptr + str.length());
88 string_offsets_.push_back(string_offset);
89 return string_offsets_.size() - 1;
90 }
91
92 std::string GetString(uint32_t string_idx) const {
93 const size_t offset = string_offsets_[string_idx];
94 const uint8_t* suffix_data = &chars_[offset];
95 uint16_t prefix_idx = (static_cast<uint16_t>(suffix_data[0]) << 8) +
96 suffix_data[1];
97 suffix_data += 2;
98 uint32_t prefix_offset;
99 uint32_t prefix_len;
100 dictionary_.GetOffset(prefix_idx, &prefix_offset, &prefix_len);
101 const uint8_t* prefix_data = &dictionary_.prefix_data_[prefix_offset];
102 std::string ret(prefix_data, prefix_data + prefix_len);
103 uint32_t suffix_len = DecodeUnsignedLeb128(&suffix_data);
104 ret.insert(ret.end(), suffix_data, suffix_data + suffix_len);
105 return ret;
106 }
107
108 ALWAYS_INLINE bool Equal(uint32_t string_idx, const uint8_t* data, size_t len) const {
109 const size_t offset = string_offsets_[string_idx];
110 const uint8_t* suffix_data = &chars_[offset];
111 uint16_t prefix_idx = (static_cast<uint16_t>(suffix_data[0]) << 8) +
112 suffix_data[1];
113 suffix_data += 2;
114 uint32_t prefix_offset;
115 uint32_t prefix_len;
116 dictionary_.GetOffset(prefix_idx, &prefix_offset, &prefix_len);
117 uint32_t suffix_len = DecodeUnsignedLeb128(&suffix_data);
118 if (prefix_len + suffix_len != len) {
119 return false;
120 }
121 const uint8_t* prefix_data = &dictionary_.prefix_data_[prefix_offset];
122 return memcmp(prefix_data, data, prefix_len) == 0u &&
123 memcmp(suffix_data, data + prefix_len, len - prefix_len) == 0u;
124 }
125
126 public:
127 PrefixDictionary dictionary_;
128 std::vector<uint8_t> chars_;
129 std::vector<uint32_t> string_offsets_;
130};
131
132// Normal non prefix strings.
133class NormalStrings {
134 public:
135 // Return the string index that was added.
136 size_t AddString(const std::string& str) {
137 const size_t string_offset = chars_.size();
138 EncodeUnsignedLeb128(&chars_, str.length());
139 const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&str[0]);
140 chars_.insert(chars_.end(), ptr, ptr + str.length());
141 string_offsets_.push_back(string_offset);
142 return string_offsets_.size() - 1;
143 }
144
145 std::string GetString(uint32_t string_idx) const {
146 const size_t offset = string_offsets_[string_idx];
147 const uint8_t* data = &chars_[offset];
148 uint32_t len = DecodeUnsignedLeb128(&data);
149 return std::string(data, data + len);
150 }
151
152 ALWAYS_INLINE bool Equal(uint32_t string_idx, const uint8_t* data, size_t len) const {
153 const size_t offset = string_offsets_[string_idx];
154 const uint8_t* str_data = &chars_[offset];
155 uint32_t str_len = DecodeUnsignedLeb128(&str_data);
156 if (str_len != len) {
157 return false;
158 }
159 return memcmp(data, str_data, len) == 0u;
160 }
161
162 public:
163 std::vector<uint8_t> chars_;
164 std::vector<uint32_t> string_offsets_;
165};
166
167
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700168// Node value = (distance from root) * (occurrences - 1).
169class MatchTrie {
170 public:
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700171 MatchTrie* Add(const std::string& str) {
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700172 MatchTrie* node = this;
173 size_t depth = 0u;
174 for (uint8_t c : str) {
175 ++depth;
176 if (node->nodes_[c] == nullptr) {
177 MatchTrie* new_node = new MatchTrie();
178 node->nodes_[c].reset(new_node);
179 new_node->parent_ = node;
180 new_node->depth_ = depth;
181 new_node->incoming_ = c;
182 node = new_node;
183 } else {
184 node = node->nodes_[c].get();
185 }
186 ++node->count_;
187 }
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700188 return node;
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700189 }
190
191 // Returns the length of the longest prefix and if it's a leaf node.
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700192 MatchTrie* LongestPrefix(const std::string& str) {
193 MatchTrie* node = this;
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700194 for (uint8_t c : str) {
195 if (node->nodes_[c] == nullptr) {
196 break;
197 }
198 node = node->nodes_[c].get();
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700199 }
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700200 return node;
201 }
202
203 bool IsLeaf() const {
204 for (const std::unique_ptr<MatchTrie>& cur_node : nodes_) {
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700205 if (cur_node != nullptr) {
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700206 return false;
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700207 }
208 }
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700209 return true;
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700210 }
211
212 int32_t Savings() const {
213 int32_t cost = kPrefixConstantCost;
214 int32_t first_used = 0u;
215 if (chosen_suffix_count_ == 0u) {
216 cost += depth_;
217 }
218 uint32_t extra_savings = 0u;
219 for (MatchTrie* cur = parent_; cur != nullptr; cur = cur->parent_) {
220 if (cur->chosen_) {
221 first_used = cur->depth_;
222 if (cur->chosen_suffix_count_ == 0u) {
223 // First suffix for the chosen parent, remove the cost of the dictionary entry.
224 extra_savings += first_used;
225 }
226 break;
227 }
228 }
229 return count_ * (depth_ - first_used) - cost + extra_savings;
230 }
231
232 template <typename T, typename... Args, template <typename...> class Queue>
233 T PopRealTop(Queue<T, Args...>& queue) {
234 auto pair = queue.top();
235 queue.pop();
236 // Keep updating values until one sticks.
237 while (pair.second->Savings() != pair.first) {
238 pair.first = pair.second->Savings();
239 queue.push(pair);
240 pair = queue.top();
241 queue.pop();
242 }
243 return pair;
244 }
245
246 std::vector<std::string> ExtractPrefixes(size_t max) {
247 std::vector<std::string> ret;
248 // Make priority queue and adaptively update it. Each node value is the savings from picking
249 // it. Insert all of the interesting nodes in the queue (children != 1).
250 std::priority_queue<std::pair<int32_t, MatchTrie*>> queue;
251 // Add all of the nodes to the queue.
252 std::vector<MatchTrie*> work(1, this);
253 while (!work.empty()) {
254 MatchTrie* elem = work.back();
255 work.pop_back();
256 size_t num_childs = 0u;
257 for (const std::unique_ptr<MatchTrie>& child : elem->nodes_) {
258 if (child != nullptr) {
259 work.push_back(child.get());
260 ++num_childs;
261 }
262 }
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700263 if (num_childs > 1u || elem->value_ != 0u) {
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700264 queue.emplace(elem->Savings(), elem);
265 }
266 }
267 std::priority_queue<std::pair<int32_t, MatchTrie*>> prefixes;
268 // The savings can only ever go down for a given node, never up.
269 while (max != 0u && !queue.empty()) {
270 std::pair<int32_t, MatchTrie*> pair = PopRealTop(queue);
271 if (pair.second != this && pair.first > 0) {
272 // Pick this node.
273 uint32_t count = pair.second->count_;
274 pair.second->chosen_ = true;
275 for (MatchTrie* cur = pair.second->parent_; cur != this; cur = cur->parent_) {
276 if (cur->chosen_) {
277 break;
278 }
279 cur->count_ -= count;
280 }
281 for (MatchTrie* cur = pair.second->parent_; cur != this; cur = cur->parent_) {
282 ++cur->chosen_suffix_count_;
283 }
284 prefixes.emplace(pair.first, pair.second);
285 --max;
286 } else {
287 // Negative or no EV, just delete the node.
288 }
289 }
290 while (!prefixes.empty()) {
291 std::pair<int32_t, MatchTrie*> pair = PopRealTop(prefixes);
292 if (pair.first <= 0) {
293 continue;
294 }
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700295 ret.push_back(pair.second->GetString());
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700296 }
297 return ret;
298 }
299
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700300 std::string GetString() const {
301 std::vector<uint8_t> chars;
302 for (const MatchTrie* cur = this; cur->parent_ != nullptr; cur = cur->parent_) {
303 chars.push_back(cur->incoming_);
304 }
305 return std::string(chars.rbegin(), chars.rend());
306 }
307
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700308 std::unique_ptr<MatchTrie> nodes_[256];
309 MatchTrie* parent_ = nullptr;
310 uint32_t count_ = 0u;
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700311 uint32_t depth_ = 0u;
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700312 int32_t savings_ = 0u;
313 uint8_t incoming_ = 0u;
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700314 // Value of the current node, non zero if the node is chosen.
315 uint32_t value_ = 0u;
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700316 // If the current node is chosen to be a used prefix.
317 bool chosen_ = false;
318 // If the current node is a prefix of a longer chosen prefix.
319 uint32_t chosen_suffix_count_ = 0u;
320};
321
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700322void PrefixStrings::Builder::Build(const std::vector<std::string>& strings) {
323 std::unique_ptr<MatchTrie> prefixe_trie(new MatchTrie());
324 for (size_t i = 0; i < strings.size(); ++i) {
325 size_t len = 0u;
326 if (i > 0u) {
327 CHECK_GT(strings[i], strings[i - 1]);
328 len = std::max(len, PrefixLen(strings[i], strings[i - 1]));
329 }
330 if (i < strings.size() - 1) {
331 len = std::max(len, PrefixLen(strings[i], strings[i + 1]));
332 }
333 len = std::min(len, kMaxPrefixLen);
334 if (len >= kMinPrefixLen) {
335 prefixe_trie->Add(strings[i].substr(0, len))->value_ = 1u;
336 }
337 }
338
339 // Build prefixes.
340 {
341 static constexpr size_t kPrefixBits = 15;
342 std::vector<std::string> prefixes(prefixe_trie->ExtractPrefixes(1 << kPrefixBits));
343 // Add longest prefixes first so that subprefixes can share data.
344 std::sort(prefixes.begin(), prefixes.end(), [](const std::string& a, const std::string& b) {
345 return a.length() > b.length();
346 });
347 prefixe_trie.reset();
348 prefixe_trie.reset(new MatchTrie());
349 uint32_t prefix_idx = 0u;
350 CHECK_EQ(output_->dictionary_.AddOffset(0u, 0u), prefix_idx++);
351 for (const std::string& str : prefixes) {
352 uint32_t prefix_offset = 0u;
353 MatchTrie* node = prefixe_trie->LongestPrefix(str);
354 if (node != nullptr && node->depth_ == str.length() && node->value_ != 0u) {
355 CHECK_EQ(node->GetString(), str);
356 uint32_t existing_len = 0u;
357 output_->dictionary_.GetOffset(node->value_, &prefix_offset, &existing_len);
358 // Make sure to register the current node.
359 prefixe_trie->Add(str)->value_ = prefix_idx;
360 } else {
361 auto add_str = [&](const std::string& s) {
362 node = prefixe_trie->Add(s);
363 node->value_ = prefix_idx;
364 while (node != nullptr) {
365 node->value_ = prefix_idx;
366 node = node->parent_;
367 }
368 };
369 static constexpr size_t kNumSubstrings = 1u;
370 // Increasing kNumSubstrings provides savings since it enables common substrings and not
371 // only prefixes to share data. The problem is that it's slow.
372 for (size_t i = 0; i < std::min(str.length(), kNumSubstrings); ++i) {
373 add_str(str.substr(i));
374 }
375 prefix_offset = output_->dictionary_.AddPrefixData(
376 reinterpret_cast<const uint8_t*>(&str[0]),
377 str.length());
378 }
379 // TODO: Validiate the prefix offset.
380 CHECK_EQ(output_->dictionary_.AddOffset(prefix_offset, str.length()), prefix_idx);
381 ++prefix_idx;
382 }
383 }
384
385 // Add strings to the dictionary.
386 for (const std::string& str : strings) {
387 MatchTrie* node = prefixe_trie->LongestPrefix(str);
388 uint32_t prefix_idx = 0u;
389 uint32_t best_length = 0u;
390 while (node != nullptr) {
391 uint32_t offset = 0u;
392 uint32_t length = 0u;
393 output_->dictionary_.GetOffset(node->value_, &offset, &length);
394 if (node->depth_ == length) {
395 prefix_idx = node->value_;
396 best_length = node->depth_;
397 break;
398 // Actually the prefix we want.
399 }
400 node = node->parent_;
401 }
402 output_->AddString(prefix_idx, str.substr(best_length));
403 }
404}
405
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700406void AnalyzeStrings::ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
407 std::set<std::string> unique_strings;
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700408 // Accumulate the strings.
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700409 for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
410 for (size_t i = 0; i < dex_file->NumStringIds(); ++i) {
411 uint32_t length = 0;
412 const char* data = dex_file->StringDataAndUtf16LengthByIdx(dex::StringIndex(i), &length);
413 // Analyze if the string has any UTF16 chars.
414 bool have_wide_char = false;
415 const char* ptr = data;
416 for (size_t j = 0; j < length; ++j) {
417 have_wide_char = have_wide_char || GetUtf16FromUtf8(&ptr) >= 0x100;
418 }
419 if (have_wide_char) {
420 wide_string_bytes_ += 2 * length;
421 } else {
422 ascii_string_bytes_ += length;
423 }
424 string_data_bytes_ += ptr - data;
425 unique_strings.insert(data);
426 }
427 }
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700428 // Unique strings only since we want to exclude savings from multi-dex duplication.
429 ProcessStrings(std::vector<std::string>(unique_strings.begin(), unique_strings.end()));
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700430}
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700431
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700432void AnalyzeStrings::ProcessStrings(const std::vector<std::string>& strings) {
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700433 // Calculate total shared prefix.
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700434 size_t prefix_index_cost_ = 0u;
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700435 for (size_t i = 0; i < strings.size(); ++i) {
436 size_t best_len = 0;
437 if (i > 0) {
438 best_len = std::max(best_len, PrefixLen(strings[i], strings[i - 1]));
439 }
440 if (i < strings.size() - 1) {
441 best_len = std::max(best_len, PrefixLen(strings[i], strings[i + 1]));
442 }
443 best_len = std::min(best_len, kMaxPrefixLen);
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700444 if (best_len >= kMinPrefixLen) {
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700445 total_shared_prefix_bytes_ += best_len;
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700446 }
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700447 prefix_index_cost_ += kPrefixIndexCost;
448 if (strings[i].length() < 64) {
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700449 ++short_strings_;
450 } else {
451 ++long_strings_;
452 }
453 }
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700454 total_prefix_index_cost_ += prefix_index_cost_;
455
456 PrefixStrings prefix_strings;
457 {
458 PrefixStrings::Builder prefix_builder(&prefix_strings);
459 prefix_builder.Build(strings);
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700460 }
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700461 Benchmark(prefix_strings, strings, &prefix_timings_);
462 const size_t num_prefixes = prefix_strings.dictionary_.offsets_.size();
463 total_num_prefixes_ += num_prefixes;
464 total_prefix_table_ += num_prefixes * sizeof(prefix_strings.dictionary_.offsets_[0]);
465 total_prefix_dict_ += prefix_strings.dictionary_.prefix_data_.size();
466
467 {
468 NormalStrings normal_strings;
469 for (const std::string& s : strings) {
470 normal_strings.AddString(s);
471 }
472 const uint64_t unique_string_data_bytes = normal_strings.chars_.size();
473 total_unique_string_data_bytes_ += unique_string_data_bytes;
474 total_prefix_savings_ += unique_string_data_bytes - prefix_strings.chars_.size() +
475 prefix_index_cost_;
476 Benchmark(normal_strings, strings, &normal_timings_);
477 }
478}
479
480template <typename Strings>
481void AnalyzeStrings::Benchmark(const Strings& strings,
482 const std::vector<std::string>& reference,
483 StringTimings* timings) {
484 const size_t kIterations = 100;
485 timings->num_comparisons_ += reference.size() * kIterations;
486
487 uint64_t start = NanoTime();
488 for (size_t j = 0; j < kIterations; ++j) {
489 for (size_t i = 0; i < reference.size(); ++i) {
490 CHECK(strings.Equal(
491 i,
492 reinterpret_cast<const uint8_t*>(&reference[i][0]),
493 reference[i].length()))
494 << i << ": " << strings.GetString(i) << " vs " << reference[i];
495 }
496 }
497 timings->time_equal_comparisons_ += NanoTime() - start;
498
499 start = NanoTime();
500 for (size_t j = 0; j < kIterations; ++j) {
501 size_t count = 0u;
502 for (size_t i = 0; i < reference.size(); ++i) {
503 count += strings.Equal(
504 reference.size() - 1 - i,
505 reinterpret_cast<const uint8_t*>(&reference[i][0]),
506 reference[i].length());
507 }
508 CHECK_LT(count, 2u);
509 }
510 timings->time_non_equal_comparisons_ += NanoTime() - start;
511}
512
513template void AnalyzeStrings::Benchmark(const PrefixStrings&,
514 const std::vector<std::string>&,
515 StringTimings* timings);
516template void AnalyzeStrings::Benchmark(const NormalStrings&,
517 const std::vector<std::string>&,
518 StringTimings* timings);
519
520void StringTimings::Dump(std::ostream& os) const {
521 const double comparisons = static_cast<double>(num_comparisons_);
522 os << "Compare equal " << static_cast<double>(time_equal_comparisons_) / comparisons << "\n";
523 os << "Compare not equal " << static_cast<double>(time_non_equal_comparisons_) / comparisons << "\n";
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700524}
525
526void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const {
527 os << "Total string data bytes " << Percent(string_data_bytes_, total_size) << "\n";
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700528 os << "Total unique string data bytes "
529 << Percent(total_unique_string_data_bytes_, total_size) << "\n";
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700530 os << "UTF-16 string data bytes " << Percent(wide_string_bytes_, total_size) << "\n";
531 os << "ASCII string data bytes " << Percent(ascii_string_bytes_, total_size) << "\n";
532
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700533 os << "Prefix string timings\n";
534 prefix_timings_.Dump(os);
535 os << "Normal string timings\n";
536 normal_timings_.Dump(os);
537
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700538 // Prefix based strings.
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700539 os << "Total shared prefix bytes " << Percent(total_shared_prefix_bytes_, total_size) << "\n";
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700540 os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n";
541 os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n";
542 os << "Prefix index cost " << Percent(total_prefix_index_cost_, total_size) << "\n";
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700543 int64_t net_savings = total_prefix_savings_;
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700544 net_savings -= total_prefix_dict_;
545 net_savings -= total_prefix_table_;
546 net_savings -= total_prefix_index_cost_;
547 os << "Prefix dictionary elements " << total_num_prefixes_ << "\n";
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700548 os << "Prefix base savings " << Percent(total_prefix_savings_, total_size) << "\n";
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700549 os << "Prefix net savings " << Percent(net_savings, total_size) << "\n";
Mathieu Chartier49ca8692018-08-24 09:34:49 -0700550 os << "Strings using prefix "
551 << Percent(strings_used_prefixed_, total_prefix_index_cost_ / kPrefixIndexCost) << "\n";
552 os << "Short strings " << Percent(short_strings_, short_strings_ + long_strings_) << "\n";
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700553 if (verbose_level_ >= VerboseLevel::kEverything) {
Mathieu Chartier5eae4c52018-08-30 15:52:15 -0700554 std::vector<std::pair<std::string, size_t>> pairs; // (prefixes_.begin(), prefixes_.end());
Mathieu Chartier0ddf6262018-08-23 17:37:46 -0700555 // Sort lexicographically.
556 std::sort(pairs.begin(), pairs.end());
557 for (const auto& pair : pairs) {
558 os << pair.first << " : " << pair.second << "\n";
559 }
560 }
561}
562
563} // namespace dexanalyze
564} // namespace art