| OLD | NEW |
| 1 #!/bin/bash | 1 #!/bin/bash |
| 2 | 2 |
| 3 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. | 3 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
| 4 # | 4 # |
| 5 # Use of this source code is governed by a BSD-style license | 5 # Use of this source code is governed by a BSD-style license |
| 6 # that can be found in the LICENSE file in the root of the source | 6 # that can be found in the LICENSE file in the root of the source |
| 7 # tree. An additional intellectual property rights grant can be found | 7 # tree. An additional intellectual property rights grant can be found |
| 8 # in the file PATENTS. All contributing project authors may | 8 # in the file PATENTS. All contributing project authors may |
| 9 # be found in the AUTHORS file in the root of the source tree. | 9 # be found in the AUTHORS file in the root of the source tree. |
| 10 # | 10 # |
| 11 # Usage: | 11 # Usage: |
| 12 # | 12 # |
| 13 # It is assumed that a release build of AppRTCMobile exists and has been | 13 # It is assumed that a release build of AppRTCMobile exists and has been |
| 14 # installed on a rooted and attached Android device. | 14 # installed on an Android device which supports USB debugging. |
| 15 # | 15 # |
| 16 # Source this script once from the WebRTC src/ directory and resolve any | 16 # Source this script once from the WebRTC src/ directory and resolve any |
| 17 # reported issues. Add relative path to build directory as parameter. | 17 # reported issues. Add relative path to build directory as parameter. |
| 18 # Required tools will be downloaded if they don't already exist. | 18 # Required tools will be downloaded if they don't already exist. |
| 19 # | 19 # |
| 20 # Once all tests are passed, a list of available functions will be given. | 20 # Once all tests are passed, a list of available functions will be given. |
| 21 # Use these functions to do the actual profiling and visualization of the | 21 # Use these functions to do the actual profiling and visualization of the |
| 22 # results. | 22 # results. |
| 23 # | 23 # |
| 24 # Note that, using a rooted device is recommended since it allows us to |
| 25 # resolve kernel symbols (kallsyms) as well. |
| 26 # |
| 24 # Example usage: | 27 # Example usage: |
| 25 # | 28 # |
| 26 # > . tools-webrtc/android/profiling/perf_setup.sh out/Release | 29 # > . tools-webrtc/android/profiling/perf_setup.sh out/Release |
| 27 # > perf_record 120 | 30 # > perf_record 120 |
| 28 # > flame_graph | 31 # > flame_graph |
| 29 # > plot_flame_graph | 32 # > plot_flame_graph |
| 30 # > perf_cleanup | 33 # > perf_cleanup |
| 31 | 34 |
| 32 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" | 35 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" |
| 33 source "${SCRIPT_DIR}/utilities.sh" | 36 source "${SCRIPT_DIR}/utilities.sh" |
| (...skipping 19 matching lines...) Expand all Loading... |
| 53 | 56 |
| 54 # Ensure that user includes name of build directory (e.g. out/Release) as | 57 # Ensure that user includes name of build directory (e.g. out/Release) as |
| 55 # input parameter. Store path in BUILD_DIR. | 58 # input parameter. Store path in BUILD_DIR. |
| 56 if [[ "$#" -eq 1 ]]; then | 59 if [[ "$#" -eq 1 ]]; then |
| 57 if is_not_dir "$1"; then | 60 if is_not_dir "$1"; then |
| 58 error "$1 is invalid" | 61 error "$1 is invalid" |
| 59 return 1 | 62 return 1 |
| 60 fi | 63 fi |
| 61 BUILD_DIR="$1" | 64 BUILD_DIR="$1" |
| 62 else | 65 else |
| 63 unset BUILD_DIR | |
| 64 error "Missing required parameter". | 66 error "Missing required parameter". |
| 65 usage | 67 usage |
| 68 return 1 |
| 66 fi | 69 fi |
| 67 | 70 |
| 68 # Helper method to simpify usage of the simpleperf binary on the device. | |
| 69 function simpleperf_android() { | |
| 70 local simpleperf="${DEV_TMP_DIR}/simpleperf" | |
| 71 if [ ! -z "$1" ]; then | |
| 72 adb shell "${simpleperf}" "$@" | |
| 73 else | |
| 74 adb shell $simpleperf --help | |
| 75 fi | |
| 76 } | |
| 77 | |
| 78 # Full (relative) path to the libjingle_peerconnection_so.so file. | 71 # Full (relative) path to the libjingle_peerconnection_so.so file. |
| 79 function native_shared_lib_path() { | 72 function native_shared_lib_path() { |
| 80 echo "${BUILD_DIR}${NATIVE_LIB_PATH}" | 73 echo "${BUILD_DIR}${NATIVE_LIB_PATH}" |
| 81 } | 74 } |
| 82 | 75 |
| 83 # Target CPU architecture for the native shared library. | 76 # Target CPU architecture for the native shared library. |
| 84 # Example: AArch64. | 77 # Example: AArch64. |
| 85 function native_shared_lib_arch() { | 78 function native_shared_lib_arch() { |
| 86 readelf -h $(native_shared_lib_path) | grep Machine | awk '{print $2}' | 79 readelf -h $(native_shared_lib_path) | grep Machine | awk '{print $2}' |
| 87 } | 80 } |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 119 adb pull /proc/kallsyms "${symbol_cache}" | 112 adb pull /proc/kallsyms "${symbol_cache}" |
| 120 } 1> /dev/null | 113 } 1> /dev/null |
| 121 | 114 |
| 122 # Download the correct version of 'simpleperf' to $DEV_TMP_DIR | 115 # Download the correct version of 'simpleperf' to $DEV_TMP_DIR |
| 123 # on the device and enable profiling. | 116 # on the device and enable profiling. |
| 124 function copy_simpleperf_to_device() { | 117 function copy_simpleperf_to_device() { |
| 125 local perf_binary | 118 local perf_binary |
| 126 [[ $(dev_arch) == "aarch64" ]] \ | 119 [[ $(dev_arch) == "aarch64" ]] \ |
| 127 && perf_binary="/arm64/simpleperf" \ | 120 && perf_binary="/arm64/simpleperf" \ |
| 128 || perf_binary="/arm/simpleperf" | 121 || perf_binary="/arm/simpleperf" |
| 129 local simpleperf="${DEV_TMP_DIR}/simpleperf" | |
| 130 # Copy the simpleperf binary from local host to temp folder on device. | 122 # Copy the simpleperf binary from local host to temp folder on device. |
| 131 adb push "${SCRIPT_DIR}/simpleperf/bin/android${perf_binary}" \ | 123 adb push "${SCRIPT_DIR}/simpleperf/bin/android${perf_binary}" \ |
| 132 "${DEV_TMP_DIR}" 1> /dev/null | 124 "${DEV_TMP_DIR}" 1> /dev/null |
| 133 adb shell chmod a+x $simpleperf | 125 # Copy simpleperf from temp folder to the application package. |
| 126 adb shell run-as "${APP_NAME}" cp "${DEV_TMP_DIR}/simpleperf" . |
| 127 adb shell run-as "${APP_NAME}" chmod a+x simpleperf |
| 134 # Enable profiling on the device. | 128 # Enable profiling on the device. |
| 135 enable_profiling | 129 enable_profiling |
| 136 # Allows usage of running report commands on the device. | 130 # Allows usage of running report commands on the device. |
| 137 if image_is_root; then | 131 if image_is_root; then |
| 138 enable_report_symbols | 132 enable_report_symbols |
| 139 fi | 133 fi |
| 140 } | 134 } |
| 141 | 135 |
| 142 # Copy the recorded 'perf.data' file from the device to the current directory. | 136 # Copy the recorded 'perf.data' file from the device to the current directory. |
| 143 # TODO(henrika): add support for specifying the destination. | 137 # TODO(henrika): add support for specifying the destination. |
| 144 function pull_perf_data_from_device() { | 138 function pull_perf_data_from_device() { |
| 145 adb pull "${DEV_TMP_DIR}/perf.data" . | 139 adb shell run-as "${APP_NAME}" cp perf.data /sdcard/perf.data |
| 140 adb pull sdcard/perf.data . |
| 146 } 1> /dev/null | 141 } 1> /dev/null |
| 147 | 142 |
| 148 | 143 |
| 149 # Wraps calls to simpleperf report. Used by e.g. perf_report_threads. | 144 # Wraps calls to simpleperf report. Used by e.g. perf_report_threads. |
| 150 # A valid profile input file must exist in the current folder. | 145 # A valid profile input file must exist in the current folder. |
| 151 # TODO(henrika): possibly add support to add path to alternative input file. | 146 # TODO(henrika): possibly add support to add path to alternative input file. |
| 152 function perf_report() { | 147 function perf_report() { |
| 153 local perf_data="perf.data" | 148 local perf_data="perf.data" |
| 154 is_file "${perf_data}" \ | 149 is_file "${perf_data}" \ |
| 155 && simpleperf report \ | 150 && simpleperf report \ |
| (...skipping 29 matching lines...) Expand all Loading... |
| 185 # Lists the main available functions after sourcing this script. | 180 # Lists the main available functions after sourcing this script. |
| 186 function print_function_help() { | 181 function print_function_help() { |
| 187 printf "\nAvailable functions in this shell:\n" | 182 printf "\nAvailable functions in this shell:\n" |
| 188 printf " perf_record [duration, default=60sec]\n" | 183 printf " perf_record [duration, default=60sec]\n" |
| 189 printf " perf_report_threads\n" | 184 printf " perf_report_threads\n" |
| 190 printf " perf_report_bins\n" | 185 printf " perf_report_bins\n" |
| 191 printf " perf_report_symbols\n" | 186 printf " perf_report_symbols\n" |
| 192 printf " perf_report_graph\n" | 187 printf " perf_report_graph\n" |
| 193 printf " perf_report_graph_callee\n" | 188 printf " perf_report_graph_callee\n" |
| 194 printf " perf_update\n" | 189 printf " perf_update\n" |
| 195 printf " perf_clean\n" | 190 printf " perf_cleanup\n" |
| 196 printf " flame_graph\n" | 191 printf " flame_graph\n" |
| 197 printf " plot_flame_graph\n" | 192 printf " plot_flame_graph\n" |
| 198 } | 193 } |
| 199 | 194 |
| 200 function cleanup() { | 195 function cleanup() { |
| 201 unset -f main | 196 unset -f main |
| 202 } | 197 } |
| 203 | 198 |
| 204 # ----------------------------------------------------------------------------- | 199 # ----------------------------------------------------------------------------- |
| 205 # Main methods to be used after sourcing the main script. | 200 # Main methods to be used after sourcing the main script. |
| 206 # ----------------------------------------------------------------------------- | 201 # ----------------------------------------------------------------------------- |
| 207 | 202 |
| 208 # Call this method after the application as been rebuilt and installed on the | 203 # Call this method after the application as been rebuilt and installed on the |
| 209 # device to ensure that symbols are up-to-date. | 204 # device to ensure that symbols are up-to-date. |
| 210 function perf_update() { | 205 function perf_update() { |
| 211 copy_native_shared_library_to_symbol_cache | 206 copy_native_shared_library_to_symbol_cache |
| 212 copy_kernel_symbols_from_device_to_symbol_cache | 207 if image_is_root; then |
| 208 copy_kernel_symbols_from_device_to_symbol_cache |
| 209 fi |
| 213 } | 210 } |
| 214 | 211 |
| 215 # Record stack frame based call graphs while using the application. | 212 # Record stack frame based call graphs while using the application. |
| 216 # We use default events (cpu-cycles), and write records to 'perf.data' in the | 213 # We use default events (cpu-cycles), and write records to 'perf.data' in the |
| 217 # tmp folder on the device. Default duration is 60 seconds but it can be changed | 214 # tmp folder on the device. Default duration is 60 seconds but it can be changed |
| 218 # by adding one parameter. As soon as the recording is done, 'perf.data' is | 215 # by adding one parameter. As soon as the recording is done, 'perf.data' is |
| 219 # copied to the directory from which this method is called and a summary of | 216 # copied to the directory from which this method is called and a summary of |
| 220 # the load distribution per thread is printed. | 217 # the load distribution per thread is printed. |
| 221 function perf_record() { | 218 function perf_record() { |
| 222 if app_is_running "${APP_NAME}"; then | 219 if app_is_running "${APP_NAME}"; then |
| 223 # Ensure that the latest native shared library exists in the local cache. | 220 # Ensure that the latest native shared library exists in the local cache. |
| 224 copy_native_shared_library_to_symbol_cache | 221 copy_native_shared_library_to_symbol_cache |
| 225 local duration=60 | 222 local duration=60 |
| 226 if [ "$#" -eq 1 ]; then | 223 if [ "$#" -eq 1 ]; then |
| 227 duration="$1" | 224 duration="$1" |
| 228 fi | 225 fi |
| 229 local pid=$(find_app_pid "${APP_NAME}") | 226 local pid=$(find_app_pid "${APP_NAME}") |
| 230 echo "Profiling PID $pid for $duration seconds (media must be is active)..." | 227 echo "Profiling PID $pid for $duration seconds (media must be is active)..." |
| 231 local output_file="${DEV_TMP_DIR}/perf.data" | 228 adb shell run-as "${APP_NAME}" ./simpleperf record \ |
| 232 simpleperf_android record \ | |
| 233 --call-graph fp \ | 229 --call-graph fp \ |
| 234 -p "${pid}" \ | 230 -p "${pid}" \ |
| 235 -o $output_file \ | |
| 236 -f 1000 \ | 231 -f 1000 \ |
| 237 --duration "${duration}" \ | 232 --duration "${duration}" \ |
| 238 --log error | 233 --log error |
| 239 app_stop "${APP_NAME}" | |
| 240 # Copy profile results from device to current directory. | 234 # Copy profile results from device to current directory. |
| 241 pull_perf_data_from_device | 235 pull_perf_data_from_device |
| 242 # Print out a summary report (load per thread). | 236 # Print out a summary report (load per thread). |
| 243 perf_report_threads | tail -n +6 | 237 perf_report_threads | tail -n +6 |
| 244 else | 238 else |
| 245 # AppRTCMobile was not enabled. Start it up automatically and ask the user | 239 # AppRTCMobile was not enabled. Start it up automatically and ask the user |
| 246 # to start media and then call this method again. | 240 # to start media and then call this method again. |
| 247 warning "AppRTCMobile must be active" | 241 warning "AppRTCMobile must be active" |
| 248 app_start "${APP_NAME}" | 242 app_start "${APP_NAME}" |
| 249 echo "Start media and then call perf_record again..." | 243 echo "Start media and then call perf_record again..." |
| 250 fi 2> /dev/null | 244 fi |
| 251 } | 245 } |
| 252 | 246 |
| 253 # Analyze the profile report and show samples per threads. | 247 # Analyze the profile report and show samples per threads. |
| 254 function perf_report_threads() { | 248 function perf_report_threads() { |
| 255 perf_report --sort comm | 249 perf_report --sort comm |
| 256 } 2> /dev/null | 250 } 2> /dev/null |
| 257 | 251 |
| 258 # Analyze the profile report and show samples per binary. | 252 # Analyze the profile report and show samples per binary. |
| 259 function perf_report_bins() { | 253 function perf_report_bins() { |
| 260 perf_report --sort dso | 254 perf_report --sort dso |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 302 fi | 296 fi |
| 303 local file_name="flame_graph.svg" | 297 local file_name="flame_graph.svg" |
| 304 local title="WebRTC Flame Graph" | 298 local title="WebRTC Flame Graph" |
| 305 if [[ "$#" -eq 1 ]]; then | 299 if [[ "$#" -eq 1 ]]; then |
| 306 file_name="$1" | 300 file_name="$1" |
| 307 fi | 301 fi |
| 308 if [[ "$#" -eq 2 ]]; then | 302 if [[ "$#" -eq 2 ]]; then |
| 309 file_name="$1" | 303 file_name="$1" |
| 310 title="$2" | 304 title="$2" |
| 311 fi | 305 fi |
| 312 report_sample.py \ | 306 if image_is_not_root; then |
| 313 --symfs "${SYMBOL_DIR}" \ | 307 report_sample.py \ |
| 314 --kallsyms "${SYMBOL_DIR}/kallsyms" \ | 308 --symfs "${SYMBOL_DIR}" \ |
| 315 perf.data >out.perf | 309 perf.data >out.perf |
| 310 else |
| 311 report_sample.py \ |
| 312 --symfs "${SYMBOL_DIR}" \ |
| 313 --kallsyms "${SYMBOL_DIR}/kallsyms" \ |
| 314 perf.data >out.perf |
| 315 fi |
| 316 stackcollapse-perf.pl out.perf >out.folded | 316 stackcollapse-perf.pl out.perf >out.folded |
| 317 flamegraph.pl --title="${title}" out.folded >"${file_name}" | 317 flamegraph.pl --title="${title}" out.folded >"${file_name}" |
| 318 rm out.perf | 318 rm out.perf |
| 319 rm out.folded | 319 rm out.folded |
| 320 } | 320 } |
| 321 | 321 |
| 322 # Remove all downloaded third-party tools. | 322 # Remove all downloaded third-party tools. |
| 323 function perf_cleanup () { | 323 function perf_cleanup () { |
| 324 rm_simpleperf | 324 rm_simpleperf |
| 325 rm_flame_graph | 325 rm_flame_graph |
| (...skipping 28 matching lines...) Expand all Loading... |
| 354 fi | 354 fi |
| 355 ok "adb tool is working" | 355 ok "adb tool is working" |
| 356 | 356 |
| 357 # Exactly one Android device must be connected. | 357 # Exactly one Android device must be connected. |
| 358 if [[ ! one_device_connected ]]; then | 358 if [[ ! one_device_connected ]]; then |
| 359 error "one device must be connected" | 359 error "one device must be connected" |
| 360 return 1 | 360 return 1 |
| 361 fi | 361 fi |
| 362 ok "one device is connected via USB" | 362 ok "one device is connected via USB" |
| 363 | 363 |
| 364 # Ensure that the device is rooted. | 364 # Restart adb with root permissions if needed. |
| 365 if image_is_not_root; then | 365 if image_is_root && adb_has_no_root_permissions; then |
| 366 error "device is not rooted" | 366 adb root |
| 367 return 1 | 367 ok "adb is running as root" |
| 368 fi | 368 fi |
| 369 ok "device is rooted" | |
| 370 | |
| 371 # Restart adb with root permissions if needed. | |
| 372 if adb_has_no_root_permissions; then | |
| 373 adb root | |
| 374 fi | |
| 375 ok "adbd is running as root" | |
| 376 | 369 |
| 377 # Create an empty symbol cache in the tmp folder. | 370 # Create an empty symbol cache in the tmp folder. |
| 378 # TODO(henrika): it might not be required to start from a clean cache. | 371 # TODO(henrika): it might not be required to start from a clean cache. |
| 379 is_dir "${SYMBOL_DIR}" && rm -rf "${SYMBOL_DIR}" | 372 is_dir "${SYMBOL_DIR}" && rm -rf "${SYMBOL_DIR}" |
| 380 mkdir "${SYMBOL_DIR}" \ | 373 mkdir "${SYMBOL_DIR}" \ |
| 381 && ok "empty symbol cache created at ${SYMBOL_DIR}" \ | 374 && ok "empty symbol cache created at ${SYMBOL_DIR}" \ |
| 382 || error "failed to create symbol cache" | 375 || error "failed to create symbol cache" |
| 383 | 376 |
| 384 # Ensure that path to the native library with symbols is valid. | 377 # Ensure that path to the native library with symbols is valid. |
| 385 local native_lib=$(native_shared_lib_path) | 378 local native_lib=$(native_shared_lib_path) |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 461 | 454 |
| 462 cleanup | 455 cleanup |
| 463 | 456 |
| 464 return 0 | 457 return 0 |
| 465 } | 458 } |
| 466 | 459 |
| 467 # Only call main() if proper input parameter has been provided. | 460 # Only call main() if proper input parameter has been provided. |
| 468 if is_set $BUILD_DIR; then | 461 if is_set $BUILD_DIR; then |
| 469 main "$@" | 462 main "$@" |
| 470 fi | 463 fi |
| OLD | NEW |