Chromium Code Reviews| Index: tools-webrtc/android/profiling/perf_setup.sh |
| diff --git a/tools-webrtc/android/profiling/perf_setup.sh b/tools-webrtc/android/profiling/perf_setup.sh |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..735787aca69aae84df1273f293502d0067be5143 |
| --- /dev/null |
| +++ b/tools-webrtc/android/profiling/perf_setup.sh |
| @@ -0,0 +1,364 @@ |
| +#!/bin/bash |
| + |
| +# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
| +# |
| +# Use of this source code is governed by a BSD-style license |
| +# that can be found in the LICENSE file in the root of the source |
| +# tree. An additional intellectual property rights grant can be found |
| +# in the file PATENTS. All contributing project authors may |
| +# be found in the AUTHORS file in the root of the source tree. |
| +# |
| +# Usage: |
| +# |
| +# It is assumed that a release build of AppRTCMobile exists and has been |
| +# installed on a rooted and attached Android device. |
| +# |
| +# Source this script once from the WebRTC source directory and resolve any |
| +# reported issues. Add relative path to build directory as parameter. |
| +# |
| +# Once all tests are passed, a list of available functions will be listed. |
| +# Use these functions to do the actual profiling and visualization of the |
| +# results. |
| +# |
| +# Example: |
| +# |
| +# >> . tools-webrtc/android/profiling/perf_setup.sh out/Release |
| +# >> perf_record 60 |
| +# >> perf_report_symbols |
| +# |
| +# TODO(henrika): add more here for Flame Graph plots... |
| + |
| +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" |
| +source "${SCRIPT_DIR}/utilities.sh" |
| + |
| +# Root directory for local symbol cache. |
| +SYMBOL_DIR="${TMPDIR:-/tmp}/android_symbols" |
| +# Root directory to prebuilt (Android and Linux) versions of simpleperf. |
| +SIMPLE_PERF_ROOT_DIR="${SCRIPT_DIR}/simpleperf/bin" |
| +# Used as a temporary folder on the Android device for data storage. |
| +DEV_TMP_DIR="/data/local/tmp" |
| +# Relative path to native shared library containing symbols. |
| +NATIVE_LIB_PATH="/lib.unstripped/libjingle_peerconnection_so.so" |
| +# Name of application package for the AppRTCMobile demo. |
| +APP_NAME="org.appspot.apprtc" |
| + |
| +# Make sure we're being sourced. |
| +if [[ -n "${BASH_VERSION}" && "${BASH_SOURCE:-$0}" == "$0" ]]; then |
| + error "perf_setup must be sourced!" |
| + exit 1 |
| +fi |
| + |
| +function usage() { |
| + printf "usage: . perf_setup.sh <build_dir>\n" |
| + printf "Example: . perf_setup.sh out/Release\n" |
| +} |
| + |
| +# Ensure that user includes name of build directory (e.g. out/Release) as |
| +# input parameter. Store path in BUILD_DIR. |
| +if [[ "$#" -eq 1 ]]; then |
| + BUILD_DIR="$1" |
| +else |
| + unset BUILD_DIR |
|
kjellander_webrtc
2017/02/22 14:46:57
Why unset it?
henrika_webrtc
2017/02/22 15:47:11
Long story. But essentially. If I don't do it, we
kjellander_webrtc
2017/02/24 11:02:25
Acknowledged.
|
| + error "Missing required parameter". |
| + usage |
| +fi |
| + |
| +# Helper method to simpify usage of the simpleperf binary on the device. |
| +function simpleperf_android() { |
| + local simpleperf="${DEV_TMP_DIR}/simpleperf" |
| + if [ ! -z "$1" ]; then |
| + adb shell "${simpleperf}" "$@" |
| + else |
| + adb shell $simpleperf --help |
| + fi |
| +} |
| + |
| +# Full (relative) path to the libjingle_peerconnection_so.so file. |
| +function native_shared_lib_path() { |
| + echo "${BUILD_DIR}${NATIVE_LIB_PATH}" |
| +} |
| + |
| +# Target CPU architecture for the native shared library. |
| +# Example: AArch64. |
| +function native_shared_lib_arch() { |
| + readelf -h $(native_shared_lib_path) | grep Machine | awk '{print $2}' |
| +} |
| + |
| +# Returns true if the device architecture and the build target are the same. |
| +function arch_is_ok() { |
| + if [[ "$(dev_arch)" == "aarch64" ]] \ |
| + && [[ "$(native_shared_lib_arch)" == "AArch64" ]]; then |
| + return 0 |
| + elif [[ "$(dev_arch)" == "aarch32" ]] \ |
| + && [[ "$(native_shared_lib_arch)" == "AArch32" ]]; then |
| + return 0 |
| + else |
| + return 1 |
| + fi |
| +} |
| + |
| +# Copies the native shared library from the local host to the symbol cache |
| +# which is used by simpleperf as base when searching for symbols. |
| +function copy_native_shared_library_to_symbol_cache() { |
| + local arm_lib="arm" |
| + if [ "$(native_shared_lib_arch)" == "AArch64" ]; then |
| + arm_lib="arm64" |
| + fi |
| + for num in 1 2; do |
| + local dir="${SYMBOL_DIR}/data/app/${APP_NAME}-${num}/lib/${arm_lib}" |
| + mkdir -p "${dir}" |
| + cp -u $(native_shared_lib_path) "${dir}" |
| + done |
| +} |
| + |
| +# Copy kernal symbols from device to symbol cache in tmp. |
| +function copy_kernal_symbols_from_device_to_symbol_cache() { |
| + local symbol_cache="${SYMBOL_DIR}/kallsyms" |
| + adb pull /proc/kallsyms "${symbol_cache}" |
| +} 1> /dev/null |
| + |
| +# Download the correct version of 'simpleperf' to $DEV_TMP_DIR |
| +# on the device and enable profiling. |
| +function copy_simpleperf_to_device() { |
| + local perf_binary |
| + [[ $(dev_arch) == "aarch64" ]] \ |
| + && perf_binary="/arm64/simpleperf" \ |
| + || perf_binary="/arm/simpleperf" |
| + local simpleperf="${DEV_TMP_DIR}/simpleperf" |
| + # Avoid copying to device if simpleperf already exists. |
| + if [[ ! $(dev_ls "${simpleperf}") ]]; then |
| + adb push "${SIMPLE_PERF_DIR}${perf_binary}" "${DEV_TMP_DIR}" |
| + adb shell chmod a+x $simpleperf |
| + fi |
| + # Enable profiling on the device. |
| + enable_profiling |
| + # Allows usgage of running report commands on the device. |
|
kjellander_webrtc
2017/02/22 14:46:57
usgage -> usage?
henrika_webrtc
2017/02/22 15:47:11
Done.
|
| + enable_report_symbols |
| +} |
| + |
| +# Copy the recorded 'perf.data' file from the device to the current directory. |
| +# TODO(henrika): add support for specifying the destination. |
| +function pull_perf_data_from_device() { |
| + adb pull "${DEV_TMP_DIR}/perf.data" . |
| +} 1> /dev/null |
| + |
| + |
| +# Wraps calls to simpleperf report. Used by e.g. perf_report_threads. |
| +# A valid profile input file must exist in the current folder. |
| +# TODO(henrika): possibly add support to add path to alternative input file. |
| +function perf_report() { |
| + local perf_data="perf.data" |
| + is_file "${perf_data}" \ |
| + && simpleperf report \ |
| + -n \ |
| + -i "${perf_data}" \ |
| + "$@" \ |
| + || error "$(pwd)/${perf_data} is invalid!" |
| +} |
| + |
| +# Lists the main available functions after sourcing this script. |
| +function print_function_help() { |
| + printf "\nAvailable functions:\n" |
| + printf " perf_record [duration, default=30sec]\n" |
| + printf " perf_report_threads\n" |
| + printf " perf_report_bins\n" |
| + printf " perf_report_symbols\n" |
| + printf " perf_update\n" |
| +} |
| + |
| +function cleanup() { |
| + unset -f main |
| +} |
| + |
| +# ----------------------------------------------------------------------------- |
| +# Main methods to be used after sourcing the main script. |
| +# ----------------------------------------------------------------------------- |
| + |
| +# Call this method after the application as been rebuilt and installed on the |
| +# device to ensure that symbols are up-to-date. |
| +function perf_update() { |
| + copy_native_shared_library_to_symbol_cache |
| + copy_kernal_symbols_from_device_to_symbol_cache |
| +} |
| + |
| +# Record stack frame based call graphs while using the application. |
| +# We use default events (cpu-cycles), and write records to 'perf.data' in the |
| +# tmp folder on the device. Default duration is 30 seconds but it can be changed |
| +# by adding one parameter. As soon as the recording is done, 'perf.data' is |
| +# copied to the directoty from which this method is called. |
| +function perf_record() { |
| + if app_is_running "${APP_NAME}"; then |
| + # Ensure that the latest native shared library exists in the local cache. |
| + copy_native_shared_library_to_symbol_cache |
| + local duration=30 |
| + if [ "$#" -eq 1 ]; then |
| + duration="$1" |
| + fi |
| + local pid=$(find_app_pid "${APP_NAME}") |
| + echo "Profiling PID $pid for $duration seconds (media must be is active)..." |
| + local output_file="${DEV_TMP_DIR}/perf.data" |
| + simpleperf_android record \ |
| + --call-graph fp \ |
| + -p "${pid}" \ |
| + -o $output_file \ |
| + -f 1000 \ |
| + --duration "${duration}" \ |
| + --log error |
| + app_stop "${APP_NAME}" |
| + # Copy profile results from device to current directory. |
| + pull_perf_data_from_device |
| + # Print out a summary report (load per thread). |
| + perf_report_threads | tail -n +6 |
| + else |
| + # AppRTCMobile was not enabled. Start it up automatically and ask the user |
| + # to start media and then call this method again. |
| + warning "AppRTCMobile must be active" |
| + app_start "${APP_NAME}" |
| + echo "Start media and then call perf_record() again..." |
| + fi 2> /dev/null |
| +} |
| + |
| +# Analyze the profile report and show samples per threads. |
| +function perf_report_threads() { |
| + perf_report --sort comm |
| +} 2> /dev/null |
| + |
| +# Analyze the profile report and show samples per binary. |
| +function perf_report_bins() { |
| + perf_report --sort dso |
| +} 2> /dev/null |
| + |
| +# Analyze the profile report and show samples per symbol. |
| +function perf_report_symbols() { |
| + perf_report --sort symbol --symfs "${SYMBOL_DIR}" |
| +} |
| + |
| +# ----------------------------------------------------------------------------- |
| +# TODO(henrika): add comments... |
|
kjellander_webrtc
2017/02/22 14:46:57
Which ones are you referring to? I think it's pret
henrika_webrtc
2017/02/22 15:47:10
Removed.
|
| +# ----------------------------------------------------------------------------- |
| + |
| +main() { |
| + printf "%s\n" "Preparing profiling of AppRTCMobile on Android:" |
| + # Verify that this script is called from the root folder of WebRTC, |
| + # i.e., the src folder one step below where the .gclient file exists. |
| + local -r project_root_dir=$(pwd) |
| + local dir=${project_root_dir##*/} |
| + if [[ "${dir}" != "src" ]]; then |
| + error "script must be called from the WebRTC project root (src) folder" |
| + return 1 |
| + fi |
| + ok "project root: ${project_root_dir}" |
| + |
| + # Verify that user has sourced envsetup.sh. |
| + # TODO(henrika): might be possible to remove this check. |
| + if [[ -z "$ENVSETUP_GYP_CHROME_SRC" ]]; then |
|
kjellander_webrtc
2017/02/22 14:46:57
This sounds like something that might go away. May
henrika_webrtc
2017/02/22 15:47:11
I have not found any other way actually. It works
|
| + error "must source envsetup script first" |
| + return 1 |
| + fi |
| + ok "envsetup script has been sourced" |
| + |
| + # Given that envsetup is sourced, the adb tool should be accessible but |
| + # do one extra check just in case. |
| + local adb_full_path=$(which adb); |
| + if [[ ! -x "${adb_full_path}" ]]; then |
| + error "unable to find the Android Debug Bridge (adb) tool" |
| + return 1 |
| + fi |
| + ok "adb tool is working" |
| + |
| + # Exactly one Android device must be connected. |
| + if [[ ! one_device_connected ]]; then |
| + error "one device must be connected!" |
| + return 1 |
| + fi |
| + ok "one device is connected via USB" |
| + |
| + # Ensure that the device is rooted. |
| + if image_is_not_root; then |
| + error "device is not rooted!" |
| + return 1 |
| + fi |
| + ok "device is rooted" |
| + |
| + # Restart adb with root permissions if needed. |
| + if adb_has_no_root_permissions; then |
| + adb root |
| + fi |
| + ok "adbd is running as root" |
| + |
| + # Create an empty symbol cache in the tmp folder. |
| + # TODO(henrika): it might not be required to start from a clean cache. |
| + is_dir "${SYMBOL_DIR}" && rm -rf "${SYMBOL_DIR}" |
| + mkdir "${SYMBOL_DIR}" \ |
| + && ok "empty symbol cache created at ${SYMBOL_DIR}" \ |
| + || error "failed to create symbol cache" |
| + |
| + # Ensure that path to the native library with symbols is valid. |
| + local native_lib=$(native_shared_lib_path) |
| + if is_not_file ${native_lib}; then |
| + error "${native_lib} is not a valid file" |
| + return 1 |
| + fi |
| + ok "native library: "${native_lib}"" |
| + |
| + # Verify that the architechture of the device matches the architecture |
| + # of the native library. |
| + if ! arch_is_ok; then |
| + error "device is $(dev_arch) and lib is $(native_shared_lib_arch)" |
| + return 1 |
| + fi |
| + ok "device is $(dev_arch) and lib is $(native_shared_lib_arch)" |
| + |
| + # Copy native shared library to symbol cache after creating an |
| + # application specific tree structure under ${SYMBOL_DIR}/data. |
| + copy_native_shared_library_to_symbol_cache |
| + ok "native library copied to ${SYMBOL_DIR}/data/app/${APP_NAME}" |
| + |
| + # Verify that the application is installed on the device. |
| + if ! app_is_installed "${APP_NAME}"; then |
| + error "${APP_NAME} is not installed on the device" |
| + return 1 |
| + fi |
| + ok "${APP_NAME} is installed on the device" |
| + |
| + # Ensure that folder for simpleperf executables (Android and Linux) exist |
| + # as it should relative to the working directory (source of WebRTC). |
| + if is_not_dir "${SIMPLE_PERF_ROOT_DIR}"; then |
| + error "${SIMPLE_PERF_ROOT_DIR} is invalid" |
| + return 1 |
| + fi |
| + ok "${SIMPLE_PERF_ROOT_DIR} is valid" |
| + |
| + # Update the PATH variable with the path to the Linux version of simpleperf. |
| + local simpleperf_linux_dir="${SCRIPT_DIR}/simpleperf/bin/linux/x86_64/" |
| + if is_not_dir "${simpleperf_linux_dir}"; then |
| + error "${simpleperf_linux_dir} is invalid" |
| + return 1 |
| + fi |
| + path_update "${simpleperf_linux_dir}" |
| + ok "${simpleperf_linux_dir}" is added to PATH |
| + |
| + # Copy correct version (arm or arm64) of simpleperf to the device |
| + # and enable profiling at the same time. |
| + if ! copy_simpleperf_to_device; then |
| + error "failed to install simpleperf on the device" |
| + return 1 |
| + fi |
| + ok "simpleperf is installed on the device" |
| + |
| + # Refresh the symbol cache as last step. Now also reading kernal symbols |
| + # from device if not already done. |
| + perf_update |
| + ok "symbol cache is updated" |
| + |
| + print_function_help |
| + |
| + cleanup |
| + |
| + return 0 |
| +} |
| + |
| +# Only call main() if proper input parameter has been provided. |
| +if is_set $BUILD_DIR; then |
| + main "$@" |
| +fi |