|  | #!/bin/bash | 
|  | # Copyright (C) 2018 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. | 
|  | set -e | 
|  | SCRIPT_DIR="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" | 
|  |  | 
|  | if [ "$TMPDIR" == "" ]; then | 
|  | TMPDIR=/tmp | 
|  | fi | 
|  |  | 
|  | function get_gn_value() { | 
|  | local out=$1 | 
|  | local key=$2 | 
|  | "$SCRIPT_DIR/gn" args "$out" --list=$key --short | awk '{print $3}' \ | 
|  | | tr -d '"' | 
|  | } | 
|  |  | 
|  | function is_monolithic() { | 
|  | local out=$1 | 
|  | value=$(get_gn_value "$out" "monolithic_binaries") | 
|  | test "$value" == "true" | 
|  | } | 
|  |  | 
|  | function is_android() { | 
|  | local out=$1 | 
|  | value=$(get_gn_value "$out" "target_os") | 
|  | test "$value" == "android" | 
|  | } | 
|  |  | 
|  | function is_ssh_target() { | 
|  | [[ -n "$SSH_TARGET" ]] | 
|  | } | 
|  |  | 
|  | function is_mac() { | 
|  | # shellcheck disable=2251 | 
|  | ! test -d /proc | 
|  | return $? | 
|  | } | 
|  |  | 
|  | function tmux_ensure_bash() { | 
|  | if [[ $SHELL == *"fish" ]]; then | 
|  | tmux send-keys "bash" Enter | 
|  | fi | 
|  | } | 
|  |  | 
|  | function reset_tracing() { | 
|  | if is_android "$OUT"; then | 
|  | # Newer versions of Android don't have debugfs mounted at all | 
|  | # anymore so use /sys/kernel/tracing if /d/tracing doesn't exist | 
|  | adb shell 'test -d /sys/kernel/tracing && echo 0 > /sys/kernel/tracing/tracing_on || echo 0 > /sys/kernel/debug/tracing/tracing_on' | 
|  | elif ! is_mac; then | 
|  | # shellcheck disable=SC2016 | 
|  | local script=' | 
|  | if [ ! -w /sys/kernel/debug ]; then | 
|  | echo "debugfs not accessible, try sudo chown -R $USER /sys/kernel/debug" | 
|  | sudo chown -R "$USER" /sys/kernel/debug | 
|  | fi | 
|  |  | 
|  | echo 0 > /sys/kernel/debug/tracing/tracing_on | 
|  | ' | 
|  |  | 
|  | if is_ssh_target; then | 
|  | # shellcheck disable=SC2029 | 
|  | ssh -t "$SSH_TARGET" "sh -c '$script'" | 
|  | else | 
|  | sh -c "$script" | 
|  | fi | 
|  | fi | 
|  | } | 
|  |  | 
|  | function adb_supports_push_sync() { | 
|  | adb --help 2>&1 | grep 'push.*\[--sync\]' >/dev/null 2>&1 | 
|  | } | 
|  |  | 
|  | function push() { | 
|  | if is_android "$OUT"; then | 
|  | local maybe_sync='' | 
|  | if adb_supports_push_sync; then | 
|  | maybe_sync='--sync' | 
|  | fi | 
|  | echo adb push $maybe_sync "$1" "$DIR" | 
|  | adb push $maybe_sync "$1" "$DIR" | 
|  | elif is_ssh_target; then | 
|  | echo scp "$1" "$SSH_TARGET:$DIR" | 
|  | scp "$1" "$SSH_TARGET:$DIR" | 
|  | else | 
|  | echo cp "$1" "$DIR" | 
|  | cp "$1" "$DIR" | 
|  | fi | 
|  | } | 
|  |  | 
|  | function pull() { | 
|  | if is_android "$OUT"; then | 
|  | echo adb pull "$DIR/$1" "$2" | 
|  | adb pull "$DIR/$1" "$2" | 
|  | elif is_ssh_target; then | 
|  | echo scp "$SSH_TARGET:$DIR/$1" "$2" | 
|  | scp "$SSH_TARGET:$DIR/$1" "$2" | 
|  | else | 
|  | echo mv "$DIR/$1" "$2" | 
|  | mv "$DIR/$1" "$2" | 
|  | fi | 
|  | } | 
|  |  | 
|  | BACKGROUND=0 | 
|  | SKIP_CONVERTERS=0 | 
|  | TMUX_LAYOUT="even-vertical" | 
|  | CPU_MASK="" | 
|  |  | 
|  | while getopts "bl:nt:c:C:z:s:" o; do | 
|  | case "$o" in | 
|  | b) BACKGROUND=1 ;; | 
|  | l) TMUX_LAYOUT=${OPTARG} ;; | 
|  | n) SKIP_CONVERTERS=1 ;; | 
|  | t) SSH_TARGET=${OPTARG} ;; | 
|  | c) CONFIG=${OPTARG} ;; | 
|  | C) OUT=${OPTARG} ;; | 
|  | z) CPU_MASK=${OPTARG} ;; | 
|  | s) SCRIPT=${OPTARG} ;; | 
|  | *) | 
|  | echo >&2 "Invalid option $o" | 
|  | exit | 
|  | ;; | 
|  | esac | 
|  | done | 
|  |  | 
|  | # Allow out to be passed as argument | 
|  | shift $((OPTIND - 1)) | 
|  | OUT="${OUT:-$1}" | 
|  |  | 
|  | # Provide useful usage information | 
|  | if [ -z "$OUT" ]; then | 
|  | echo "Usage: $0 [OPTION]... [OUTPUT]" | 
|  | echo "" | 
|  | echo "Options:" | 
|  | echo "  -b          run in the background" | 
|  | echo "  -l LAYOUT   tmux pane layout" | 
|  | echo "  -n          skip post-trace convertors" | 
|  | echo "  -t TARGET   SSH device target" | 
|  | echo "  -c CONFIG   trace configuration file" | 
|  | echo "  -C OUTPUT   output directory" | 
|  | echo "  -z MASK     constrain binaries to given cpu mask (taskset syntax)" | 
|  | echo "  -s SCRIPT   a script to put into a tmux pane" | 
|  | echo "" | 
|  | echo "Environment variables:" | 
|  | echo "  SSH_TARGET  SSH device target" | 
|  | echo "  CONFIG      trace configuration file" | 
|  | echo "  OUT         output directory" | 
|  | exit 1 | 
|  | fi | 
|  |  | 
|  | # Warn about invalid output directories | 
|  | if [ ! -f "$OUT/args.gn" ]; then | 
|  | echo >&2 "OUT=$OUT doesn't look like an output directory." | 
|  | echo >&2 "Please specify a directory by doing:" | 
|  | echo >&2 "  export OUT=out/xxx $0" | 
|  | exit 1 | 
|  | fi | 
|  |  | 
|  | # Check SSH target is valid | 
|  | if is_ssh_target && ! ssh -q "$SSH_TARGET" exit; then | 
|  | echo >&2 "SSH_TARGET=$SSH_TARGET doesn't look like a valid SSH target." | 
|  | echo >&2 "Please specify a SSH cross-compilation target by doing:" | 
|  | echo >&2 "  export SSH_TARGET=<user>@<host> $0" | 
|  | exit 1 | 
|  | fi | 
|  |  | 
|  | if ! builtin type -P tmux &>/dev/null; then | 
|  | echo >&2 "tmux not found" | 
|  | exit 1 | 
|  | fi | 
|  |  | 
|  | # You can set the config to one of the files under test/configs e.g. | 
|  | # CONFIG=ftrace.cfg or to :test. Defaults to :test. | 
|  | CONFIG="${CONFIG:-:test}" | 
|  |  | 
|  | if is_android "$OUT"; then | 
|  | DIR=/data/local/tmp | 
|  | elif is_ssh_target; then | 
|  | DIR=$(ssh "$SSH_TARGET" mktemp -d $TMPDIR/perfetto.XXXXXX) | 
|  | elif is_mac; then | 
|  | DIR=$(mktemp -d $TMPDIR/perfetto.XXXXXX) | 
|  | else | 
|  | DIR=$(mktemp -p $TMPDIR -d perfetto.XXXXXX) | 
|  | fi | 
|  |  | 
|  | if is_android "$OUT"; then | 
|  | TRACECONV="gcc_like_host/traceconv" | 
|  | else | 
|  | TRACECONV="traceconv" | 
|  | fi | 
|  |  | 
|  |  | 
|  | # (re)build the binaries | 
|  | BUILD_TARGETS=(traced traced_probes perfetto test/configs) | 
|  | if [[ SKIP_CONVERTERS -eq 0 ]]; then | 
|  | BUILD_TARGETS+=($TRACECONV) | 
|  | fi | 
|  |  | 
|  | "$SCRIPT_DIR/ninja" -C "$OUT" ${BUILD_TARGETS[*]} | 
|  |  | 
|  | push "$OUT/traced" | 
|  | push "$OUT/traced_probes" | 
|  | push "$OUT/perfetto" | 
|  | reset_tracing | 
|  |  | 
|  | if is_android "$OUT"; then | 
|  | PREFIX="PERFETTO_CONSUMER_SOCK_NAME=@perfetto_test_consumer PERFETTO_PRODUCER_SOCK_NAME=@perfetto_test_producer" | 
|  | else | 
|  | PREFIX="" | 
|  | fi | 
|  |  | 
|  | if ! is_monolithic "$OUT"; then | 
|  | PREFIX="$PREFIX LD_LIBRARY_PATH=$DIR" | 
|  | push "$OUT/libperfetto.so" | 
|  | fi | 
|  |  | 
|  | CONFIG_DEVICE_PATH="$CONFIG" | 
|  | CMD_OPTS="" | 
|  | # Shorthand for using serialized test configs. | 
|  | if [[ "$CONFIG" == *.protobuf ]]; then | 
|  | CONFIG_DEVICE_PATH="$CONFIG" | 
|  | CONFIG_PATH=$OUT/$CONFIG | 
|  | if [[ ! -f $CONFIG_PATH ]]; then | 
|  | echo >&2 "Config \"$CONFIG_PATH\" not known." | 
|  | exit 1 | 
|  | fi | 
|  | push "$CONFIG_PATH" | 
|  | elif [[ "$CONFIG" != ":test" ]]; then | 
|  | CONFIG_DEVICE_PATH="$(basename "$CONFIG")" | 
|  | CONFIG_PATH=$CONFIG | 
|  | # If path isn't valid, assume it's a name of a test config. | 
|  | if [[ ! -f $CONFIG_PATH ]]; then | 
|  | CONFIG_PATH=test/configs/$CONFIG | 
|  | if [[ ! -f $CONFIG_PATH ]]; then | 
|  | echo >&2 "Config \"$CONFIG\" not known." | 
|  | exit 1 | 
|  | fi | 
|  | fi | 
|  | CMD_OPTS="--txt $CMD_OPTS" | 
|  | push "$CONFIG_PATH" | 
|  | fi | 
|  |  | 
|  | if [[ -f "$SCRIPT" ]]; then | 
|  | push "$SCRIPT" | 
|  | fi | 
|  |  | 
|  | POSTFIX="" | 
|  |  | 
|  | if [[ -n "$CPU_MASK" ]]; then | 
|  | PREFIX="$PREFIX taskset $CPU_MASK" | 
|  | fi | 
|  |  | 
|  | if [[ BACKGROUND -eq 1 ]]; then | 
|  | PREFIX="$PREFIX nohup" | 
|  | POSTFIX=" &> /dev/null &" | 
|  | fi | 
|  |  | 
|  | if tmux has-session -t demo; then | 
|  | tmux kill-session -t demo | 
|  | fi | 
|  | tmux -2 new-session -d -s demo | 
|  |  | 
|  | if [ ! -z "$ANDROID_ADB_SERVER_PORT" ]; then | 
|  | tmux set-environment -t demo ANDROID_ADB_SERVER_PORT $ANDROID_ADB_SERVER_PORT | 
|  | fi | 
|  |  | 
|  | if tmux -V | awk '{split($2, ver, "."); if (ver[1] < 2) exit 1 ; else if (ver[1] == 2 && ver[2] < 1) exit 1 }'; then | 
|  | tmux set-option -g mouse on | 
|  | else | 
|  | tmux set-option -g mode-mouse on | 
|  | tmux set-option -g mouse-resize-pane on | 
|  | tmux set-option -g mouse-select-pane on | 
|  | tmux set-option -g mouse-select-window on | 
|  | fi | 
|  |  | 
|  | tmux split-window -v | 
|  | tmux split-window -v | 
|  |  | 
|  | if [[ -n "$SCRIPT" ]]; then | 
|  | tmux split-window -v | 
|  | fi | 
|  |  | 
|  | tmux select-layout "${TMUX_LAYOUT}" | 
|  |  | 
|  | tmux select-pane -t 0 | 
|  | tmux send-keys "clear" C-m | 
|  | if is_android "$OUT"; then | 
|  | tmux send-keys "adb shell" C-m | 
|  | fi | 
|  |  | 
|  | tmux select-pane -t 1 | 
|  | tmux send-keys "clear" C-m | 
|  | if is_android "$OUT"; then | 
|  | tmux send-keys "adb shell" C-m | 
|  | fi | 
|  |  | 
|  | tmux select-pane -t 2 | 
|  | tmux send-keys "clear" C-m | 
|  | if is_android "$OUT"; then | 
|  | tmux send-keys "adb shell" C-m | 
|  | fi | 
|  |  | 
|  | if [[ -n "$SCRIPT" ]]; then | 
|  | tmux select-pane -t 3 | 
|  | tmux send-keys "clear" C-m | 
|  | if is_android "$OUT"; then | 
|  | tmux send-keys "adb shell" C-m | 
|  | fi | 
|  | fi | 
|  |  | 
|  | sleep 2 | 
|  |  | 
|  | tmux select-pane -t 1 | 
|  | if is_ssh_target; then | 
|  | tmux send-keys "ssh $SSH_TARGET" Enter | 
|  | fi | 
|  | tmux_ensure_bash | 
|  | tmux send-keys "PS1='[traced]$ '" Enter | 
|  | tmux send-keys "cd $DIR" Enter | 
|  | tmux send-keys "clear" C-m | 
|  | tmux send-keys "$PREFIX ./traced $POSTFIX" Enter | 
|  |  | 
|  | tmux select-pane -t 0 | 
|  | if is_ssh_target; then | 
|  | tmux send-keys "ssh $SSH_TARGET" Enter | 
|  | fi | 
|  | tmux_ensure_bash | 
|  | tmux send-keys "PS1='[traced_probes]$ '" Enter | 
|  | tmux send-keys "cd $DIR" Enter | 
|  | tmux send-keys "clear" C-m | 
|  | tmux send-keys "$PREFIX ./traced_probes $POSTFIX" Enter | 
|  |  | 
|  | tmux select-pane -t 2 | 
|  | if is_ssh_target; then | 
|  | tmux send-keys "ssh $SSH_TARGET" Enter | 
|  | fi | 
|  | tmux_ensure_bash | 
|  | tmux send-keys "PS1='[consumer]$ '" Enter | 
|  | tmux send-keys "cd $DIR" Enter | 
|  | tmux send-keys "clear" C-m | 
|  | tmux send-keys "$PREFIX ./perfetto $CMD_OPTS -c $CONFIG_DEVICE_PATH -o trace $POSTFIX" | 
|  |  | 
|  | if [[ -n "$SCRIPT" ]]; then | 
|  | tmux select-pane -t 3 | 
|  | if is_ssh_target; then | 
|  | tmux send-keys "ssh $SSH_TARGET" Enter | 
|  | fi | 
|  | tmux_ensure_bash | 
|  | tmux send-keys "PS1='[script]$ '" Enter | 
|  | tmux send-keys "cd $DIR" Enter | 
|  | tmux send-keys "clear" C-m | 
|  | if [[ -f "$SCRIPT" ]]; then | 
|  | tmux send-keys "./$(basename "$SCRIPT")" | 
|  | else | 
|  | tmux send-keys "$SCRIPT" | 
|  | fi | 
|  | fi | 
|  |  | 
|  | # Select consumer pane. | 
|  | tmux select-pane -t 2 | 
|  |  | 
|  | tmux -2 attach-session -t demo | 
|  | if [[ BACKGROUND -eq 1 ]]; then | 
|  | exit 0 | 
|  | fi | 
|  |  | 
|  | reset_tracing | 
|  |  | 
|  | TRACE=$TMPDIR/trace | 
|  | echo -e "\n\x1b[32mPulling trace into $TRACE.perfetto-trace\x1b[0m" | 
|  | pull trace "$TRACE.perfetto-trace" | 
|  |  | 
|  | if [[ SKIP_CONVERTERS -eq 0 ]]; then | 
|  | echo -e "\n\x1b[32mPulling trace into $TRACE.pbtext\x1b[0m" | 
|  | "$OUT/$TRACECONV" text <"$TRACE.perfetto-trace" >"$TRACE.pbtext" | 
|  | fi |