1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
|
#!/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
|