995c17bdd0
Bug: 134784018 Change-Id: I6a43696cbdc91542fe3683071850192dfc22b759 Test: manual (run with and without fadvise mode)
404 lines
11 KiB
Bash
Executable File
404 lines
11 KiB
Bash
Executable File
#!/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
|