diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/art | 213 | ||||
-rw-r--r-- | tools/dexanalyze/Android.bp | 1 | ||||
-rw-r--r-- | tools/dexanalyze/dexanalyze.cc | 1 | ||||
-rw-r--r-- | tools/dexanalyze/dexanalyze_bytecode.cc | 131 | ||||
-rw-r--r-- | tools/dexanalyze/dexanalyze_bytecode.h | 16 | ||||
-rw-r--r-- | tools/dexanalyze/dexanalyze_experiments.cc | 131 | ||||
-rw-r--r-- | tools/dexanalyze/dexanalyze_experiments.h | 19 | ||||
-rw-r--r-- | tools/dexanalyze/dexanalyze_strings.cc | 162 | ||||
-rw-r--r-- | tools/dexanalyze/dexanalyze_strings.h | 54 | ||||
-rwxr-xr-x | tools/golem/build-target.sh | 2 | ||||
-rwxr-xr-x | tools/run-jdwp-tests.sh | 8 |
11 files changed, 482 insertions, 256 deletions
@@ -16,28 +16,9 @@ # shell dialect that should work on the host (e.g. bash), and # Android (e.g. mksh). -# Globals -ART_BINARY=dalvikvm -DELETE_ANDROID_DATA="no" -LAUNCH_WRAPPER= -LIBART=libart.so -JIT_PROFILE="no" -ALLOW_DEFAULT_JDWP="no" -VERBOSE="no" -CLEAN_OAT_FILES="yes" -EXTRA_OPTIONS=() - -# Follow all sym links to get the program name. -if [ z"$BASH_SOURCE" != z ]; then - PROG_NAME="$BASH_SOURCE" -else - PROG_NAME="$0" -fi -while [ -h "$PROG_NAME" ]; do - # On Mac OS, readlink -f doesn't work. - PROG_NAME="$(readlink "$PROG_NAME")" -done - +###################################### +# Functions +###################################### function find_libdir() { # Get the actual file, $1 is the ART_BINARY_PATH and may be a symbolic link. # Use realpath instead of readlink because Android does not have a readlink. @@ -48,29 +29,6 @@ function find_libdir() { fi } -function replace_compiler_filter_with_quicken() { - ARGS_WITH_QUICKEN=("$@") - - found="false" - ((index=0)) - while ((index <= $#)); do - what="${ARGS_WITH_QUICKEN[$index]}" - - case "$what" in - --compiler-filter=*) - ARGS_WITH_QUICKEN[$index]="--compiler-filter=quicken" - found="true" - ;; - esac - - ((index++)) - shift - done - if [ "$found" != "true" ]; then - ARGS_WITH_QUICKEN=(-Xcompiler-option --compiler-filter=quicken "${ARGS_WITH_QUICKEN[@]}") - fi -} - function usage() { cat 1>&2 <<EOF Usage: art [OPTIONS] [--] [ART_OPTIONS] CLASS @@ -224,19 +182,81 @@ function detect_boot_image_location() { echo "$image_location" } -# If android logging is not explicitly set, only print warnings and errors. -if [ -z "$ANDROID_LOG_TAGS" ]; then - ANDROID_LOG_TAGS='*:w' -fi +function run_dex2oat() { + CLASS_LOADER_CONTEXT= + for dex_file in "${DEX2OAT_CLASSPATH[@]}" + do + while [ -h "$dex_file" ]; do + # On Mac OS, readlink -f doesn't work. + dex_file="$(readlink "$dex_file")" + done + # Create oat file directory. + verbose_run mkdir -p $(dirname "$dex_file")/oat/$ISA + local oat_file=$(basename "$dex_file") + local oat_file=$(dirname "$dex_file")/oat/$ISA/${oat_file%.*}.odex + # When running dex2oat use the exact same context as when running dalvikvm. + # (see run_art function) + verbose_run ANDROID_DATA=$ANDROID_DATA \ + ANDROID_ROOT=$ANDROID_ROOT \ + LD_LIBRARY_PATH=$LD_LIBRARY_PATH \ + PATH=$ANDROID_ROOT/bin:$PATH \ + LD_USE_LOAD_BIAS=1 \ + ANDROID_LOG_TAGS=$ANDROID_LOG_TAGS \ + $DEX2OAT_BINARY_PATH \ + --runtime-arg -Xnorelocate \ + --boot-image=$DEX2OAT_BOOT_IMAGE \ + --instruction-set=$ISA \ + --class-loader-context="PCL[$CLASS_LOADER_CONTEXT]" \ + "${DEX2OAT_FLAGS[@]}" \ + --dex-file=$dex_file \ + --oat-file=$oat_file + if [[ ! -z $CLASS_LOADER_CONTEXT ]]; then + CLASS_LOADER_CONTEXT+=":" + fi + CLASS_LOADER_CONTEXT+="$dex_file" + done +} + +# Extract the dex2oat flags from the list of arguments. +# -Xcompiler-options arguments are stored in DEX2OAT_FLAGS array +# -cp argument is split by ':' and stored in DEX2OAT_CLASSPATH +# -Ximage argument is stored in DEX2OAT_BOOT_IMAGE +function extract_dex2oat_flags() { + while [ $# -gt 0 ]; do + case $1 in + -Xcompiler-option) + DEX2OAT_FLAGS+=("$2") + shift + ;; + -Ximage:*) + DEX2OAT_BOOT_IMAGE=$1 + # Remove '-Ximage:' from the argument. + DEX2OAT_BOOT_IMAGE=${DEX2OAT_BOOT_IMAGE##-Ximage:} + ;; + -cp) + # Reset any previously parsed classpath, just like dalvikvm + # only supports one -cp argument. + DEX2OAT_CLASSPATH=() + # TODO: support -classpath and CLASSPATH + local oifs=$IFS + IFS=':' + for classpath_elem in $2 + do + DEX2OAT_CLASSPATH+=("$classpath_elem") + done + shift + IFS=$oifs + ;; + esac + shift + done +} # Runs dalvikvm, returns its exit code. # (Oat directories are cleaned up in between runs) function run_art() { - local image_location="$(detect_boot_image_location)" local ret - # First cleanup any left-over 'oat' files from the last time dalvikvm was run. - cleanup_oat_directory_for_classpath "$@" # Run dalvikvm. verbose_run ANDROID_DATA="$ANDROID_DATA" \ ANDROID_ROOT="$ANDROID_ROOT" \ @@ -247,7 +267,7 @@ function run_art() { $LAUNCH_WRAPPER $ART_BINARY_PATH $lib \ -XXlib:"$LIBART" \ -Xnorelocate \ - -Ximage:"$image_location" \ + -Ximage:"$DEFAULT_IMAGE_LOCATION" \ "$@" ret=$? @@ -258,6 +278,23 @@ function run_art() { return $ret } +###################################### +# Globals +###################################### +ART_BINARY=dalvikvm +DEX2OAT_BINARY=dex2oat +DELETE_ANDROID_DATA="no" +LAUNCH_WRAPPER= +LIBART=libart.so +JIT_PROFILE="no" +ALLOW_DEFAULT_JDWP="no" +VERBOSE="no" +CLEAN_OAT_FILES="yes" +EXTRA_OPTIONS=() +DEX2OAT_FLAGS=() +DEX2OAT_CLASSPATH=() + +# Parse arguments while [[ "$1" = "-"* ]]; do case "$1" in --) @@ -275,6 +312,7 @@ while [[ "$1" = "-"* ]]; do ;& # Fallthrough --debug) LIBART="libartd.so" + DEX2OAT_BINARY=dex2oatd # Expect that debug mode wants all checks. EXTRA_OPTIONS+=(-XX:SlowDebug=true) ;; @@ -329,9 +367,21 @@ if [ $# -eq 0 ]; then exit 1 fi +# Follow all sym links to get the program name. +if [ z"$BASH_SOURCE" != z ]; then + PROG_NAME="$BASH_SOURCE" +else + PROG_NAME="$0" +fi +while [ -h "$PROG_NAME" ]; do + # On Mac OS, readlink -f doesn't work. + PROG_NAME="$(readlink "$PROG_NAME")" +done + PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)" ANDROID_ROOT=$PROG_DIR/.. ART_BINARY_PATH=$ANDROID_ROOT/bin/$ART_BINARY +ISA=$($ART_BINARY_PATH -showversion | (read art version number isa && echo $isa)) if [ ! -x "$ART_BINARY_PATH" ]; then cat 1>&2 <<EOF @@ -341,8 +391,31 @@ EOF exit 1 fi +DEX2OAT_BINARY_PATH=$ANDROID_ROOT/bin/$DEX2OAT_BINARY + +if [ ! -x "$DEX2OAT_BINARY_PATH" ]; then + echo "Warning: Android Compiler not found: $DEX2OAT_BINARY_PATH" +fi + +###################################### +# Main program +###################################### + +# If android logging is not explicitly set, only print warnings and errors. +if [ -z "$ANDROID_LOG_TAGS" ]; then + ANDROID_LOG_TAGS='*:w' +fi + LIBDIR="$(find_libdir $ART_BINARY_PATH)" LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR +DEFAULT_IMAGE_LOCATION="$(detect_boot_image_location)" +DEX2OAT_BOOT_IMAGE="$DEFAULT_IMAGE_LOCATION" + +# Extract the dex2oat flags from the list of arguments. +# -Xcompiler-options arguments are stored in DEX2OAT_FLAGS array +# -cp argument is split by ':' and stored in DEX2OAT_CLASSPATH +# -Ximage argument is stored in DEX2OAT_BOOTIMAGE +extract_dex2oat_flags "$@" # If ANDROID_DATA is the system ANDROID_DATA or is not set, use our own, # and ensure we delete it at the end. @@ -360,31 +433,34 @@ fi if [ "$PERF" != "" ]; then LAUNCH_WRAPPER="perf record -g --call-graph dwarf -F 10000 -o $ANDROID_DATA/perf.data -e cycles:u $LAUNCH_WRAPPER" - EXTRA_OPTIONS+=(-Xcompiler-option --generate-debug-info) + DEX2OAT_FLAGS+=(--generate-debug-info) fi if [ "$ALLOW_DEFAULT_JDWP" = "no" ]; then EXTRA_OPTIONS+=(-XjdwpProvider:none) fi +# First cleanup any left-over 'oat' files from the last time dalvikvm was run. +cleanup_oat_directory_for_classpath "$@" + +# Protect additional arguments in quotes to preserve whitespaces (used by +# run-jdwp-test.sh when running on device), '$' (may be used as part of +# classpath) and other special characters when evaluated. +EXTRA_OPTIONS+=("$@") + if [ "$JIT_PROFILE" = "yes" ]; then # Create the profile. The runtime expects profiles to be created before # execution. PROFILE_PATH="$ANDROID_DATA/primary.prof" touch "$PROFILE_PATH" - # Replace the compiler filter with quicken so that we - # can capture the profile. - ARGS_WITH_QUICKEN= - replace_compiler_filter_with_quicken "$@" - run_art -Xjitsaveprofilinginfo \ -Xps-min-methods-to-save:1 \ -Xps-min-classes-to-save:1 \ -Xps-min-notification-before-wake:10 \ -Xps-profile-path:$PROFILE_PATH \ -Xusejit:true \ - "${ARGS_WITH_QUICKEN[@]}" \ + ${EXTRA_OPTIONS[@]} \ &> "$ANDROID_DATA/profile_gen.log" EXIT_STATUS=$? @@ -400,13 +476,20 @@ if [ "$JIT_PROFILE" = "yes" ]; then rm -rf "$ANDROID_DATA/dalvik-cache" # Append arguments so next invocation of run_art uses the profile. - EXTRA_OPTIONS+=(-Xcompiler-option --profile-file="$PROFILE_PATH") + DEX2OAT_FLAGS+=(--profile-file="$PROFILE_PATH") fi -# Protect additional arguments in quotes to preserve whitespaces (used by -# run-jdwp-test.sh when running on device), '$' (may be used as part of -# classpath) and other special characters when evaluated. -EXTRA_OPTIONS+=("$@") +if [ -x "$DEX2OAT_BINARY_PATH" ]; then + # Run dex2oat before launching ART to generate the oat files for the classpath. + run_dex2oat +fi + +# Do not continue if the dex2oat failed. +EXIT_STATUS=$? +if [ $EXIT_STATUS != 0 ]; then + echo "Failed dex2oat invocation" >&2 + exit $EXIT_STATUS +fi run_art "${EXTRA_OPTIONS[@]}" EXIT_STATUS=$? diff --git a/tools/dexanalyze/Android.bp b/tools/dexanalyze/Android.bp index 9515ca5c50..a85bf562af 100644 --- a/tools/dexanalyze/Android.bp +++ b/tools/dexanalyze/Android.bp @@ -22,6 +22,7 @@ cc_defaults { "dexanalyze.cc", "dexanalyze_bytecode.cc", "dexanalyze_experiments.cc", + "dexanalyze_strings.cc", ], target: { android: { diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc index 841719b821..040f41ba5d 100644 --- a/tools/dexanalyze/dexanalyze.cc +++ b/tools/dexanalyze/dexanalyze.cc @@ -23,6 +23,7 @@ #include "dexanalyze_bytecode.h" #include "dexanalyze_experiments.h" +#include "dexanalyze_strings.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" diff --git a/tools/dexanalyze/dexanalyze_bytecode.cc b/tools/dexanalyze/dexanalyze_bytecode.cc index 0bb3f911a2..e6e58c0ecc 100644 --- a/tools/dexanalyze/dexanalyze_bytecode.cc +++ b/tools/dexanalyze/dexanalyze_bytecode.cc @@ -50,6 +50,53 @@ static inline SafeMap<T, U> SortByOrder(const SafeMap<T, U>& usage, Order order) return ret; } +template <typename A, typename B> +std::ostream& operator <<(std::ostream& os, const std::pair<A, B>& pair) { + return os << "{" << pair.first << ", " << pair.second << "}"; +} + +template <typename T, typename... Args, template <typename...> class ArrayType> +SafeMap<size_t, T> MakeUsageMap(const ArrayType<T, Args...>& array) { + SafeMap<size_t, T> ret; + for (size_t i = 0; i < array.size(); ++i) { + if (array[i] > 0) { + ret.Put(i, array[i]); + } + } + return ret; +} + +template <typename T, typename U, typename... Args, template <typename...> class Map> +void PrintMostUsed(std::ostream& os, + const Map<T, U, Args...>& usage, + size_t max_count, + std::function<void(std::ostream& os, T)> printer = + [](std::ostream& os, T v) { + os << v; + }) { + std::vector<std::pair<U, T>> sorted; + uint64_t total = 0u; + for (const auto& pair : usage) { + sorted.emplace_back(pair.second, pair.first); + total += pair.second; + } + std::sort(sorted.rbegin(), sorted.rend()); + uint64_t other = 0u; + for (auto&& pair : sorted) { + if (max_count > 0) { + os << Percent(pair.first, total) << " : "; + printer(os, pair.second); + os << "\n"; + --max_count; + } else { + other += pair.first; + } + } + if (other != 0u) { + os << "other: " << Percent(other, total) << "\n"; + } +} + static inline std::ostream& operator<<(std::ostream& os, const std::vector<uint8_t>& bytes) { os << std::hex; for (const uint8_t& c : bytes) { @@ -125,32 +172,42 @@ void NewRegisterInstructions::Dump(std::ostream& os, uint64_t total_size) const std::vector<std::pair<size_t, std::vector<uint8_t>>> pairs; for (auto&& pair : instruction_freq_) { if (pair.second > 0 && !pair.first.empty()) { - // Savings exclude one byte per occurrence and one occurence from having the macro + // Savings exclude one byte per occurrence and one occurrence from having the macro // dictionary. pairs.emplace_back((pair.second - 1) * (pair.first.size() - 1), pair.first); } } std::sort(pairs.rbegin(), pairs.rend()); static constexpr size_t kMaxMacros = 128; + static constexpr size_t kMaxPrintedMacros = 32; uint64_t top_instructions_savings = 0u; for (size_t i = 0; i < kMaxMacros && i < pairs.size(); ++i) { top_instructions_savings += pairs[i].first; } if (verbose_level_ >= VerboseLevel::kNormal) { + os << "Move result register distribution" << "\n"; + PrintMostUsed(os, MakeUsageMap(move_result_reg_), 16); + os << "First arg register usage\n"; + std::function<void(std::ostream& os, size_t)> printer = [&](std::ostream& os, size_t idx) { + os << Instruction::Name(static_cast<Instruction::Code>(idx)); + }; + PrintMostUsed(os, MakeUsageMap(first_arg_reg_count_), 16, printer); + os << "Most used field linkage pairs\n"; + PrintMostUsed(os, field_linkage_counts_, 32); + os << "Current extended " << extended_field_ << "\n"; + os << "Most used method linkage pairs\n"; + PrintMostUsed(os, method_linkage_counts_, 32); + os << "Current extended " << extended_method_ << "\n"; os << "Top " << kMaxMacros << " instruction bytecode sizes and hex dump" << "\n"; for (size_t i = 0; i < kMaxMacros && i < pairs.size(); ++i) { auto bytes = pairs[i].second; // Remove opcode bytes. bytes.erase(bytes.begin()); - os << Percent(pairs[i].first, total_size) << " " - << Instruction::Name(static_cast<Instruction::Code>(pairs[i].second[0])) - << "(" << bytes << ")\n"; - } - os << "Move result register distribution" << "\n"; - const size_t move_result_total = - std::accumulate(move_result_reg_.begin(), move_result_reg_.end(), 0u); - for (size_t i = 0; i < move_result_reg_.size(); ++i) { - os << i << ": " << Percent(move_result_reg_[i], move_result_total) << "\n"; + if (i < kMaxPrintedMacros) { + os << Percent(pairs[i].first, total_size) << " " + << Instruction::Name(static_cast<Instruction::Code>(pairs[i].second[0])) + << "(" << bytes << ")\n"; + } } } os << "Top instructions 1b macro savings " @@ -164,10 +221,7 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file, std::map<size_t, TypeLinkage>& types) { TypeLinkage& current_type = types[current_class_type.index_]; bool skip_next = false; - for (auto inst = code_item.begin(); ; ++inst) { - if (inst == code_item.end()) { - break; - } + for (auto inst = code_item.begin(); inst != code_item.end(); ++inst) { if (verbose_level_ >= VerboseLevel::kEverything) { std::cout << std::endl; std::cout << inst->DumpString(nullptr); @@ -182,6 +236,7 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file, bool is_iget = false; const Instruction::Code opcode = inst->Opcode(); Instruction::Code new_opcode = opcode; + ++opcode_count_[opcode]; switch (opcode) { case Instruction::IGET: case Instruction::IGET_WIDE: @@ -209,18 +264,18 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file, uint32_t receiver = inst->VRegB_22c(); uint32_t first_arg_reg = code_item.RegistersSize() - code_item.InsSize(); uint32_t out_reg = inst->VRegA_22c(); - if (Enabled(kExperimentInstanceFieldSelf) && - first_arg_reg == receiver && - holder_type == current_class_type) { - if (count_types) { - ++current_type.fields_.FindOrAdd(dex_field_idx)->second; - } else { - uint32_t field_idx = types[holder_type.index_].fields_.Get(dex_field_idx); - ExtendPrefix(&out_reg, &field_idx); - CHECK(InstNibbles(new_opcode, {out_reg, field_idx})); - continue; - } - } else if (Enabled(kExperimentInstanceField)) { + if (Enabled(kExperimentInstanceFieldSelf) && + first_arg_reg == receiver && + holder_type == current_class_type) { + if (count_types) { + ++current_type.fields_.FindOrAdd(dex_field_idx)->second; + } else { + uint32_t field_idx = types[holder_type.index_].fields_.Get(dex_field_idx); + ExtendPrefix(&out_reg, &field_idx); + CHECK(InstNibbles(new_opcode, {out_reg, field_idx})); + continue; + } + } else if (Enabled(kExperimentInstanceField)) { if (count_types) { ++current_type.types_.FindOrAdd(holder_type.index_)->second; ++types[holder_type.index_].fields_.FindOrAdd(dex_field_idx)->second; @@ -288,7 +343,8 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file, } else { uint32_t type_idx = current_type.types_.Get(holder_type.index_); uint32_t field_idx = types[holder_type.index_].fields_.Get(dex_field_idx); - ExtendPrefix(&type_idx, &field_idx); + ++field_linkage_counts_[std::make_pair(type_idx, field_idx)]; + extended_field_ += ExtendPrefix(&type_idx, &field_idx) ? 1u : 0u; if (InstNibbles(new_opcode, {out_reg >> 4, out_reg & 0xF, type_idx, field_idx})) { continue; } @@ -313,6 +369,7 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file, } else { uint32_t args[6] = {}; uint32_t arg_count = inst->GetVarArgs(args); + const uint32_t first_arg_reg = code_item.RegistersSize() - code_item.InsSize(); bool next_move_result = false; uint32_t dest_reg = 0; @@ -330,6 +387,7 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file, uint32_t type_idx = current_type.types_.Get(receiver_type.index_); uint32_t local_idx = types[receiver_type.index_].methods_.Get(method_idx); + ++method_linkage_counts_[std::make_pair(type_idx, local_idx)]; // If true, we always put the return value in r0. static constexpr bool kMoveToDestReg = true; @@ -338,15 +396,21 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file, if (kMoveToDestReg && arg_count % 2 == 1) { // Use the extra nibble to sneak in part of the type index. new_args.push_back(local_idx >> 4); - local_idx ^= local_idx & 0xF0; + local_idx &= ~0xF0; } - ExtendPrefix(&type_idx, &local_idx); + extended_method_ += ExtendPrefix(&type_idx, &local_idx) ? 1u : 0u; new_args.push_back(type_idx); new_args.push_back(local_idx); if (!kMoveToDestReg) { ExtendPrefix(&dest_reg, &local_idx); new_args.push_back(dest_reg); } + for (size_t i = 0; i < arg_count; ++i) { + if (args[i] == first_arg_reg) { + ++first_arg_reg_count_[opcode]; + break; + } + } new_args.insert(new_args.end(), args, args + arg_count); if (InstNibbles(opcode, new_args)) { skip_next = next_move_result; @@ -467,18 +531,18 @@ void NewRegisterInstructions::Add(Instruction::Code opcode, const Instruction& i ++instruction_freq_[std::vector<uint8_t>(buffer_.begin() + buffer_start, buffer_.end())]; } -void NewRegisterInstructions::ExtendPrefix(uint32_t* value1, uint32_t* value2) { +bool NewRegisterInstructions::ExtendPrefix(uint32_t* value1, uint32_t* value2) { if (*value1 < 16 && *value2 < 16) { - return; + return false; } if ((*value1 >> 4) == 1 && *value2 < 16) { InstNibbles(0xE5, {}); *value1 ^= 1u << 4; - return; + return true; } else if ((*value2 >> 4) == 1 && *value1 < 16) { InstNibbles(0xE6, {}); *value2 ^= 1u << 4; - return; + return true; } if (*value1 < 256 && *value2 < 256) { // Extend each value by 4 bits. @@ -495,6 +559,7 @@ void NewRegisterInstructions::ExtendPrefix(uint32_t* value1, uint32_t* value2) { } *value1 &= 0xF; *value2 &= 0XF; + return true; } bool NewRegisterInstructions::InstNibbles(uint8_t opcode, const std::vector<uint32_t>& args) { diff --git a/tools/dexanalyze/dexanalyze_bytecode.h b/tools/dexanalyze/dexanalyze_bytecode.h index db009b03b8..015801f516 100644 --- a/tools/dexanalyze/dexanalyze_bytecode.h +++ b/tools/dexanalyze/dexanalyze_bytecode.h @@ -54,7 +54,11 @@ struct TypeLinkage { class NewRegisterInstructions : public Experiment { public: - explicit NewRegisterInstructions(uint64_t experiments) : experiments_(experiments) {} + explicit NewRegisterInstructions(uint64_t experiments) + : experiments_(experiments), + move_result_reg_(256), + first_arg_reg_count_(256), + opcode_count_(256) {} void ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files); void Dump(std::ostream& os, uint64_t total_size) const; @@ -65,7 +69,7 @@ class NewRegisterInstructions : public Experiment { std::map<size_t, TypeLinkage>& types); void Add(Instruction::Code opcode, const Instruction& inst); bool InstNibbles(uint8_t opcode, const std::vector<uint32_t>& args); - void ExtendPrefix(uint32_t* value1, uint32_t* value2); + bool ExtendPrefix(uint32_t* value1, uint32_t* value2); bool Enabled(BytecodeExperiment experiment) const { return experiments_ & (1u << static_cast<uint64_t>(experiment)); } @@ -76,7 +80,13 @@ class NewRegisterInstructions : public Experiment { uint64_t deduped_size_ = 0u; uint64_t dex_code_bytes_ = 0u; uint64_t experiments_ = std::numeric_limits<uint64_t>::max(); - std::array<size_t, 256> move_result_reg_; + uint64_t extended_field_ = 0u; + uint64_t extended_method_ = 0u; + std::vector<size_t> move_result_reg_; + std::vector<size_t> first_arg_reg_count_; + std::vector<size_t> opcode_count_; + std::map<std::pair<uint32_t, uint32_t>, size_t> method_linkage_counts_; + std::map<std::pair<uint32_t, uint32_t>, size_t> field_linkage_counts_; std::map<std::vector<uint8_t>, size_t> instruction_freq_; // Output instruction buffer. std::vector<uint8_t> buffer_; diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index 1f6fe4694e..b124f433b3 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -208,137 +208,6 @@ void AnalyzeDebugInfo::Dump(std::ostream& os, uint64_t total_size) const { << Percent(total_unique_non_header_bytes_, total_size) << "\n"; } -void AnalyzeStrings::ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) { - std::set<std::string> unique_strings; - for (const std::unique_ptr<const DexFile>& dex_file : dex_files) { - for (size_t i = 0; i < dex_file->NumStringIds(); ++i) { - uint32_t length = 0; - const char* data = dex_file->StringDataAndUtf16LengthByIdx(dex::StringIndex(i), &length); - // Analyze if the string has any UTF16 chars. - bool have_wide_char = false; - const char* ptr = data; - for (size_t j = 0; j < length; ++j) { - have_wide_char = have_wide_char || GetUtf16FromUtf8(&ptr) >= 0x100; - } - if (have_wide_char) { - wide_string_bytes_ += 2 * length; - } else { - ascii_string_bytes_ += length; - } - string_data_bytes_ += ptr - data; - unique_strings.insert(data); - } - } - // Unique strings only since we want to exclude savings from multidex duplication. - std::vector<std::string> strings(unique_strings.begin(), unique_strings.end()); - unique_strings.clear(); - - // Tunable parameters. - static const size_t kMinPrefixLen = 1; - static const size_t kMaxPrefixLen = 255; - static const size_t kPrefixConstantCost = 4; - static const size_t kPrefixIndexCost = 2; - - // Calculate total shared prefix. - std::vector<size_t> shared_len; - prefixes_.clear(); - for (size_t i = 0; i < strings.size(); ++i) { - size_t best_len = 0; - if (i > 0) { - best_len = std::max(best_len, PrefixLen(strings[i], strings[i - 1])); - } - if (i < strings.size() - 1) { - best_len = std::max(best_len, PrefixLen(strings[i], strings[i + 1])); - } - best_len = std::min(best_len, kMaxPrefixLen); - std::string prefix; - if (best_len >= kMinPrefixLen) { - prefix = strings[i].substr(0, best_len); - ++prefixes_[prefix]; - } - total_prefix_index_cost_ += kPrefixIndexCost; - } - // Optimize the result by moving long prefixes to shorter ones if it causes savings. - while (true) { - bool have_savings = false; - auto it = prefixes_.begin(); - std::vector<std::string> longest; - for (const auto& pair : prefixes_) { - longest.push_back(pair.first); - } - std::sort(longest.begin(), longest.end(), [](const std::string& a, const std::string& b) { - return a.length() > b.length(); - }); - // Do longest first since this provides the best results. - for (const std::string& s : longest) { - it = prefixes_.find(s); - CHECK(it != prefixes_.end()); - const std::string& prefix = it->first; - int64_t best_savings = 0u; - int64_t best_len = -1; - for (int64_t len = prefix.length() - 1; len >= 0; --len) { - auto found = prefixes_.find(prefix.substr(0, len)); - if (len != 0 && found == prefixes_.end()) { - continue; - } - // Calculate savings from downgrading the prefix. - int64_t savings = kPrefixConstantCost + prefix.length() - - (prefix.length() - len) * it->second; - if (savings > best_savings) { - best_savings = savings; - best_len = len; - break; - } - } - if (best_len != -1) { - prefixes_[prefix.substr(0, best_len)] += it->second; - it = prefixes_.erase(it); - optimization_savings_ += best_savings; - have_savings = true; - } else { - ++it; - } - } - if (!have_savings) { - break; - } - } - total_num_prefixes_ += prefixes_.size(); - for (const auto& pair : prefixes_) { - // 4 bytes for an offset, one for length. - total_prefix_dict_ += pair.first.length(); - total_prefix_table_ += kPrefixConstantCost; - total_prefix_savings_ += pair.first.length() * pair.second; - } -} - -void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const { - os << "Total string data bytes " << Percent(string_data_bytes_, total_size) << "\n"; - os << "UTF-16 string data bytes " << Percent(wide_string_bytes_, total_size) << "\n"; - os << "ASCII string data bytes " << Percent(ascii_string_bytes_, total_size) << "\n"; - - // Prefix based strings. - os << "Total shared prefix bytes " << Percent(total_prefix_savings_, total_size) << "\n"; - os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n"; - os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n"; - os << "Prefix index cost " << Percent(total_prefix_index_cost_, total_size) << "\n"; - int64_t net_savings = total_prefix_savings_; - net_savings -= total_prefix_dict_; - net_savings -= total_prefix_table_; - net_savings -= total_prefix_index_cost_; - os << "Prefix dictionary elements " << total_num_prefixes_ << "\n"; - os << "Optimization savings " << Percent(optimization_savings_, total_size) << "\n"; - os << "Prefix net savings " << Percent(net_savings, total_size) << "\n"; - if (verbose_level_ >= VerboseLevel::kEverything) { - std::vector<std::pair<std::string, size_t>> pairs(prefixes_.begin(), prefixes_.end()); - // Sort lexicographically. - std::sort(pairs.begin(), pairs.end()); - for (const auto& pair : pairs) { - os << pair.first << " : " << pair.second << "\n"; - } - } -} - void CountDexIndices::ProcessDexFiles( const std::vector<std::unique_ptr<const DexFile>>& dex_files) { std::set<std::string> unique_field_names; diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h index 4e66b3cf3b..3542d959ba 100644 --- a/tools/dexanalyze/dexanalyze_experiments.h +++ b/tools/dexanalyze/dexanalyze_experiments.h @@ -62,25 +62,6 @@ class Experiment { VerboseLevel verbose_level_ = VerboseLevel::kNormal; }; -// Analyze string data and strings accessed from code. -class AnalyzeStrings : public Experiment { - public: - void ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) OVERRIDE; - void Dump(std::ostream& os, uint64_t total_size) const OVERRIDE; - - private: - int64_t wide_string_bytes_ = 0u; - int64_t ascii_string_bytes_ = 0u; - int64_t string_data_bytes_ = 0u; - int64_t total_prefix_savings_ = 0u; - int64_t total_prefix_dict_ = 0u; - int64_t total_prefix_table_ = 0u; - int64_t total_prefix_index_cost_ = 0u; - int64_t total_num_prefixes_ = 0u; - int64_t optimization_savings_ = 0u; - std::unordered_map<std::string, size_t> prefixes_; -}; - // Analyze debug info sizes. class AnalyzeDebugInfo : public Experiment { public: diff --git a/tools/dexanalyze/dexanalyze_strings.cc b/tools/dexanalyze/dexanalyze_strings.cc new file mode 100644 index 0000000000..9f67ff431a --- /dev/null +++ b/tools/dexanalyze/dexanalyze_strings.cc @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dexanalyze_strings.h" + +#include <algorithm> +#include <iomanip> +#include <iostream> + +#include "dex/class_accessor-inl.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_instruction-inl.h" + +namespace art { +namespace dexanalyze { + +void AnalyzeStrings::ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) { + std::set<std::string> unique_strings; + for (const std::unique_ptr<const DexFile>& dex_file : dex_files) { + for (size_t i = 0; i < dex_file->NumStringIds(); ++i) { + uint32_t length = 0; + const char* data = dex_file->StringDataAndUtf16LengthByIdx(dex::StringIndex(i), &length); + // Analyze if the string has any UTF16 chars. + bool have_wide_char = false; + const char* ptr = data; + for (size_t j = 0; j < length; ++j) { + have_wide_char = have_wide_char || GetUtf16FromUtf8(&ptr) >= 0x100; + } + if (have_wide_char) { + wide_string_bytes_ += 2 * length; + } else { + ascii_string_bytes_ += length; + } + string_data_bytes_ += ptr - data; + unique_strings.insert(data); + } + } + // Unique strings only since we want to exclude savings from multidex duplication. + std::vector<std::string> strings(unique_strings.begin(), unique_strings.end()); + unique_strings.clear(); + + // Tunable parameters. + static const size_t kMinPrefixLen = 1; + static const size_t kMaxPrefixLen = 255; + static const size_t kPrefixConstantCost = 4; + static const size_t kPrefixIndexCost = 2; + + // Calculate total shared prefix. + std::vector<size_t> shared_len; + prefixes_.clear(); + for (size_t i = 0; i < strings.size(); ++i) { + size_t best_len = 0; + if (i > 0) { + best_len = std::max(best_len, PrefixLen(strings[i], strings[i - 1])); + } + if (i < strings.size() - 1) { + best_len = std::max(best_len, PrefixLen(strings[i], strings[i + 1])); + } + best_len = std::min(best_len, kMaxPrefixLen); + std::string prefix; + if (best_len >= kMinPrefixLen) { + prefix = strings[i].substr(0, best_len); + ++prefixes_[prefix]; + } + total_prefix_index_cost_ += kPrefixIndexCost; + } + // Optimize the result by moving long prefixes to shorter ones if it causes savings. + while (true) { + bool have_savings = false; + auto it = prefixes_.begin(); + std::vector<std::string> longest; + for (const auto& pair : prefixes_) { + longest.push_back(pair.first); + } + std::sort(longest.begin(), longest.end(), [](const std::string& a, const std::string& b) { + return a.length() > b.length(); + }); + // Do longest first since this provides the best results. + for (const std::string& s : longest) { + it = prefixes_.find(s); + CHECK(it != prefixes_.end()); + const std::string& prefix = it->first; + int64_t best_savings = 0u; + int64_t best_len = -1; + for (int64_t len = prefix.length() - 1; len >= 0; --len) { + auto found = prefixes_.find(prefix.substr(0, len)); + if (len != 0 && found == prefixes_.end()) { + continue; + } + // Calculate savings from downgrading the prefix. + int64_t savings = kPrefixConstantCost + prefix.length() - + (prefix.length() - len) * it->second; + if (savings > best_savings) { + best_savings = savings; + best_len = len; + break; + } + } + if (best_len != -1) { + prefixes_[prefix.substr(0, best_len)] += it->second; + it = prefixes_.erase(it); + optimization_savings_ += best_savings; + have_savings = true; + } else { + ++it; + } + } + if (!have_savings) { + break; + } + } + total_num_prefixes_ += prefixes_.size(); + for (const auto& pair : prefixes_) { + // 4 bytes for an offset, one for length. + total_prefix_dict_ += pair.first.length(); + total_prefix_table_ += kPrefixConstantCost; + total_prefix_savings_ += pair.first.length() * pair.second; + } +} + +void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const { + os << "Total string data bytes " << Percent(string_data_bytes_, total_size) << "\n"; + os << "UTF-16 string data bytes " << Percent(wide_string_bytes_, total_size) << "\n"; + os << "ASCII string data bytes " << Percent(ascii_string_bytes_, total_size) << "\n"; + + // Prefix based strings. + os << "Total shared prefix bytes " << Percent(total_prefix_savings_, total_size) << "\n"; + os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n"; + os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n"; + os << "Prefix index cost " << Percent(total_prefix_index_cost_, total_size) << "\n"; + int64_t net_savings = total_prefix_savings_; + net_savings -= total_prefix_dict_; + net_savings -= total_prefix_table_; + net_savings -= total_prefix_index_cost_; + os << "Prefix dictionary elements " << total_num_prefixes_ << "\n"; + os << "Optimization savings " << Percent(optimization_savings_, total_size) << "\n"; + os << "Prefix net savings " << Percent(net_savings, total_size) << "\n"; + if (verbose_level_ >= VerboseLevel::kEverything) { + std::vector<std::pair<std::string, size_t>> pairs(prefixes_.begin(), prefixes_.end()); + // Sort lexicographically. + std::sort(pairs.begin(), pairs.end()); + for (const auto& pair : pairs) { + os << pair.first << " : " << pair.second << "\n"; + } + } +} + +} // namespace dexanalyze +} // namespace art diff --git a/tools/dexanalyze/dexanalyze_strings.h b/tools/dexanalyze/dexanalyze_strings.h new file mode 100644 index 0000000000..a5c202e31f --- /dev/null +++ b/tools/dexanalyze/dexanalyze_strings.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_TOOLS_DEXANALYZE_DEXANALYZE_STRINGS_H_ +#define ART_TOOLS_DEXANALYZE_DEXANALYZE_STRINGS_H_ + +#include <array> +#include <vector> +#include <map> + +#include "base/safe_map.h" +#include "dexanalyze_experiments.h" +#include "dex/code_item_accessors.h" +#include "dex/utf-inl.h" + +namespace art { +namespace dexanalyze { + +// Analyze string data and strings accessed from code. +class AnalyzeStrings : public Experiment { + public: + void ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) OVERRIDE; + void Dump(std::ostream& os, uint64_t total_size) const OVERRIDE; + + private: + int64_t wide_string_bytes_ = 0u; + int64_t ascii_string_bytes_ = 0u; + int64_t string_data_bytes_ = 0u; + int64_t total_prefix_savings_ = 0u; + int64_t total_prefix_dict_ = 0u; + int64_t total_prefix_table_ = 0u; + int64_t total_prefix_index_cost_ = 0u; + int64_t total_num_prefixes_ = 0u; + int64_t optimization_savings_ = 0u; + std::unordered_map<std::string, size_t> prefixes_; +}; + +} // namespace dexanalyze +} // namespace art + +#endif // ART_TOOLS_DEXANALYZE_DEXANALYZE_STRINGS_H_ diff --git a/tools/golem/build-target.sh b/tools/golem/build-target.sh index 921a8cbe36..45c9125930 100755 --- a/tools/golem/build-target.sh +++ b/tools/golem/build-target.sh @@ -367,7 +367,7 @@ if [[ "$make_tarball" == "make_tarball" ]]; then dirs_rooted+=("$root_dir/$tar_dir") done - execute tar -czf "${tarball}" "${dirs_rooted[@]}" --exclude .git --exclude .gitignore + execute tar -czf "${tarball}" --exclude ".git" --exclude ".gitignore" "${dirs_rooted[@]}" tar_result=$? if [[ $tar_result -ne 0 ]]; then [[ -f $tarball ]] && rm $tarball diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index a435f2e03e..b0b5810dcc 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -311,10 +311,10 @@ if [[ "$plugin" != "" ]]; then vm_args="$vm_args --vm-arg $plugin" fi -if $use_jit; then - vm_args="$vm_args --vm-arg -Xcompiler-option --vm-arg --compiler-filter=quicken" - debuggee_args="$debuggee_args -Xcompiler-option --compiler-filter=quicken" -fi +# Because we're running debuggable, we discard any AOT code. +# Therefore we run de2oat with 'quicken' to avoid spending time compiling. +vm_args="$vm_args --vm-arg -Xcompiler-option --vm-arg --compiler-filter=quicken" +debuggee_args="$debuggee_args -Xcompiler-option --compiler-filter=quicken" if $instant_jit; then debuggee_args="$debuggee_args -Xjitthreshold:0" |