Home | History | Annotate | Download | only in app_startup
      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