blob: 56bffa85eb90e9a0d3d103682cebc1f67f32df40 [file] [log] [blame]
Igor Murashkin25f394d2018-09-11 16:37:18 -07001#!/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)
Igor Murashkinb622e782018-09-13 17:57:34 -070095 compiler_filter="$2"
Igor Murashkin25f394d2018-09-11 16:37:18 -070096 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
Igor Murashkin25f394d2018-09-11 16:37:18 -0700114find_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
Igor Murashkin25f394d2018-09-11 16:37:18 -0700124# 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
Igor Murashkin25f394d2018-09-11 16:37:18 -0700179keep_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
Igor Murashkinb622e782018-09-13 17:57:34 -0700268configure_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
Igor Murashkin25f394d2018-09-11 16:37:18 -0700309# 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