• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
17usage() {
18    cat <<EOF
19Usage: 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)
32EOF
33}
34
35DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
36source "$DIR/lib/common"
37
38needs_trace_file="n"
39input_file=""
40package=""
41mode='warm'
42count=2
43sleep_time=2
44timeout=10
45output="" # stdout by default
46when="aot"
47parse_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
106echo_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
114find_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
125if [[ $# -eq 0 ]]; then
126  usage
127  exit 1
128else
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
151fi
152
153adb root > /dev/null
154
155if [[ ($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
161fi
162
163# TODO: set performance governor etc, preferrably only once
164# before every single app run.
165
166# Kill everything before running.
167remote_pkill "$package"
168sleep 1
169
170timings_array=()
171
172package_path="$(find_package_path "$package")"
173if [[ $? -ne 0 ]]; then
174  echo "Failed to detect package path for '$package'" >&2
175  exit 1
176fi
177verbose_print "Package was in path '$package_path'"
178
179keep_application_trace_file=n
180application_trace_file_path="$package_path/TraceFile.pb"
181trace_file_directory="$package_path"
182if [[ $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
195fi
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.
200if [[ $keep_application_trace_file == "n" ]]; then
201  adb shell "[[ -f '$application_trace_file_path' ]] && rm '$application_trace_file_path'"
202fi
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.
208perform_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
245perform_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
268configure_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.
307configure_compiler_filter "$compiler_filter" "$package" "$activity" || exit 1
308
309# TODO: This loop logic could probably be moved into app_startup_runner.py
310for ((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)
338done
339
340# drop the first result which is usually garbage.
341timings_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.
346for tim in "${timings_array[@]}"; do
347  echo_to_output_file -ne "$tim,"
348done
349echo_to_output_file ""
350
351average_string=$(echo "${timings_array[@]}" | awk '{s+=$0}END{print "Average:",s/NR}' RS=" ")
352echo -ne ${average_string}.
353if [[ x$output != x ]]; then
354  echo " Saved results to '$output'"
355fi
356
357# Temporary hack around multiple activities being launched with different package paths (for same app):
358# Clean up all left-over TraceFile.pb
359adb 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.
362remote_pkill "$package"
363
364exit 0
365