summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/tools_webrtc/android
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/tools_webrtc/android')
-rw-r--r--third_party/libwebrtc/tools_webrtc/android/OWNERS1
-rw-r--r--third_party/libwebrtc/tools_webrtc/android/__init__.py0
-rwxr-xr-xthird_party/libwebrtc/tools_webrtc/android/adb_shell.sh23
-rwxr-xr-xthird_party/libwebrtc/tools_webrtc/android/build_aar.py269
-rwxr-xr-xthird_party/libwebrtc/tools_webrtc/android/profiling/perf_setup.sh470
-rwxr-xr-xthird_party/libwebrtc/tools_webrtc/android/profiling/utilities.sh154
-rw-r--r--third_party/libwebrtc/tools_webrtc/android/templates/maven-repository.jinja11
-rw-r--r--third_party/libwebrtc/tools_webrtc/android/templates/pom.jinja18
-rwxr-xr-xthird_party/libwebrtc/tools_webrtc/android/test_aar.py143
9 files changed, 1089 insertions, 0 deletions
diff --git a/third_party/libwebrtc/tools_webrtc/android/OWNERS b/third_party/libwebrtc/tools_webrtc/android/OWNERS
new file mode 100644
index 0000000000..cf092a316a
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/android/OWNERS
@@ -0,0 +1 @@
+xalep@webrtc.org
diff --git a/third_party/libwebrtc/tools_webrtc/android/__init__.py b/third_party/libwebrtc/tools_webrtc/android/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/android/__init__.py
diff --git a/third_party/libwebrtc/tools_webrtc/android/adb_shell.sh b/third_party/libwebrtc/tools_webrtc/android/adb_shell.sh
new file mode 100755
index 0000000000..e2d4f9187e
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/android/adb_shell.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# Copyright (c) 2012 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.
+
+# 'adb shell' always returns "0" regardless of executable return code.
+# This handy script will return executable return code to shell which
+# can be used by buildbots.
+
+adb_shell () {
+ local RET ADB_LOG
+ ADB_LOG=$(mktemp "${TMPDIR:-/tmp}/adb-XXXXXXXX")
+ adb "$1" "$2" shell "$3" "$4" ";" echo \$? | tee "$ADB_LOG"
+ sed -i -e 's![[:cntrl:]]!!g' "$ADB_LOG" # Remove \r.
+ RET=$(sed -e '$!d' "$ADB_LOG") # Last line contains status code.
+ rm -f "$ADB_LOG"
+ return $RET
+}
diff --git a/third_party/libwebrtc/tools_webrtc/android/build_aar.py b/third_party/libwebrtc/tools_webrtc/android/build_aar.py
new file mode 100755
index 0000000000..d910b39a7c
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/android/build_aar.py
@@ -0,0 +1,269 @@
+#!/usr/bin/env vpython3
+
+# 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.
+"""Script to generate libwebrtc.aar for distribution.
+
+The script has to be run from the root src folder.
+./tools_webrtc/android/build_aar.py
+
+.aar-file is just a zip-archive containing the files of the library. The file
+structure generated by this script looks like this:
+ - AndroidManifest.xml
+ - classes.jar
+ - libs/
+ - armeabi-v7a/
+ - libjingle_peerconnection_so.so
+ - x86/
+ - libjingle_peerconnection_so.so
+"""
+
+import argparse
+import logging
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+import zipfile
+
+SCRIPT_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
+SRC_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir))
+DEFAULT_ARCHS = ['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64']
+NEEDED_SO_FILES = ['libjingle_peerconnection_so.so']
+JAR_FILE = 'lib.java/sdk/android/libwebrtc.jar'
+MANIFEST_FILE = 'sdk/android/AndroidManifest.xml'
+TARGETS = [
+ 'sdk/android:libwebrtc',
+ 'sdk/android:libjingle_peerconnection_so',
+]
+
+sys.path.append(os.path.join(SCRIPT_DIR, '..', 'libs'))
+from generate_licenses import LicenseBuilder
+
+sys.path.append(os.path.join(SRC_DIR, 'build'))
+import find_depot_tools
+
+
+def _ParseArgs():
+ parser = argparse.ArgumentParser(description='libwebrtc.aar generator.')
+ parser.add_argument(
+ '--build-dir',
+ type=os.path.abspath,
+ help='Build dir. By default will create and use temporary dir.')
+ parser.add_argument('--output',
+ default='libwebrtc.aar',
+ type=os.path.abspath,
+ help='Output file of the script.')
+ parser.add_argument('--arch',
+ default=DEFAULT_ARCHS,
+ nargs='*',
+ help='Architectures to build. Defaults to %(default)s.')
+ parser.add_argument('--use-goma',
+ action='store_true',
+ default=False,
+ help='Use goma.')
+ parser.add_argument('--use-remoteexec',
+ action='store_true',
+ default=False,
+ help='Use RBE.')
+ parser.add_argument('--use-unstripped-libs',
+ action='store_true',
+ default=False,
+ help='Use unstripped .so files within libwebrtc.aar')
+ parser.add_argument('--verbose',
+ action='store_true',
+ default=False,
+ help='Debug logging.')
+ parser.add_argument(
+ '--extra-gn-args',
+ default=[],
+ nargs='*',
+ help="""Additional GN arguments to be used during Ninja generation.
+ These are passed to gn inside `--args` switch and
+ applied after any other arguments and will
+ override any values defined by the script.
+ Example of building debug aar file:
+ build_aar.py --extra-gn-args='is_debug=true'""")
+ parser.add_argument(
+ '--extra-ninja-switches',
+ default=[],
+ nargs='*',
+ help="""Additional Ninja switches to be used during compilation.
+ These are applied after any other Ninja switches.
+ Example of enabling verbose Ninja output:
+ build_aar.py --extra-ninja-switches='-v'""")
+ parser.add_argument(
+ '--extra-gn-switches',
+ default=[],
+ nargs='*',
+ help="""Additional GN switches to be used during compilation.
+ These are applied after any other GN switches.
+ Example of enabling verbose GN output:
+ build_aar.py --extra-gn-switches='-v'""")
+ return parser.parse_args()
+
+
+def _RunGN(args):
+ cmd = [
+ sys.executable,
+ os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'gn.py')
+ ]
+ cmd.extend(args)
+ logging.debug('Running: %r', cmd)
+ subprocess.check_call(cmd)
+
+
+def _RunNinja(output_directory, args):
+ cmd = [
+ os.path.join(SRC_DIR, 'third_party', 'ninja', 'ninja'), '-C',
+ output_directory
+ ]
+ cmd.extend(args)
+ logging.debug('Running: %r', cmd)
+ subprocess.check_call(cmd)
+
+
+def _EncodeForGN(value):
+ """Encodes value as a GN literal."""
+ if isinstance(value, str):
+ return '"' + value + '"'
+ if isinstance(value, bool):
+ return repr(value).lower()
+ return repr(value)
+
+
+def _GetOutputDirectory(build_dir, arch):
+ """Returns the GN output directory for the target architecture."""
+ return os.path.join(build_dir, arch)
+
+
+def _GetTargetCpu(arch):
+ """Returns target_cpu for the GN build with the given architecture."""
+ if arch in ['armeabi', 'armeabi-v7a']:
+ return 'arm'
+ if arch == 'arm64-v8a':
+ return 'arm64'
+ if arch == 'x86':
+ return 'x86'
+ if arch == 'x86_64':
+ return 'x64'
+ raise Exception('Unknown arch: ' + arch)
+
+
+def _GetArmVersion(arch):
+ """Returns arm_version for the GN build with the given architecture."""
+ if arch == 'armeabi':
+ return 6
+ if arch == 'armeabi-v7a':
+ return 7
+ if arch in ['arm64-v8a', 'x86', 'x86_64']:
+ return None
+ raise Exception('Unknown arch: ' + arch)
+
+
+def Build(build_dir, arch, use_goma, use_remoteexec, extra_gn_args,
+ extra_gn_switches, extra_ninja_switches):
+ """Generates target architecture using GN and builds it using ninja."""
+ logging.info('Building: %s', arch)
+ output_directory = _GetOutputDirectory(build_dir, arch)
+ gn_args = {
+ 'target_os': 'android',
+ 'is_debug': False,
+ 'is_component_build': False,
+ 'rtc_include_tests': False,
+ 'target_cpu': _GetTargetCpu(arch),
+ 'use_goma': use_goma,
+ 'use_remoteexec': use_remoteexec,
+ }
+ arm_version = _GetArmVersion(arch)
+ if arm_version:
+ gn_args['arm_version'] = arm_version
+ gn_args_str = '--args=' + ' '.join(
+ [k + '=' + _EncodeForGN(v) for k, v in gn_args.items()] + extra_gn_args)
+
+ gn_args_list = ['gen', output_directory, gn_args_str]
+ gn_args_list.extend(extra_gn_switches)
+ _RunGN(gn_args_list)
+
+ ninja_args = TARGETS[:]
+ if use_goma or use_remoteexec:
+ ninja_args.extend(['-j', '200'])
+ ninja_args.extend(extra_ninja_switches)
+ _RunNinja(output_directory, ninja_args)
+
+
+def CollectCommon(aar_file, build_dir, arch):
+ """Collects architecture independent files into the .aar-archive."""
+ logging.info('Collecting common files.')
+ output_directory = _GetOutputDirectory(build_dir, arch)
+ aar_file.write(MANIFEST_FILE, 'AndroidManifest.xml')
+ aar_file.write(os.path.join(output_directory, JAR_FILE), 'classes.jar')
+
+
+def Collect(aar_file, build_dir, arch, unstripped):
+ """Collects architecture specific files into the .aar-archive."""
+ logging.info('Collecting: %s', arch)
+ output_directory = _GetOutputDirectory(build_dir, arch)
+
+ abi_dir = os.path.join('jni', arch)
+ for so_file in NEEDED_SO_FILES:
+ source_so_file = os.path.join("lib.unstripped",
+ so_file) if unstripped else so_file
+ aar_file.write(os.path.join(output_directory, source_so_file),
+ os.path.join(abi_dir, so_file))
+
+
+def GenerateLicenses(output_dir, build_dir, archs):
+ builder = LicenseBuilder(
+ [_GetOutputDirectory(build_dir, arch) for arch in archs], TARGETS)
+ builder.GenerateLicenseText(output_dir)
+
+
+def BuildAar(archs,
+ output_file,
+ use_goma=False,
+ use_remoteexec=False,
+ extra_gn_args=None,
+ ext_build_dir=None,
+ extra_gn_switches=None,
+ extra_ninja_switches=None,
+ unstripped=False):
+ extra_gn_args = extra_gn_args or []
+ extra_gn_switches = extra_gn_switches or []
+ extra_ninja_switches = extra_ninja_switches or []
+ build_dir = ext_build_dir if ext_build_dir else tempfile.mkdtemp()
+
+ for arch in archs:
+ Build(build_dir, arch, use_goma, use_remoteexec, extra_gn_args,
+ extra_gn_switches, extra_ninja_switches)
+
+ with zipfile.ZipFile(output_file, 'w') as aar_file:
+ # Architecture doesn't matter here, arbitrarily using the first one.
+ CollectCommon(aar_file, build_dir, archs[0])
+ for arch in archs:
+ Collect(aar_file, build_dir, arch, unstripped)
+
+ license_dir = os.path.dirname(os.path.realpath(output_file))
+ GenerateLicenses(license_dir, build_dir, archs)
+
+ if not ext_build_dir:
+ shutil.rmtree(build_dir, True)
+
+
+def main():
+ args = _ParseArgs()
+ logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
+
+ BuildAar(args.arch, args.output, args.use_goma, args.use_remoteexec,
+ args.extra_gn_args, args.build_dir, args.extra_gn_switches,
+ args.extra_ninja_switches, args.use_unstripped_libs)
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/third_party/libwebrtc/tools_webrtc/android/profiling/perf_setup.sh b/third_party/libwebrtc/tools_webrtc/android/profiling/perf_setup.sh
new file mode 100755
index 0000000000..9c6b0f98ea
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/android/profiling/perf_setup.sh
@@ -0,0 +1,470 @@
+#!/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 an Android device which supports USB debugging.
+#
+# Source this script once from the WebRTC src/ directory and resolve any
+# reported issues. Add relative path to build directory as parameter.
+# Required tools will be downloaded if they don't already exist.
+#
+# Once all tests are passed, a list of available functions will be given.
+# Use these functions to do the actual profiling and visualization of the
+# results.
+#
+# Note that, using a rooted device is recommended since it allows us to
+# resolve kernel symbols (kallsyms) as well.
+#
+# Example usage:
+#
+# > . tools_webrtc/android/profiling/perf_setup.sh out/Release
+# > perf_record 120
+# > flame_graph
+# > plot_flame_graph
+# > perf_cleanup
+
+if [ -n "$ZSH_VERSION" ]; then
+ # Running inside zsh.
+ SCRIPT_PATH="${(%):-%N}"
+else
+ # Running inside something else (most likely bash).
+ SCRIPT_PATH="${BASH_SOURCE[0]}"
+fi
+SCRIPT_DIR="$(cd $(dirname "$SCRIPT_PATH") && pwd -P)"
+source "${SCRIPT_DIR}/utilities.sh"
+
+# Root directory for local symbol cache.
+SYMBOL_DIR="${TMPDIR:-/tmp}/android_symbols"
+# 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"
+}
+
+# Ensure that user includes name of build directory (e.g. out/Release) as
+# input parameter. Store path in BUILD_DIR.
+if [[ "$#" -eq 1 ]]; then
+ if is_not_dir "$1"; then
+ error "$1 is invalid"
+ return 1
+ fi
+ BUILD_DIR="$1"
+else
+ error "Missing required parameter".
+ usage
+ return 1
+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 kernel symbols from device to symbol cache in tmp.
+function copy_kernel_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"
+ # Copy the simpleperf binary from local host to temp folder on device.
+ adb push "${SCRIPT_DIR}/simpleperf/bin/android${perf_binary}" \
+ "${DEV_TMP_DIR}" 1> /dev/null
+ # Copy simpleperf from temp folder to the application package.
+ adb shell run-as "${APP_NAME}" cp "${DEV_TMP_DIR}/simpleperf" .
+ adb shell run-as "${APP_NAME}" chmod a+x simpleperf
+ # Enable profiling on the device.
+ enable_profiling
+ # Allows usage of running report commands on the device.
+ if image_is_root; then
+ enable_report_symbols
+ fi
+}
+
+# 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 shell run-as "${APP_NAME}" cp perf.data /sdcard/perf.data
+ adb pull sdcard/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"
+}
+
+# Removes the folder specified as input parameter. Mainly intended for removal
+# of simpleperf and Flame Graph tools.
+function remove_tool() {
+ local tool_dir="$1"
+ if is_dir "${tool_dir}"; then
+ echo "Removing ${tool_dir}..."
+ rm -rf "${tool_dir}"
+ path_remove "${tool_dir}"
+ fi
+}
+
+# Utility method which deletes the downloaded simpleperf tool from the repo.
+# It also removes the simpleperf root folder from PATH.
+function rm_simpleperf() {
+ remove_tool "${SCRIPT_DIR}/simpleperf"
+}
+
+# Utility method which deletes the downloaded Flame Graph tool from the repo.
+# It also removes the Flame Graph root folder from PATH.
+function rm_flame_graph() {
+ remove_tool "${SCRIPT_DIR}/flamegraph"
+}
+
+# Lists the main available functions after sourcing this script.
+function print_function_help() {
+ printf "\nAvailable functions in this shell:\n"
+ printf " perf_record [duration, default=60sec]\n"
+ printf " perf_report_threads\n"
+ printf " perf_report_bins\n"
+ printf " perf_report_symbols\n"
+ printf " perf_report_graph\n"
+ printf " perf_report_graph_callee\n"
+ printf " perf_update\n"
+ printf " perf_cleanup\n"
+ printf " flame_graph\n"
+ printf " plot_flame_graph\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
+ if image_is_root; then
+ copy_kernel_symbols_from_device_to_symbol_cache
+ fi
+}
+
+# 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 60 seconds but it can be changed
+# by adding one parameter. As soon as the recording is done, 'perf.data' is
+# copied to the directory from which this method is called and a summary of
+# the load distribution per thread is printed.
+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=60
+ 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)..."
+ adb shell run-as "${APP_NAME}" ./simpleperf record \
+ --call-graph fp \
+ -p "${pid}" \
+ -f 1000 \
+ --duration "${duration}" \
+ --log error
+ # 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
+}
+
+# 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}"
+}
+
+# Print call graph showing how functions call others.
+function perf_report_graph() {
+ perf_report -g caller --symfs "${SYMBOL_DIR}"
+}
+
+# Print call graph showing how functions are called from others.
+function perf_report_graph_callee() {
+ perf_report -g callee --symfs "${SYMBOL_DIR}"
+}
+
+# Plots the default Flame Graph file if no parameter is provided.
+# If a parameter is given, it will be used as file name instead of the default.
+function plot_flame_graph() {
+ local file_name="flame_graph.svg"
+ if [[ "$#" -eq 1 ]]; then
+ file_name="$1"
+ fi
+ # Open up the SVG file in Chrome. Try unstable first and revert to stable
+ # if unstable fails.
+ google-chrome-unstable "${file_name}" \
+ || google-chrome-stable "${file_name}" \
+ || error "failed to find any Chrome instance"
+} 2> /dev/null
+
+# Generate Flame Graph in interactive SVG format.
+# First input parameter corresponds to output file name and second input
+# parameter is the heading of the plot.
+# Defaults will be utilized if parameters are not provided.
+# See https://github.com/brendangregg/FlameGraph for details on Flame Graph.
+function flame_graph() {
+ local perf_data="perf.data"
+ if is_not_file $perf_data; then
+ error "$(pwd)/${perf_data} is invalid"
+ return 1
+ fi
+ local file_name="flame_graph.svg"
+ local title="WebRTC Flame Graph"
+ if [[ "$#" -eq 1 ]]; then
+ file_name="$1"
+ fi
+ if [[ "$#" -eq 2 ]]; then
+ file_name="$1"
+ title="$2"
+ fi
+ if image_is_not_root; then
+ report_sample.py \
+ --symfs "${SYMBOL_DIR}" \
+ perf.data >out.perf
+ else
+ report_sample.py \
+ --symfs "${SYMBOL_DIR}" \
+ --kallsyms "${SYMBOL_DIR}/kallsyms" \
+ perf.data >out.perf
+ fi
+ stackcollapse-perf.pl out.perf >out.folded
+ flamegraph.pl --title="${title}" out.folded >"${file_name}"
+ rm out.perf
+ rm out.folded
+}
+
+# Remove all downloaded third-party tools.
+function perf_cleanup () {
+ rm_simpleperf
+ rm_flame_graph
+}
+
+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
+ 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"
+
+ # Restart adb with root permissions if needed.
+ if image_is_root && adb_has_no_root_permissions; then
+ adb root
+ ok "adb is running as root"
+ fi
+
+ # 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"
+
+ # Download simpleperf to <src>/tools_webrtc/android/profiling/simpleperf/.
+ # Cloning will only take place if the target does not already exist.
+ # The PATH variable will also be updated.
+ # TODO(henrika): would it be better to use a target outside the WebRTC repo?
+ local simpleperf_dir="${SCRIPT_DIR}/simpleperf"
+ if is_not_dir "${simpleperf_dir}"; then
+ echo "Dowloading simpleperf..."
+ git clone https://android.googlesource.com/platform/prebuilts/simpleperf \
+ "${simpleperf_dir}"
+ chmod u+x "${simpleperf_dir}/report_sample.py"
+ fi
+ path_add "${simpleperf_dir}"
+ ok "${simpleperf_dir}" is added to PATH
+
+ # 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_add "${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 and read kernal symbols from device if not
+ # already done.
+ perf_update
+ ok "symbol cache is updated"
+
+ # Download Flame Graph to <src>/tools_webrtc/android/profiling/flamegraph/.
+ # Cloning will only take place if the target does not already exist.
+ # The PATH variable will also be updated.
+ # TODO(henrika): would it be better to use a target outside the WebRTC repo?
+ local flamegraph_dir="${SCRIPT_DIR}/flamegraph"
+ if is_not_dir "${flamegraph_dir}"; then
+ echo "Dowloading Flame Graph visualization tool..."
+ git clone https://github.com/brendangregg/FlameGraph.git "${flamegraph_dir}"
+ fi
+ path_add "${flamegraph_dir}"
+ ok "${flamegraph_dir}" is added to PATH
+
+ print_function_help
+
+ cleanup
+
+ return 0
+}
+
+# Only call main() if proper input parameter has been provided.
+if is_set $BUILD_DIR; then
+ main "$@"
+fi
diff --git a/third_party/libwebrtc/tools_webrtc/android/profiling/utilities.sh b/third_party/libwebrtc/tools_webrtc/android/profiling/utilities.sh
new file mode 100755
index 0000000000..46a97b8142
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/android/profiling/utilities.sh
@@ -0,0 +1,154 @@
+#!/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.
+
+# Utility functions to be used by perf_setup.sh.
+# Contains helper methods and functions that wraps usage of adb.
+
+function error() {
+ echo "[ERROR] "$@"" >&2
+}
+
+function warning() {
+ echo "[WARNING] "$@"" >&1
+}
+
+function ok() {
+ echo "[OK] "$@"" >&1
+}
+
+function abs_path {
+ (cd $1; pwd)
+}
+
+function is_set() {
+ local var="$1"
+ [[ -n "${var}" ]]
+}
+
+function is_file() {
+ local file="$1"
+ [[ -f "${file}" ]]
+}
+
+function is_not_file() {
+ local file="$1"
+ [[ ! -f "${file}" ]]
+}
+
+function is_dir() {
+ local dir="$1"
+ [[ -d "${dir}" ]]
+}
+
+function is_not_dir() {
+ local dir="$1"
+ [[ ! -d "${dir}" ]]
+}
+
+# Adds (prepends) the PATH environment variable while avoid duplicates.
+function path_add() {
+ case ":${PATH:=$1}:" in
+ *:$1:*) ;;
+ *) PATH="$1:$PATH" ;;
+ esac
+}
+
+# Removes a path from the PATH environment variable using search-and-replace
+# parameter expansion.
+function path_remove {
+ local path="$1"
+ # Substitute first occurrence of ":path" in PATH with an empty string.
+ # Deletes instances in the middle or at the end.
+ PATH=${PATH/":$path"/}
+ # Substitute first occurrence of "path:" in PATH with an empty string.
+ # Delete instances at the beginning.
+ PATH=${PATH/"$path:"/}
+}
+
+# Returns the process ID (PID) of the process that corresponds to the
+# application name given as input parameter.
+function find_app_pid() {
+ local app_name="$1"
+ adb shell ps | grep "${app_name}" | awk '{print $2}'
+}
+
+function app_is_installed() {
+ local app_name="$1"
+ local installed_app_name=$(adb shell pm list packages \
+ | grep "${app_name}" | awk -F':' '{print $2}')
+ is_set "${installed_app_name}" \
+ && [[ "${installed_app_name}" = "${app_name}" ]]
+}
+
+function app_is_running() {
+ local app_name="$1"
+ local app_pid=$(find_app_pid "${app_name}")
+ is_set "${app_pid}"
+}
+
+function app_start() {
+ local app_name="$1"
+ adb shell am start \
+ -n "${app_name}/.ConnectActivity" \
+ -a android.intent.action.MAIN
+}
+
+function app_stop() {
+ local app_name="$1"
+ adb shell am force-stop "${app_name}"
+}
+
+function app_uninstall() {
+ local app_name="$1"
+ adb uninstall "${app_name}"
+}
+
+function dev_arch() {
+ adb shell uname -m
+}
+
+function dev_ls() {
+ local dir="$1"
+ adb shell ls "${dir}"
+}
+
+# Returns true if exactly on device is connected.
+function one_device_connected() {
+ [[ $(adb devices | wc -l) = 3 ]]
+}
+
+# Returns true if device is rooted.
+function image_is_root() {
+ [[ $(adb shell getprop ro.build.type) = "userdebug" ]]
+}
+
+# Returns true if device is not rooted.
+function image_is_not_root() {
+ [[ $(adb shell getprop ro.build.type) = "user" ]]
+}
+
+# Returns true if adb is not already running as root.
+# Should only be called on rooted devices.
+function adb_has_no_root_permissions() {
+ [[ $(adb shell getprop service.adb.root) = 0 ]]
+}
+
+# Android devices may disable profiling by default. We must enable it.
+function enable_profiling() {
+ adb shell setprop security.perf_harden 0
+}
+
+# To make the report of symbols on device successful, we need to execute
+# `echo 0 >/proc/sys/kernel/kptr_restrict`.
+# Only needed if we run report commands on the same machine as we run
+# record commands.
+function enable_report_symbols() {
+ adb shell "echo 0 > /proc/sys/kernel/kptr_restrict"
+}
diff --git a/third_party/libwebrtc/tools_webrtc/android/templates/maven-repository.jinja b/third_party/libwebrtc/tools_webrtc/android/templates/maven-repository.jinja
new file mode 100644
index 0000000000..10a37f40d3
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/android/templates/maven-repository.jinja
@@ -0,0 +1,11 @@
+allprojects {
+ repositories {
+ maven {
+ url '{{ url }}'
+ credentials {
+ username '{{ username }}'
+ password '{{ password }}'
+ }
+ }
+ }
+}
diff --git a/third_party/libwebrtc/tools_webrtc/android/templates/pom.jinja b/third_party/libwebrtc/tools_webrtc/android/templates/pom.jinja
new file mode 100644
index 0000000000..90ca51ca9c
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/android/templates/pom.jinja
@@ -0,0 +1,18 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.webrtc</groupId>
+ <artifactId>google-webrtc</artifactId>
+ <version>{{ version }}</version>
+ <packaging>aar</packaging>
+
+ <name>Google's WebRTC Android library</name>
+ <url>https://webrtc.org/</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.build.commitid>{{ commit }}</project.build.commitid>
+ </properties>
+
+</project>
diff --git a/third_party/libwebrtc/tools_webrtc/android/test_aar.py b/third_party/libwebrtc/tools_webrtc/android/test_aar.py
new file mode 100755
index 0000000000..7eb281aa9a
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/android/test_aar.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env vpython3
+
+# 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.
+"""Script for building and testing WebRTC AAR."""
+
+import argparse
+import logging
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+
+SCRIPT_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
+CHECKOUT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir))
+
+sys.path.append(os.path.join(CHECKOUT_ROOT, 'tools_webrtc'))
+from android.build_aar import BuildAar
+
+ARCHS = ['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64']
+ARTIFACT_ID = 'google-webrtc'
+COMMIT_POSITION_REGEX = r'^Cr-Commit-Position: refs/heads/master@{#(\d+)}$'
+GRADLEW_BIN = os.path.join(CHECKOUT_ROOT,
+ 'examples/androidtests/third_party/gradle/gradlew')
+ADB_BIN = os.path.join(CHECKOUT_ROOT,
+ 'third_party/android_sdk/public/platform-tools/adb')
+AAR_PROJECT_DIR = os.path.join(CHECKOUT_ROOT, 'examples/aarproject')
+
+
+def _ParseArgs():
+ parser = argparse.ArgumentParser(description='Releases WebRTC on Bintray.')
+ parser.add_argument('--use-goma',
+ action='store_true',
+ default=False,
+ help='Use goma.')
+ parser.add_argument('--skip-tests',
+ action='store_true',
+ default=False,
+ help='Skips running the tests.')
+ parser.add_argument(
+ '--build-dir',
+ default=None,
+ help='Temporary directory to store the build files. If not specified, '
+ 'a new directory will be created.')
+ parser.add_argument('--verbose',
+ action='store_true',
+ default=False,
+ help='Debug logging.')
+ return parser.parse_args()
+
+
+def _GetCommitHash():
+ commit_hash = subprocess.check_output(
+ ['git', 'rev-parse', 'HEAD'], cwd=CHECKOUT_ROOT).decode('UTF-8').strip()
+ return commit_hash
+
+
+def _GetCommitPos():
+ commit_message = subprocess.check_output(
+ ['git', 'rev-list', '--format=%B', '--max-count=1', 'HEAD'],
+ cwd=CHECKOUT_ROOT).decode('UTF-8')
+ commit_pos_match = re.search(COMMIT_POSITION_REGEX, commit_message,
+ re.MULTILINE)
+ if not commit_pos_match:
+ raise Exception('Commit position not found in the commit message: %s' %
+ commit_message)
+ return commit_pos_match.group(1)
+
+
+def _TestAAR(build_dir):
+ """Runs AppRTCMobile tests using the AAR. Returns true if the tests pass."""
+ logging.info('Testing library.')
+
+ # Uninstall any existing version of AppRTCMobile.
+ logging.info('Uninstalling previous AppRTCMobile versions. It is okay for '
+ 'these commands to fail if AppRTCMobile is not installed.')
+ subprocess.call([ADB_BIN, 'uninstall', 'org.appspot.apprtc'])
+ subprocess.call([ADB_BIN, 'uninstall', 'org.appspot.apprtc.test'])
+
+ # Run tests.
+ try:
+ # First clean the project.
+ subprocess.check_call([GRADLEW_BIN, 'clean'], cwd=AAR_PROJECT_DIR)
+ # Then run the tests.
+ subprocess.check_call([
+ GRADLEW_BIN, 'connectedDebugAndroidTest',
+ '-PaarDir=' + os.path.abspath(build_dir)
+ ],
+ cwd=AAR_PROJECT_DIR)
+ except subprocess.CalledProcessError:
+ logging.exception('Test failure.')
+ return False # Clean or tests failed
+
+ return True # Tests pass
+
+
+def BuildAndTestAar(use_goma, skip_tests, build_dir):
+ version = '1.0.' + _GetCommitPos()
+ commit = _GetCommitHash()
+ logging.info('Building and Testing AAR version %s with hash %s', version,
+ commit)
+
+ # If build directory is not specified, create a temporary directory.
+ use_tmp_dir = not build_dir
+ if use_tmp_dir:
+ build_dir = tempfile.mkdtemp()
+
+ try:
+ base_name = ARTIFACT_ID + '-' + version
+ aar_file = os.path.join(build_dir, base_name + '.aar')
+
+ logging.info('Building at %s', build_dir)
+ BuildAar(ARCHS,
+ aar_file,
+ use_goma=use_goma,
+ ext_build_dir=os.path.join(build_dir, 'aar-build'))
+
+ tests_pass = skip_tests or _TestAAR(build_dir)
+ if not tests_pass:
+ raise Exception('Test failure.')
+
+ logging.info('Test success.')
+
+ finally:
+ if use_tmp_dir:
+ shutil.rmtree(build_dir, True)
+
+
+def main():
+ args = _ParseArgs()
+ logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
+ BuildAndTestAar(args.use_goma, args.skip_tests, args.build_dir)
+
+
+if __name__ == '__main__':
+ sys.exit(main())