blob: e27b12028d8254e309f13224390040e8d7231e27 [file] [log] [blame]
#!/bin/bash
# Copyright 2021 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
print_prereqs_and_exit () {
echo "error: $1"
echo """
WORKSTATION SETUP
- record_cros_trace in PATH (src/platform/graphics/src/perfetto/)
- vperfetto_merge in PATH (build https://github.com/batesj/vperfetto)
- in ~/.ssh/config, cache connections for both host and guest, like:
ControlM*ster auto
ControlPersist yes
ControlPath ~/.ssh/connection-%h-%p-%r
HOST AND GUEST SETUP
- perCetto library installed (https://github.com/olvaffe/percetto)
- perfetto installed (perfetto tool must be in PATH).
- code instrumented with frequent* perCetto trace events.
- traced service running.
- [for ARCVM] developer mode and adb enabled.
- only up to 2 VMs can run simultaneously, one of them being ARCVM.
* There must be frequent events on both host and guest for merging to
work. The incremental update in perCetto will contain this time sync data.
That defaults to 1 second intervals. Pass -i <ms> to change that period for
traces that are less than 2 seconds long. Guest sommelier now has its own
perfetto time sync events tied to the guest app's frame submits, so if
the trace includes multiple guest app frames, there will be enough data to
synchronize."""
exit 1
}
if [[ "$1" = "--help" ]] || [[ -z "$1" ]] || [[ -z "$2" ]]; then
echo "syntax: $0 <host-ssh-ip> <guest-ssh-ip> [args for record_cros_trace]..."
echo "example: $0 hostip guestip -t 5000 virgl virgls"
echo "example: $0 hostip hostip-arcvm virgl virgls gfx wm view"
echo " Without the -t option, you will need to Ctrl+C on both sides"
exit 1
fi
verbose=false
vperfetto_merge=vperfetto_merge
record_cros_trace=record_cros_trace
command -v "${vperfetto_merge}" > /dev/null 2>&1 || \
print_prereqs_and_exit "missing ${vperfetto_merge}"
command -v "${record_cros_trace}" > /dev/null 2>&1 || \
print_prereqs_and_exit "missing ${record_cros_trace}"
host_file=/tmp/trace-host
guest_file=/tmp/trace-guest
rm -f "${host_file}" "${guest_file}"
host=$1
guest=$2
# Determine what tsc-offset to use. Debugfs has files for each VM.
# This script supports at most two VMs running: ARCVM + one other.
is_arcvm=false
if [[ "${guest}" == *-arcvm ]]; then
is_arcvm=true
fi
sync_app="com.google.perfettoguesttimesync"
if [[ "${is_arcvm}" == true ]]; then
found=$(ssh -q "${host}" "adb shell pm list packages | grep ${sync_app}")
vperfetto_dir=$(dirname $(which "${vperfetto_merge}"))
apk_root="${vperfetto_dir}/../utils/arcvm-time-sync-app"
install=false
if [[ -z "${found}" ]]; then
install=true
else
# Check if installed version is older than latest available.
latest_version=$(sed -n 's/.*versionCode\"\: \([0-9]*\)\,$/\1/p' < \
"${apk_root}/app/release/output-metadata.json")
install_version=$(ssh -q ${host} adb shell dumpsys package ${sync_app} | \
grep versionCode | sed -n 's/.*versionCode\=\([0-9]*\) .*/\1/p')
if [[ ( ${install_version} < ${latest_version} ) ]]; then
echo "upgrading ${sync_app} from ${install_version} to ${latest_version}"
install=true
fi
fi
if [[ "${install}" == true ]]; then
echo "attempting to install ${sync_app} from vperfetto..."
apk="${apk_root}/app/release/app-release.apk"
scp "${apk}" "${host}":/tmp/app.apk \
|| print_prereqs_and_exit "error: could not find ${apk}"
ssh -q "${host}" "adb install -r /tmp/app.apk" || exit 1
fi
# Start the time trace service.
ssh -q "${host}" "adb shell \
am start-foreground-service ${sync_app}/${sync_app}.TimeTrace" \
> /dev/null
trap "ssh -q ${host} adb shell am force-stop ${sync_app}" EXIT
fi
find_tsc_offsets="find /sys/kernel/debug/ -name 'tsc-offset'"
vm_pids=$(ssh -q "${host}" "${find_tsc_offsets}" \
| sed -r 's/.*kvm\/([0-9]*).*/\1/g' | sort | uniq)
if [[ $(echo "{vm_pids}" | wc -w) -gt "2" ]]; then
print_prereqs_and_exit "error: more than 2 VMs are running"
fi
found_guest_pid=''
for pid in ${vm_pids}
do
cmd_arcvm=$(ssh -q "${host}" "cat /proc/${pid}/cmdline" \
| grep arcvm > /dev/null && echo arcvm)
if [[ "${is_arcvm}" == true ]]; then
if [[ "${cmd_arcvm}" == "arcvm" ]]; then
found_guest_pid="${pid}"
fi
else
if [[ ! "${cmd_arcvm}" == "arcvm" ]]; then
found_guest_pid="${pid}"
fi
fi
done
if [[ -z "${found_guest_pid}" ]] && [[ "${is_arcvm}" == true ]]; then
# ARC++ is detected.
tsc_offset=0
echo "warning: ARC++ perfetto may not support --txt config option"
else
if [[ -z "${found_guest_pid}" ]]; then
echo "error: guest tsc offset not found -- is guest running?"
exit 1
fi
# Make sure the tsc offsets are all the same for each vcpu.
tsc_offset=$(ssh -q "${host}" "${find_tsc_offsets} \
| grep ${found_guest_pid}- | xargs cat" | uniq)
if [[ ! $(echo "${tsc_offset}" | wc -w) == "1" ]]; then
echo "error: guest tsc offsets are inconsistent; file a bug on this script"
exit 1
fi
fi
[[ "${verbose}" == true ]] && echo "guest tsc-offset is ${tsc_offset}"
args_file="/tmp/trace-args.txt"
"${record_cros_trace}" --ssh "${host}" -o "${host_file}" "${@:3}" \
--write-args "${args_file}" &
pids="$!"
"${record_cros_trace}" --ssh "${guest}" -o "${guest_file}" "${@:3}" &
pids+=" $!"
trap "echo ; kill -s SIGUSR1 ${pids}" SIGINT
# First wait may get canceled by the SIGINT.
wait
# Second wait waits for background processes to finish.
# Don't allow SIGINT while waiting to avoid accidental interrupt.
trap "" SIGINT
wait
trap - SIGINT
now=$(date +"%Y-%m-%d_%H-%M-%S")
outfile="${host}-${guest}-perfetto-trace-$(cat ${args_file})-${now}"
rm -f "${args_file}"
"${vperfetto_merge}" "${guest_file}" "${host_file}" "${outfile}" \
--merge-guest-into-host --guest-tsc-offset "${tsc_offset}" && \
ls -hs "./${outfile}"
# merge debugging:
#d=$(pwd)
#cd path/to/perfetto
#out/linux/trace_to_text text "${guest_file}" "${d}/trace-guest.txt"
#out/linux/trace_to_text text "${host_file}" "${d}/trace-host.txt"
#out/linux/trace_to_text text "${d}/${outfile}" "${d}/${outfile}.txt"
#cd -