chromium/tools/android/tracing/profile-chrome-startup.sh

#!/bin/bash
# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

set -e
shopt -s nullglob
shopt -s extglob

function kill_process() {
    name=$1
    pid=$($adb shell "pidof $name" || true)
    if [ ! -z "$pid" ]; then
        echo "Killing $name..."
        $adb shell "kill $pid"
    else
        echo "$name is not running."
    fi
}

function get_data_file() {
    trace_file=$1
    extension=$2
    echo "${trace_file%.*}.$extension"
}

function get_logcat_file() {
    trace_file=$1
    get_data_file "$trace_file" "logcat"
}

function get_meminfo_file() {
    trace_file=$1
    tag=$2
    get_data_file "$trace_file" "$tag.meminfo"
}

function analyze_trace_file() {
    trace_file=$1
    trace_events_to_print=$2
    logcat_file="$(get_logcat_file "$trace_file")"
    echo "Processes started / died:"
    grep -E "Start proc|died|MY-DBG" "$logcat_file"

    extractor_arguments="$trace_file"
    if [ ! -z "$trace_events_to_print" ]; then
        extractor_arguments="$extractor_arguments --print-events=$trace_events_to_print"
    fi
    "$this_path/systrace-extract-startup.py" $extractor_arguments
}

function analyze_meminfo_file() {
    meminfo_file=$1
    if [ -f "$meminfo_file" ]; then
        echo "$meminfo_file"
        grep -A 5 "Total RAM" "$meminfo_file"
    fi
}

function capture_analyze_meminfo_file() {
    meminfo_file=$1
    $adb shell "dumpsys meminfo" > "$meminfo_file"
    analyze_meminfo_file "$meminfo_file"
}

function echo_separator() {
    s="----------"
    echo
    echo "$s$s$s$s$s$s$s$s" # 8*10
}

this_path="$(dirname "$0")"

adb="$ADB_PATH"

if [ -z "$adb" ]; then
    adb="$(which adb)"
fi
if [ -z "$adb" ]; then
    echo "Where's adb? Can't find it."
    exit 1
fi

export ADB_PATH="$adb"

output_tag=
browser=chrome
trace_time=10
cold=false
url=
atrace_buffer_size=
atrace_categories=
killg=false
repeat=
analyze=false
meminfo=false
checktabs=false
taburl=
trace_events_to_print=
webapk_package=

for i in "$@"; do
    case $i in
        --help)
        echo "$(basename "$0") output-tag [options]"
        exit 0
        ;;
        --browser=*)
        browser="${i#*=}"
        ;;
        --url=*)
        url="${i#*=}"
        ;;
        --warm)
        cold=false
        ;;
        --cold)
        cold=true
        ;;
        --atrace=*)
        atrace_categories="${i#*=}"
        ;;
        --atrace-buffer-size*)
        atrace_buffer_size="${i#*=}"
        ;;
        --killg)
        killg=true
        ;;
        --trace-time=*)
        trace_time="${i#*=}"
        ;;
        --repeat=*)
        repeat="${i#*=}"
        ;;
        --analyze)
        analyze=true
        ;;
        --meminfo)
        meminfo=true
        ;;
        --checktabs)
        checktabs=true
        ;;
        --taburl=*)
        taburl="${i#*=}"
        ;;
        --print-trace-events=*)
        trace_events_to_print="${i#*=}"
        ;;
        --webapk=*)
        webapk_package="${i#*=}"
        ;;
        --extra_chrome_categories=*)
        extra_chrome_categories="${i#*=}"
        ;;
        --*)
        echo "Unknown option or missing option argument: $i"
        exit 1
        ;;
        *)
        if [ -z "$output_tag" ]; then
            output_tag="$i"
        else
            echo "Unknown option: $i"
            exit 1
        fi
        ;;
    esac
    if [ -z "$output_tag" ]; then
        echo "First argument must be the output tag."
        exit 1
    fi
    shift
done

if [ -d "$output_tag" ]; then
    output_tag="${output_tag%%+(/)}/trace"
fi
specified_output_tag="$output_tag"

if [ $analyze = true ]; then
    for file in $output_tag*.html; do
        echo_separator
        echo "$file"

        analyze_meminfo_file "$(get_meminfo_file "$output_file" "before")"
        analyze_meminfo_file "$(get_meminfo_file "$output_file" "after")"

        echo

        analyze_trace_file "$file" "$print_trace_events"
    done

    exit 0
fi

browser_package=
case $browser in
    chrome)
    browser_package="com.google.android.apps.chrome"
    ;;
    canary)
    browser_package="com.chrome.canary"
    browser="chrome_canary"
    ;;
    dev)
    browser_package="com.chrome.dev"
    ;;
    beta)
    browser_package="com.chrome.beta"
    ;;
    stable)
    browser_package="com.android.chrome"
    ;;
    chromium)
    browser_package="org.chromium.chrome"
    ;;
    *)
    echo "Unknown browser $browser"
    exit 1
esac

profile_options=

profile_options="$profile_options --browser=$browser"
if [ ! "$browser" = "chrome" ]; then
    output_tag="$output_tag-$browser"
fi

profile_options="$profile_options --time=$trace_time"

if [ ! -z "$atrace_categories" ]; then
    profile_options="$profile_options --atrace-categories=$atrace_categories"
    output_tag="$output_tag-${atrace_categories//,/_}"
