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 |