1 #!/bin/bash 2 # 3 # Copyright 2018, The Android Open Source Project 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 usage() { 18 cat <<EOF 19 Usage: run_app_with_prefetch --package <name> [OPTIONS]... 20 21 -p, --package <name> package of the app to test 22 -a, --activity <name> activity to use 23 -h, --help usage information (this) 24 -v, --verbose enable extra verbose printing 25 -i, --input <file> trace file protobuf (default 'TraceFile.pb') 26 -r, --readahead <mode> cold, warm, fadvise, mlock (default 'warm') 27 -w, --when <when> aot or jit (default 'aot') 28 -c, --count <count> how many times to run (default 1) 29 -s, --sleep <sec> how long to sleep after readahead 30 -t, --timeout <sec> how many seconds to timeout in between each app run (default 10) 31 -o, --output <file.csv> what file to write the performance results into as csv (default stdout) 32 EOF 33 } 34 35 DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 36 source "$DIR/lib/common" 37 38 needs_trace_file="n" 39 input_file="" 40 package="" 41 mode='warm' 42 count=2 43 sleep_time=2 44 timeout=10 45 output="" # stdout by default 46 when="aot" 47 parse_arguments() { 48 while [[ $# -gt 0 ]]; do 49 case "$1" in 50 -h|--help) 51 usage 52 exit 0 53 ;; 54 -p|--package) 55 package="$2" 56 shift 57 ;; 58 -a|--activity) 59 activity="$2" 60 shift 61 ;; 62 -i|--input) 63 input_file="$2" 64 shift 65 ;; 66 -v|--verbose) 67 export verbose="y" 68 ;; 69 -r|--readahead) 70 mode="$2" 71 shift 72 ;; 73 -c|--count) 74 count="$2" 75 ((count+=1)) 76 shift 77 ;; 78 -s|--sleep) 79 sleep_time="$2" 80 shift 81 ;; 82 -t|--timeout) 83 timeout="$2" 84 shift 85 ;; 86 -o|--output) 87 output="$2" 88 shift 89 ;; 90 -w|--when) 91 when="$2" 92 shift 93 ;; 94 --compiler-filter) 95 compiler_filter="$2" 96 shift 97 ;; 98 *) 99 echo "Invalid argument: $1" >&2 100 exit 1 101 esac 102 shift 103 done 104 } 105 106 echo_to_output_file() { 107 if [[ "x$output" != x ]]; then 108 echo "$@" >> $output 109 fi 110 # Always echo to stdout as well. 111 echo "$@" 112 } 113 114 find_package_path() { 115 local pkg="$1" 116 117 res="$(adb shell find "/data/app/$pkg"-'*' -maxdepth 0 2> /dev/null)" 118 if [[ -z $res ]]; then 119 res="$(adb shell find "/system/app/$pkg"-'*' -maxdepth 0 2> /dev/null)" 120 fi 121 echo "$res" 122 } 123 124 # Main entry point 125 if [[ $# -eq 0 ]]; then 126 usage 127 exit 1 128 else 129 parse_arguments "$@" 130 131 # if we do not have have package exit early with an error 132 [[ "$package" == "" ]] && echo "--package not specified" 1>&2 && exit 1 133 134 if [[ $mode != "cold" && $mode != "warm" ]]; then 135 needs_trace_file="y" 136 if [[ -z "$input_file" ]] || ! [[ -f $input_file ]]; then 137 echo "--input not specified" 1>&2 138 exit 1 139 fi 140 fi 141 142 if [[ "$activity" == "" ]]; then 143 activity="$(get_activity_name "$package")" 144 if [[ "$activity" == "" ]]; then 145 echo "Activity name could not be found, invalid package name?" 1>&2 146 exit 1 147 else 148 verbose_print "Activity name inferred: " "$activity" 149 fi 150 fi 151 fi 152 153 adb root > /dev/null 154 155 if [[ ($when == jit) || ($when == aot) ]] && [[ "$(adb shell getenforce)" != "Permissive" ]]; then 156 echo "Disable selinux permissions and restart framework." 157 adb shell setenforce 0 158 adb shell stop 159 adb shell start 160 adb wait-for-device 161 fi 162 163 # TODO: set performance governor etc, preferrably only once 164 # before every single app run. 165 166 # Kill everything before running. 167 remote_pkill "$package" 168 sleep 1 169 170 timings_array=() 171 172 package_path="$(find_package_path "$package")" 173 if [[ $? -ne 0 ]]; then 174 echo "Failed to detect package path for '$package'" >&2 175 exit 1 176 fi 177 verbose_print "Package was in path '$package_path'" 178 179 keep_application_trace_file=n 180 application_trace_file_path="$package_path/TraceFile.pb" 181 trace_file_directory="$package_path" 182 if [[ $needs_trace_file == y ]]; then 183 # system server always passes down the package path in a hardcoded spot. 184 if [[ $when == "jit" ]]; then 185 verbose_print adb push "$input_file" "$application_trace_file_path" 186 adb push "$input_file" "$application_trace_file_path" 187 keep_application_trace_file="y" 188 else 189 # otherwise use a temporary directory to get normal non-jit behavior. 190 trace_file_directory="/data/local/tmp/prefetch/$package" 191 adb shell mkdir -p "$trace_file_directory" 192 verbose_print adb push "$input_file" "$trace_file_directory/TraceFile.pb" 193 adb push "$input_file" "$trace_file_directory/TraceFile.pb" 194 fi 195 fi 196 197 # Everything other than JIT: remove the trace file, 198 # otherwise system server activity hints will kick in 199 # and the new just-in-time app pre-warmup will happen. 200 if [[ $keep_application_trace_file == "n" ]]; then 201 adb shell "[[ -f '$application_trace_file_path' ]] && rm '$application_trace_file_path'" 202 fi 203 204 # Perform AOT readahead/pinning/etc when an application is about to be launched. 205 # For JIT readahead, we allow the system to handle it itself (this is a no-op). 206 # 207 # For warm, cold, etc modes which don't need readahead this is always a no-op. 208 perform_aot() { 209 local the_when="$1" # user: aot, jit 210 local the_mode="$2" # warm, cold, fadvise, mlock, etc. 211 212 if [[ $the_when != "aot" ]]; then 213 # TODO: just in time implementation.. should probably use system server. 214 return 0 215 fi 216 217 # any non-warm/non-cold modes should use the iorap-activity-hint wrapper script. 218 if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then 219 220 # TODO: add activity_hint_sender.exp 221 verbose_print "starting with package=$package package_path=$trace_file_directory" 222 coproc hint_sender_fd { $ANDROID_BUILD_TOP/system/iorap/src/sh/activity_hint_sender.exp "$package" "$trace_file_directory" "$the_mode"; } 223 hint_sender_pid=$! 224 verbose_print "Activity hint sender began" 225 226 notification_success="n" 227 while read -r -u "${hint_sender_fd[0]}" hint_sender_output; do 228 verbose_print "$hint_sender_output" 229 if [[ "$hint_sender_output" == "Press any key to send completed event..."* ]]; then 230 verbose_print "WE DID SEE NOTIFICATION SUCCESS." 231 notification_success='y' 232 # Give it some time to actually perform the readaheads. 233 sleep $sleep_time 234 break 235 fi 236 done 237 238 if [[ $notification_success == 'n' ]]; then 239 echo "[FATAL] Activity hint notification failed." 1>&2 240 exit 1 241 fi 242 fi 243 } 244 245 perform_aot_cleanup() { 246 local the_when="$1" # user: aot, jit 247 local the_mode="$2" # warm, cold, fadvise, mlock, etc. 248 249 if [[ $the_when != "aot" ]]; then 250 # TODO: just in time implementation.. should probably use system server. 251 return 0 252 fi 253 254 # any non-warm/non-cold modes should use the iorap-activity-hint wrapper script. 255 if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then 256 # Clean up the hint sender by telling it that the launch was completed, 257 # and to shutdown the watcher. 258 echo "Done\n" >&"${hint_sender_fd[1]}" 259 260 while read -r -u "${hint_sender_fd[0]}" hint_sender_output; do 261 verbose_print "$hint_sender_output" 262 done 263 264 wait $hint_sender_pid 265 fi 266 } 267 268 configure_compiler_filter() { 269 local the_compiler_filter="$1" 270 local the_package="$2" 271 local the_activity="$3" 272 273 if [[ -z $the_compiler_filter ]]; then 274 verbose_print "No --compiler-filter specified, don't need to force it." 275 return 0 276 fi 277 278 local current_compiler_filter_info="$("$DIR"/query_compiler_filter.py --package "$the_package")" 279 local res=$? 280 if [[ $res -ne 0 ]]; then 281 return $res 282 fi 283 284 local current_compiler_filter 285 local current_reason 286 local current_isa 287 read current_compiler_filter current_reason current_isa <<< "$current_compiler_filter_info" 288 289 verbose_print "Compiler Filter="$current_compiler_filter "Reason="$current_reason "Isa="$current_isa 290 291 # Don't trust reasons that aren't 'unknown' because that means we didn't manually force the compilation filter. 292 # (e.g. if any automatic system-triggered compilations are not unknown). 293 if [[ $current_reason != "unknown" ]] || [[ $current_compiler_filter != $the_compiler_filter ]]; then 294 verbose_print "$DIR"/force_compiler_filter --compiler-filter "$the_compiler_filter" --package "$the_package" --activity "$the_activity" 295 "$DIR"/force_compiler_filter --compiler-filter "$the_compiler_filter" --package "$the_package" --activity "$the_activity" 296 res=$? 297 else 298 verbose_print "Queried compiler-filter matched requested compiler-filter, skip forcing." 299 res=0 300 fi 301 302 return $res 303 } 304 305 # Ensure the APK is currently compiled with whatever we passed in via --compiler-filter. 306 # No-op if this option was not passed in. 307 configure_compiler_filter "$compiler_filter" "$package" "$activity" || exit 1 308 309 # TODO: This loop logic could probably be moved into app_startup_runner.py 310 for ((i=0;i<count;++i)) do 311 verbose_print "==========================================" 312 verbose_print "==== ITERATION $i ====" 313 verbose_print "==========================================" 314 if [[ $mode != "warm" ]]; then 315 verbose_print "Drop caches for non-warm start." 316 # Drop all caches to get cold starts. 317 adb shell "echo 3 > /proc/sys/vm/drop_caches" 318 fi 319 320 perform_aot "$when" "$mode" 321 322 verbose_print "Running with timeout $timeout" 323 324 # TODO: multiple metrics output. 325 total_time="$(timeout $timeout $DIR/launch_application "$package" "$activity")" 326 327 if [[ $? -ne 0 ]]; then 328 echo "WARNING: Skip bad result, try iteration again." >&2 329 ((i=i-1)) 330 continue 331 fi 332 333 perform_aot_cleanup "$when" "$mode" 334 335 echo "Iteration $i. Total time was: $total_time" 336 337 timings_array+=($total_time) 338 done 339 340 # drop the first result which is usually garbage. 341 timings_array=("${timings_array[@]:1}") 342 343 344 # Print out interactive/debugging timings and averages. 345 # Other scripts should use the --output flag and parse the CSV. 346 for tim in "${timings_array[@]}"; do 347 echo_to_output_file -ne "$tim," 348 done 349 echo_to_output_file "" 350 351 average_string=$(echo "${timings_array[@]}" | awk '{s+=$0}END{print "Average:",s/NR}' RS=" ") 352 echo -ne ${average_string}. 353 if [[ x$output != x ]]; then 354 echo " Saved results to '$output'" 355 fi 356 357 # Temporary hack around multiple activities being launched with different package paths (for same app): 358 # Clean up all left-over TraceFile.pb 359 adb shell 'for i in $(find /data/app -name TraceFile.pb); do rm \$i; done' 360 361 # Kill the process to ensure AM isn't keeping it around. 362 remote_pkill "$package" 363 364 exit 0 365