fi

if [ ! -z "$atrace_buffer_size" ]; then
    profile_options="$profile_options --atrace-buffer-size=$atrace_buffer_size"
    output_tag="$output_tag-${atrace_buffer_size}"
fi

if [ $cold = true ]; then
    profile_options="$profile_options --cold"
    output_tag="$output_tag-cold"
fi

profile_options="$profile_options --url=$url"
if [ ! -z "$url" ]; then
    output_tag="$output_tag-url"
fi

if [ ! -z "$webapk_package" ]; then
    output_tag="$output_tag-webapk"
    profile_options="$profile_options --webapk-package=$webapk_package"
fi

if [ $killg = true ]; then
    output_tag="$output_tag-killg"
fi

# Must be last for ease of globbing.
output_tag="$output_tag-${trace_time}s"

if [ $checktabs = true ] || [ $killg = true ] || [ ! -z "$taburl" ]; then
    $adb root > /dev/null
fi

# Make sure Chrome can write the trace file.
# These commands may fail with a security exception on some Android devices that
# don't allow changing permissions. Chrome likely already has these permissions
# so the script should still work.
$adb shell "pm grant $browser_package \
                android.permission.READ_EXTERNAL_STORAGE" || true
$adb shell "pm grant $browser_package \
                android.permission.WRITE_EXTERNAL_STORAGE" || true

if [ ! -z "$taburl" ]; then
    echo "Opening $taburl in a single tab..."
    $adb shell "am force-stop $browser_package"
    $adb shell "rm -f /data/data/$browser_package/app_tabs/0/tab*"
    $adb shell "am start -a android.intent.action.VIEW \
                    -n $browser_package/org.chromium.chrome.browser.ChromeTabbedActivity \
                    -d $taburl"
    sleep 5
    $adb shell "am start -a android.intent.action.MAIN -c android.intent.category.HOME"
    sleep 1
    $adb shell "am force-stop $browser_package"
    echo
fi

repeat_count=1
if [ ! -z "$repeat" ]; then
    repeat_count="$repeat"
fi

if [ ! -z "$extra_chrome_categories" ]; then
    chrome_categories="_DEFAULT_CHROME_CATEGORIES,${extra_chrome_categories}"
    profile_options="$profile_options --chrome_categories=${chrome_categories}"
fi

first_iteration=true
for iteration in $(seq "$repeat_count"); do

    if [ $first_iteration = true ]; then
        first_iteration=false
    else
        echo_separator
        sleep 2
    fi

    if [ $killg = true ]; then
        echo "Preemptively killing g* processes..."
        $adb logcat -c
        kill_process "com.google.process.gapps"
        kill_process "com.google.android.gms"
        sleep 1
        $adb logcat -d | grep -E "Start proc|died" || true
    fi

    echo

    if [ -z "$repeat" ]; then
        output_file="$output_tag.html"
        if [ -f "$output_file" ]; then
            for i in {2..1000}; do
                output_file="$output_tag~$i.html"
                if [ ! -f "$output_file" ]; then
                    break;
                fi
            done
        fi
        if [ -f "$output_file" ]; then
            echo "Failed to find unoccupied output file. Last was: $output_file"
            exit 1
        fi
    else
        output_file="$output_tag~${repeat_count}_$iteration.html"
        if [ -f "$output_file" ]; then
            echo "Output file already exists: $output_file"
            echo "Please add something unique to the tag ($specified_output_tag)."
            exit 1
        fi
    fi
    echo "Output file: $output_file"

    logcat_file="$(get_logcat_file "$output_file")"
    echo "Logcat output file: $logcat_file"

    if [ $meminfo = true ]; then
        echo
        capture_analyze_meminfo_file "$(get_meminfo_file "$output_file" "before")"
    fi

    echo

    echo "Profiling with options: $profile_options"
    ./build/android/adb_profile_chrome_startup \
        $profile_options \
        "--output=$output_file"

    if [ $meminfo = true ]; then
        echo
        capture_analyze_meminfo_file "$(get_meminfo_file "$output_file" "after")"
    fi

    $adb shell "am force-stop $browser_package"
    $adb shell "killall logcat" > /dev/null 2>&1 || true

    $adb logcat -d > "${logcat_file}"

    rm -f chrome-profile-results-*
    $adb shell "rm -f /sdcard/Download/chrome-profile-results-*"

    echo

    analyze_trace_file "$output_file" "$trace_events_to_print"

    if [ $checktabs = true ]; then
        # Yields empty string when value is -1
        active_tab_id="$( \
            $adb shell "cat /data/data/$browser_package/shared_prefs/${browser_package}_preferences.xml" \
            | sed -n 's/^.*ACTIVE_TAB_ID" value="\([0-9]\{1,3\}\).*$/\1/p')"
        # Yields empty string when there is no 'tab' file
        tab_id="$( \
            $adb shell ls /data/data/$browser_package/app_tabs/0 \
            | sed -n 's/tab\([0-9]\{1,3\}\)\s*/\1/p')"
        if [ "$active_tab_id" != "$tab_id" ]; then
            echo "Tab IDs don't match (active_tab_id=$active_tab_id, tab_id=$tab_id), last result is not reliable."
            exit 1
        fi
    fi
done