| 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 |