Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 45087dee authored by Igor Murashkin's avatar Igor Murashkin
Browse files

startop: Add script to force dex2oat compilation filter for app

Example: ./force_compiler_filter --package com.google.android.apps.maps --compiler-filter speed-profile

Run the app just slightly enough to fully start up, then force it to
dump the profile and recompile the application with dex2oat under the
speed-profile filter.

(Also supports any other compilation filter such as quicken, speed,
etc).

Subsequently, this command can be used to manually validate that the
compiler filter was indeed changed:

 $ adb shell dumpsys package com.google.android.apps.maps | grep -A10 "Dexopt state"
 Dexopt state:
   [com.google.android.apps.maps]
     path: /data/app/com.google.android.apps.maps-D7s8PLidqqEq7Jc7UH_a5A==/base.apk
       arm64: [status=speed-profile] [reason=unknown]

Test: Manual (see above)
Change-Id: Iea6067f90dc287d1de651d1ab36df69d23b2e9c1
parent 25f394d6
Loading
Loading
Loading
Loading
+173 −0
Original line number Diff line number Diff line
#!/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.

#
# Forces an application APK to be compiled (by ART's dex2oat)
# with a specific compiler filter.
#
# Example usage:
#    $> ./force_compiler_filter -p com.google.android.apps.maps -c speed-profile
#
# (The application may be started/stopped as a side effect)
#

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$DIR/lib/common"

usage() {
    cat <<EOF
Usage: $(basename $0) [OPTION]...

  Required:
    -p, --package               package of the app to recompile
    -c, --compiler-filter       override the compiler filter if set (default none)
                                valid options are listed by: adb shell cmd package, under compile -m

  Optional:
    -a, --activity              activity of the app to recompile
    -h, --help                  usage information (this)
    -v, --verbose               enable extra verbose printing
    -w, --wait_time             how long to wait for app startup (default 10) in seconds
EOF
}

wait_time="10" # seconds

parse_arguments() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      -a|--activity)
        activity="$2"
        shift
        ;;
      -h|--help)
        usage
        exit 0
        ;;
      -p|--package)
        package="$2"
        shift
        ;;
      -w|--wait_time)
        wait_time="$2"
        shift
        ;;
      -c|--compiler-filter)
        compiler_filter="$2"
        shift
        ;;
      -v|--verbose)
        verbose="y"
        ;;
    esac
    shift
  done

  if [[ -z "$compiler_filter" ]]; then
    echo "Missing required --compiler-filter" >&2
    echo ""
    usage
    exit 1
  fi
  if [[ -z "$package" ]]; then
    echo "Missing required --package" >&2
    echo ""
    usage
    exit 1
  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
}

get_activity_name() {
  local package="$1"
  local action_key="android.intent.action.MAIN:"

  local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package")"
  verbose_print $activity_line
  IFS="/" read -a array <<< "$activity_line"
  local activity_name="${array[1]}"
  echo "$activity_name"
  #adb shell am start "$package/$activity_name"
}

remote_pidof() {
  local procname="$1"
  adb shell ps | grep "$procname" | awk '{print $2;}'
}

remote_pkill() {
  local procname="$1"
  shift

  local the_pids=$(remote_pidof "$procname")
  local pid

  for pid in $the_pids; do
    verbose_print adb shell kill "$@" "$pid"
    adb shell kill "$@" "$pid"
  done
}

force_package_compilation() {
  local arg_compiler_filter="$1"
  local arg_package="$2"

  if [[ $arg_compiler_filter == speed-profile ]]; then
    # Force the running app to dump its profiles to disk.
    remote_pkill "$arg_package" -SIGUSR1
    sleep 1 # give some time for above to complete.
  fi

  adb shell cmd package compile -m "$arg_compiler_filter" -f "$arg_package"
}

main() {
  parse_arguments "$@"

  if [[ $compiler_filter == speed-profile ]]; then
    # screen needs to be unlocked in order to run an app
    "$DIR"/unlock_screen

    am_output="$(adb shell am start -S -W "$package"/"$activity")"
    if [[ $? -ne 0 ]]; then
      echo "am start failed" >&2
      exit 1
    fi

    verbose_print "$am_output"
    # give some time for app startup to complete.
    # this is supposed to be an upper bound for measuring startup time.
    sleep "$wait_time"
  fi

  force_package_compilation "$compiler_filter" "$package"

  # kill the application to ensure next time it's started,
  # it picks up the correct compilation filter.
  adb shell am force-stop "$package"
  remote_pkill "$package"
}

main "$@"