summaryrefslogtreecommitdiff
path: root/startop/scripts/iorap/collector
diff options
context:
space:
mode:
Diffstat (limited to 'startop/scripts/iorap/collector')
-rwxr-xr-xstartop/scripts/iorap/collector403
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