summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/art213
-rw-r--r--tools/dexanalyze/Android.bp1
-rw-r--r--tools/dexanalyze/dexanalyze.cc1
-rw-r--r--tools/dexanalyze/dexanalyze_bytecode.cc131
-rw-r--r--tools/dexanalyze/dexanalyze_bytecode.h16
-rw-r--r--tools/dexanalyze/dexanalyze_experiments.cc131
-rw-r--r--tools/dexanalyze/dexanalyze_experiments.h19
-rw-r--r--tools/dexanalyze/dexanalyze_strings.cc162
-rw-r--r--tools/dexanalyze/dexanalyze_strings.h54
-rwxr-xr-xtools/golem/build-target.sh2
-rwxr-xr-xtools/run-jdwp-tests.sh8
11 files changed, 482 insertions, 256 deletions
diff --git a/tools/art b/tools/art
index aebf5a6778..7bd4c54a22 100644
--- a/tools/art
+++ b/tools/art
@@ -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"