blob: 56bffa85eb90e9a0d3d103682cebc1f67f32df40 [file] [log] [blame]
#!/bin/bash
#
# Copyright 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.
usage() {
cat <<EOF
Usage: run_app_with_prefetch --package <name> [OPTIONS]...
-p, --package <name> package of the app to test
-a, --activity <name> activity to use
-h, --help usage information (this)
-v, --verbose enable extra verbose printing
-i, --input <file> trace file protobuf (default 'TraceFile.pb')
-r, --readahead <mode> cold, warm, fadvise, mlock (default 'warm')
-w, --when <when> aot or jit (default 'aot')
-c, --count <count> how many times to run (default 1)
-s, --sleep <sec> how long to sleep after readahead
-t, --timeout <sec> how many seconds to timeout in between each app run (default 10)
-o, --output <file.csv> what file to write the performance results into as csv (default stdout)
EOF
}
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$DIR/lib/common"
needs_trace_file="n"
input_file=""
package=""
mode='warm'
count=2
sleep_time=2
timeout=10
output="" # stdout by default
when="aot"
parse_arguments() {
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
-p|--package)
package="$2"
shift
;;
-a|--activity)
activity="$2"
shift
;;
-i|--input)
input_file="$2"
shift
;;
-v|--verbose)
export verbose="y"
;;
-r|--readahead)
mode="$2"
shift
;;
-c|--count)
count="$2"
((count+=1))
shift
;;
-s|--sleep)
sleep_time="$2"
shift
;;
-t|--timeout)
timeout="$2"
shift
;;
-o|--output)
output="$2"
shift
;;
-w|--when)
when="$2"
shift
;;
--compiler-filter)
compiler_filter="$2"
shift
;;
*)
echo "Invalid argument: $1" >&2
exit 1
esac
shift
done
}
echo_to_output_file() {
if [[ "x$output" != x ]]; then
echo "$@" >> $output
fi
# Always echo to stdout as well.
echo "$@"
}
find_package_path() {
local pkg="$1"
res="$(adb shell find "/data/app/$pkg"-'*' -maxdepth 0 2> /dev/null)"
if [[ -z $res ]]; then
res="$(adb shell find "/system/app/$pkg"-'*' -maxdepth 0 2> /dev/null)"
fi
echo "$res"
}
# Main entry point
if [[ $# -eq 0 ]]; then
usage
exit 1
else
parse_arguments "$@"
# if we do not have have package exit early with an error
[[ "$package" == "" ]] && echo "--package not specified" 1>&2 && exit 1
if [[ $mode != "cold" && $mode != "warm" ]]; then
needs_trace_file="y"
if [[ -z "$input_file" ]] || ! [[ -f $input_file ]]; then
echo "--input not specified" 1>&2
exit 1
fi
fi
if [[ "$activity" == "" ]]; then
activity="$(get_activity_name "$package")"
if [[ "$activity" == "" ]]; then
echo "Activity name could not be found, invalid package name?" 1>&2
exit 1
else
verbose_print "Activity name inferred: " "$activity"
fi
fi
fi
adb root > /dev/null
if [[ ($when == jit) || ($when == aot) ]] && [[ "$(adb shell getenforce)" != "Permissive" ]]; then
echo "Disable selinux permissions and restart framework."
adb shell setenforce 0
adb shell stop
adb shell start
adb wait-for-device
fi
# TODO: set performance governor etc, preferrably only once
# before every single app run.
# Kill everything before running.
remote_pkill "$package"
sleep 1
timings_array=()
package_path="$(find_package_path "$package")"
if [[ $? -ne 0 ]]; then
echo "Failed to detect package path for '$package'" >&2
exit 1
fi
verbose_print "Package was in path '$package_path'"
keep_application_trace_file=n
application_trace_file_path="$package_path/TraceFile.pb"
trace_file_directory="$package_path"
if [[ $needs_trace_file == y ]]; then
# system server always passes down the package path in a hardcoded spot.
if [[ $when == "jit" ]]; then
verbose_print adb push "$input_file" "$application_trace_file_path"
adb push "$input_file" "$application_trace_file_path"
keep_application_trace_file="y"
else
# otherwise use a temporary directory to get normal non-jit behavior.
trace_file_directory="/data/local/tmp/prefetch/$package"
adb shell mkdir -p "$trace_file_directory"
verbose_print adb push "$input_file" "$trace_file_directory/TraceFile.pb"
adb push "$input_file" "$trace_file_directory/TraceFile.pb"
fi
fi
# Everything other than JIT: remove the trace file,
# otherwise system server activity hints will kick in
# and the new just-in-time app pre-warmup will happen.
if [[ $keep_application_trace_file == "n" ]]; then
adb shell "[[ -f '$application_trace_file_path' ]] && rm '$application_trace_file_path'"
fi
# Perform AOT readahead/pinning/etc when an application is about to be launched.
# For JIT readahead, we allow the system to handle it itself (this is a no-op).
#
# For warm, cold, etc modes which don't need readahead this is always a no-op.
perform_aot() {
local the_when="$1" # user: aot, jit
local the_mode="$2" # warm, cold, fadvise, mlock, etc.
if [[ $the_when != "aot" ]]; then
# TODO: just in time implementation.. should probably use system server.
return 0
fi
# any non-warm/non-cold modes should use the iorap-activity-hint wrapper script.
if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then
# TODO: add activity_hint_sender.exp
verbose_print "starting with package=$package package_path=$trace_file_directory"
coproc hint_sender_fd { $ANDROID_BUILD_TOP/system/iorap/src/sh/activity_hint_sender.exp "$package" "$trace_file_directory" "$the_mode"; }
hint_sender_pid=$!
verbose_print "Activity hint sender began"
notification_success="n"
while read -r -u "${hint_sender_fd[0]}" hint_sender_output; do
verbose_print "$hint_sender_output"
if [[ "$hint_sender_output" == "Press any key to send completed event..."* ]]; then
verbose_print "WE DID SEE NOTIFICATION SUCCESS."
notification_success='y'
# Give it some time to actually perform the readaheads.
sleep $sleep_time
break
fi
done
if [[ $notification_success == 'n' ]]; then
echo "[FATAL] Activity hint notification failed." 1>&2
exit 1
fi
fi
}
perform_aot_cleanup() {
local the_when="$1" # user: aot, jit
local the_mode="$2" # warm, cold, fadvise, mlock, etc.
if [[ $the_when != "aot" ]]; then
# TODO: just in time implementation.. should probably use system server.
return 0
fi
# any non-warm/non-cold modes should use the iorap-activity-hint wrapper script.
if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then
# Clean up the hint sender by telling it that the launch was completed,
# and to shutdown the watcher.
echo "Done\n" >&"${hint_sender_fd[1]}"
while read -r -u "${hint_sender_fd[0]}" hint_sender_output; do
verbose_print "$hint_sender_output"
done
wait $hint_sender_pid
fi
}
configure_compiler_filter() {
local the_compiler_filter="$1"
local the_package="$2"
local the_activity="$3"
if [[ -z $the_compiler_filter ]]; then
verbose_print "No --compiler-filter specified, don't need to force it."
return 0
fi
local current_compiler_filter_info="$("$DIR"/query_compiler_filter.py --package "$the_package")"
local res=$?
if [[ $res -ne 0 ]]; then
return $res
fi
local current_compiler_filter
local current_reason
local current_isa
read current_compiler_filter current_reason current_isa <<< "$current_compiler_filter_info"
verbose_print "Compiler Filter="$current_compiler_filter "Reason="$current_reason "Isa="$current_isa
# Don't trust reasons that aren't 'unknown' because that means we didn't manually force the compilation filter.
# (e.g. if any automatic system-triggered compilations are not unknown).
if [[ $current_reason != "unknown" ]] || [[ $current_compiler_filter != $the_compiler_filter ]]; then
verbose_print "$DIR"/force_compiler_filter --compiler-filter "$the_compiler_filter" --package "$the_package" --activity "$the_activity"
"$DIR"/force_compiler_filter --compiler-filter "$the_compiler_filter" --package "$the_package" --activity "$the_activity"
res=$?
else
verbose_print "Queried compiler-filter matched requested compiler-filter, skip forcing."
res=0
fi
return $res
}
# Ensure the APK is currently compiled with whatever we passed in via --compiler-filter.
# No-op if this option was not passed in.
configure_compiler_filter "$compiler_filter" "$package" "$activity" || exit 1
# TODO: This loop logic could probably be moved into app_startup_runner.py
for ((i=0;i<count;++i)) do
verbose_print "=========================================="
verbose_print "==== ITERATION $i ===="
verbose_print "=========================================="
if [[ $mode != "warm" ]]; then
verbose_print "Drop caches for non-warm start."
# Drop all caches to get cold starts.
adb shell "echo 3 > /proc/sys/vm/drop_caches"
fi
perform_aot "$when" "$mode"
verbose_print "Running with timeout $timeout"
# TODO: multiple metrics output.
total_time="$(timeout $timeout $DIR/launch_application "$package" "$activity")"
if [[ $? -ne 0 ]]; then
echo "WARNING: Skip bad result, try iteration again." >&2
((i=i-1))
continue
fi
perform_aot_cleanup "$when" "$mode"
echo "Iteration $i. Total time was: $total_time"
timings_array+=($total_time)
done
# drop the first result which is usually garbage.
timings_array=("${timings_array[@]:1}")
# Print out interactive/debugging timings and averages.
# Other scripts should use the --output flag and parse the CSV.
for tim in "${timings_array[@]}"; do
echo_to_output_file -ne "$tim,"
done
echo_to_output_file ""
average_string=$(echo "${timings_array[@]}" | awk '{s+=$0}END{print "Average:",s/NR}' RS=" ")
echo -ne ${average_string}.
if [[ x$output != x ]]; then
echo " Saved results to '$output'"
fi
# Temporary hack around multiple activities being launched with different package paths (for same app):
# Clean up all left-over TraceFile.pb
adb shell 'for i in $(find /data/app -name TraceFile.pb); do rm \$i; done'
# Kill the process to ensure AM isn't keeping it around.
remote_pkill "$package"
exit 0