diff options
Diffstat (limited to 'startop/scripts/iorap/collector')
-rwxr-xr-x | startop/scripts/iorap/collector | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/startop/scripts/iorap/collector b/startop/scripts/iorap/collector new file mode 100755 index 000000000000..3dc080a5ac9c --- /dev/null +++ b/startop/scripts/iorap/collector @@ -0,0 +1,403 @@ +#!/bin/bash +# +# Copyright 2017, 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. + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +APP_STARTUP_DIR="$DIR/../app_startup/" +source "$DIR/common" + +usage() { + cat <<EOF +Usage: collector [OPTIONS]... + +Runs an application, causes an iorap trace to be collected for it, and then invokes the iorap +compiler to generate a TraceFile.pb. + + -p, --package package of the app to test + -a, --activity activity of the app to test + -h, --help usage information (this) + -v, --verbose enable extra verbose printing + -i, --inodes path to inodes file (system/extras/pagecache/pagecache.py -d inodes) + -b, --trace_buffer_size how big to make trace buffer size (default 32768) + -w, --wait_time how long to run systrace for (default 10) in seconds + -c, --compiler-filter override the compilation filter if set (default none) + -o, --output output trace file protobuf (default 'TraceFile.pb') +EOF +} + + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +trace_buffer_size=32768 +wait_time=10 +comp_filter="" +output_dest="TraceFile.pb" + +parse_arguments() { + while [[ $# -gt 0 ]]; do + case "$1" in + -a|--activity) + activity="$2" + shift + ;; + -h|--help) + usage + exit 0 + ;; + -p|--package) + package="$2" + shift + ;; + -i|--inodes) + inodes="$2" + shift + ;; + -b|--trace_buffer_size) + trace_buffer_size="$2" + shift + ;; + -w|--wait_time) + wait_time="$2" + shift + ;; + -c|--compiler-filter) + comp_filter="$2" + shift + ;; + -o|--output) + output_dest="$2" + shift + ;; + -v|--verbose) + verbose="y" + ;; + esac + shift + done +} + +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_comp_filter="$1" + local arg_package="$2" + + if [[ $arg_comp_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_comp_filter" -f "$arg_package" +} + +parse_package_dumpsys_line() { + local what_left="$1" + local what_right="$2" + local line="$3" + + if [[ $line == *${what_left}*${what_right}* ]]; then + found="${line#*$what_left}" + found="${found%$what_right*}" + echo "$found" + return 0 + fi + + return 1 +} + +parse_package_dumpsys_section() { + local what_left="$1" + local what_right="$2" + shift + local lines="$@" + + lines="${lines//$'\n'/}" + + local new_lines=() + + local current_line="" + local newline=n + local line + for line in "${lines[@]}"; do + if [[ $line == *: ]]; then + newline=y + current_line="" + new_lines+=("$current_line") + + parse_package_dumpsys_line "$what_left" "$what_right" "$current_line" && return 0 + else + # strip all spaces from the start + line="${line//$' '/}" + current_line+="$line" + #prepend to current line + fi + done + [[ "$current_line" != "" ]] && new_lines+=("$current_line") + + parse_package_dumpsys_line "$what_left" "$what_right" "$current_line" && return 0 + + return 1 +} + +parse_package_compilation() { + local pkg="$1" +# [com.google.android.apps.maps] + + local compilation_filter + local is_prebuilt + local isa + local etc + + local ret_code + + read compilation_filter is_prebuilt isa etc <<< "$("$APP_STARTUP_DIR"/query_compiler_filter.py --package "$pkg")" + ret_code=$? + + if [[ $ret_code -eq 0 && x$compilation_filter != x ]]; then + verbose_print "Package compilation info for $pkg was '$compilation_filter'" + echo "$compilation_filter" + return 0 + else + verbose_print "query failed ret code $ret_code filter=$compilation_filter" + fi + + return $ret_code +} + +# Main entry point +if [[ $# -eq 0 ]]; then + usage + exit 1 +else + parse_arguments "$@" + + # if we do not have have package exit early with an error + [[ "$package" == "" ]] && echo "--package not specified" 1>&2 && exit 1 + + if [[ -z "$inodes" ]] || ! [[ -f $inodes ]]; then + echo "--inodes not specified" 1>&2 + 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 +fi + +adb root > /dev/null + +if [[ "$(adb shell getenforce)" != "Permissive" ]]; then + adb shell setenforce 0 + adb shell stop + adb shell start + adb wait-for-device +fi + +compilation_was="$(parse_package_compilation "$package")" +if [[ $? -ne 0 ]]; then + echo "Could not determine package compilation filter; was this package installed?" >&2 + exit 1 +fi +verbose_print "Package compilation: $compilation_was" + +# Cannot downgrade (e.g. from speed-profile to quicken) without forceful recompilation. +# Forceful recompilation will recompile even if compilation filter was unchanged. +# Therefore avoid recompiling unless the filter is actually different than what we asked for. +if [[ "x$comp_filter" != "x" ]] && [[ "$compilation_was" != "$comp_filter" ]]; then + echo "Current compilation filter is '$compilation_was'; force recompile to '$comp_filter'" >&2 + #TODO: this matching seems hopelessly broken, it will always recompile. + + force_package_compilation "$comp_filter" "$package" +fi + +# Drop all caches prior to beginning a systrace, otherwise we won't record anything already in pagecache. +adb shell "echo 3 > /proc/sys/vm/drop_caches" + +trace_tmp_file="$(mktemp -t trace.XXXXXXXXX.html)" + +function finish { + [[ -f "$trace_tmp_file" ]] && rm "$trace_tmp_file" +} +trap finish EXIT + +launch_application_and_wait_for_trace() { + local package="$1" + local activity="$2" + local timeout=30 # seconds + + # Ensure application isn't running already. + remote_pkill "$package" + + # 5 second trace of Home screen causes + # a trace of the home screen. + # There is no way to abort the trace + # so just wait for it to complete instead. + sleep 30 + + local time_now="$(logcat_save_timestamp)" + local retcode=0 + + verbose_print "Drop caches for non-warm start." + # Drop all caches to get cold starts. + adb shell "echo 3 > /proc/sys/vm/drop_caches" + + verbose_print "now launching application" + # Launch an application + "$APP_STARTUP_DIR"/launch_application "$package" "$activity" + retcode=$? + if [[ $retcode -ne 0 ]]; then + echo "FATAL: Application launch failed." >&2 + return $retcode + fi + + # This blocks until 'am start' returns at which point the application is + # already to be considered "started" as the first frame has been drawn. + + # TODO: check for cold start w.r.t to activitymanager? + + # Wait for application to start from the point of view of ActivityTaskManager. + local pattern="ActivityTaskManager: Displayed $package" + logcat_wait_for_pattern "$timeout" "$time_now" "$pattern" + retcode=$? + if [[ $retcode -ne 0 ]]; then + echo "FATAL: Could not find '$pattern' in logcat." >&2 + return $retcode + fi + + # Wait for iorapd to finish writing out the perfetto traces for this app. + iorapd_perfetto_wait_for_app_trace "$package" "$activity" "$timeout" "$time_now" + retcode=$? + if [[ $retcode -ne 0 ]]; then + echo "FATAL: Could not save perfetto app trace file." >&2 + return $retcode + fi + + verbose_print "iorapd has finished collecting app trace file for $package/$activity" +} + +collector_main() { + # don't even bother trying to run anything until the screen is unlocked. + "$APP_STARTUP_DIR"/unlock_screen + + # Don't mutate state while iorapd is running. + iorapd_stop || return $? + + # Remove all existing metadata for a package/activity in iorapd. + iorapd_perfetto_purge_app_trace "$package" "$activity" || return $? + iorapd_compiler_purge_trace_file "$package" "$activity" || return $? + + iorapd_perfetto_enable || return $? + iorapd_readahead_disable || return $? + iorapd_start || return $? + + # Wait for perfetto trace to finished writing itself out. + launch_application_and_wait_for_trace "$package" "$activity" || return $? + + # Pull the perfetto trace for manual inspection. + iorapd_perfetto_pull_trace_file "$package" "$activity" "perfetto_trace.pb" + + # Compile the trace so that the next app run can use prefetching. + iorapd_compiler_for_app_trace "$package" "$activity" "$inodes" || return $? + + # Save TraceFile.pb to local file. + iorapd_compiler_pull_trace_file "$package" "$activity" "$output_dest" || return $? + # Remove the TraceFile.pb from the device. + iorapd_compiler_purge_trace_file "$package" "$activity" || return $? + + # TODO: better transactional support for restoring iorapd global properties + iorapd_perfetto_disable || return $? +} + +collector_main "$@" + +verbose_print "Collector finished. Children: " +if [[ $verbose == y ]]; then + jobs -p + ps f -g$$ +fi + +exit $? + + +verbose_print "About to begin systrace" +coproc systrace_fd { + # Disable stdout buffering since we need to know the output of systrace RIGHT AWAY. + stdbuf -oL "$ANDROID_BUILD_TOP"/external/chromium-trace/systrace.py --target=android -b "$trace_buffer_size" -t "$wait_time" am pagecache dalvik -o "$trace_tmp_file" +} + +verbose_print "Systrace began" + +systrace_pid="$!" + +while read -r -u "${systrace_fd[0]}" systrace_output; do + verbose_print "$systrace_output" + if [[ "$systrace_output" == *"Starting tracing"* ]]; then + verbose_print "WE DID SEE STARTING TRACING." + break + fi +done +# Systrace has begun recording the tracing. +# Run the application and collect the results. + +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" +total_time="$(echo "$am_output" | grep 'TotalTime:' | sed 's/TotalTime: //g')" +verbose_print "total time: $total_time" + +# Now wait for systrace to finish. + +wait "$systrace_pid" || { echo "Systrace finished before am start was finished, try a longer --wait_time"; exit 1; } +verbose_print "Systrace has now finished" +verbose_print "$(ls -la "$trace_tmp_file")" + + +iorapd_perfetto_disable + +# Now that systrace has finished, convert the trace file html file to a protobuf. + +"$ANDROID_BUILD_TOP"/system/iorap/src/py/collector/trace_parser.py -i "$inodes" -t "$trace_tmp_file" -o "$output_dest" || exit 1 + +echo "Trace file collection complete, trace file saved to \"$output_dest\"!" >&2 + +finish |