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 |