Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: tools-webrtc/android/profiling/perf_setup.sh

Issue 2864213004: Rename tools-webrtc -> tools_webrtc (Closed)
Patch Set: REmove symlink Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools-webrtc/android/build_aar.py ('k') | tools-webrtc/android/profiling/utilities.sh » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/bin/bash
2
3 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
4 #
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
7 # tree. An additional intellectual property rights grant can be found
8 # in the file PATENTS. All contributing project authors may
9 # be found in the AUTHORS file in the root of the source tree.
10 #
11 # Usage:
12 #
13 # It is assumed that a release build of AppRTCMobile exists and has been
14 # installed on an Android device which supports USB debugging.
15 #
16 # Source this script once from the WebRTC src/ directory and resolve any
17 # reported issues. Add relative path to build directory as parameter.
18 # Required tools will be downloaded if they don't already exist.
19 #
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
22 # results.
23 #
24 # Note that, using a rooted device is recommended since it allows us to
25 # resolve kernel symbols (kallsyms) as well.
26 #
27 # Example usage:
28 #
29 # > . tools-webrtc/android/profiling/perf_setup.sh out/Release
30 # > perf_record 120
31 # > flame_graph
32 # > plot_flame_graph
33 # > perf_cleanup
34
35 if [ -n "$ZSH_VERSION" ]; then
36 # Running inside zsh.
37 SCRIPT_PATH="${(%):-%N}"
38 else
39 # Running inside something else (most likely bash).
40 SCRIPT_PATH="${BASH_SOURCE[0]}"
41 fi
42 SCRIPT_DIR="$(cd $(dirname "$SCRIPT_PATH") && pwd -P)"
43 source "${SCRIPT_DIR}/utilities.sh"
44
45 # Root directory for local symbol cache.
46 SYMBOL_DIR="${TMPDIR:-/tmp}/android_symbols"
47 # Used as a temporary folder on the Android device for data storage.
48 DEV_TMP_DIR="/data/local/tmp"
49 # Relative path to native shared library containing symbols.
50 NATIVE_LIB_PATH="/lib.unstripped/libjingle_peerconnection_so.so"
51 # Name of application package for the AppRTCMobile demo.
52 APP_NAME="org.appspot.apprtc"
53
54 # Make sure we're being sourced.
55 if [[ -n "${BASH_VERSION}" && "${BASH_SOURCE:-$0}" == "$0" ]]; then
56 error "perf_setup must be sourced"
57 exit 1
58 fi
59
60 function usage() {
61 printf "usage: . perf_setup.sh <build_dir>\n"
62 }
63
64 # Ensure that user includes name of build directory (e.g. out/Release) as
65 # input parameter. Store path in BUILD_DIR.
66 if [[ "$#" -eq 1 ]]; then
67 if is_not_dir "$1"; then
68 error "$1 is invalid"
69 return 1
70 fi
71 BUILD_DIR="$1"
72 else
73 error "Missing required parameter".
74 usage
75 return 1
76 fi
77
78 # Full (relative) path to the libjingle_peerconnection_so.so file.
79 function native_shared_lib_path() {
80 echo "${BUILD_DIR}${NATIVE_LIB_PATH}"
81 }
82
83 # Target CPU architecture for the native shared library.
84 # Example: AArch64.
85 function native_shared_lib_arch() {
86 readelf -h $(native_shared_lib_path) | grep Machine | awk '{print $2}'
87 }
88
89 # Returns true if the device architecture and the build target are the same.
90 function arch_is_ok() {
91 if [[ "$(dev_arch)" == "aarch64" ]] \
92 && [[ "$(native_shared_lib_arch)" == "AArch64" ]]; then
93 return 0
94 elif [[ "$(dev_arch)" == "aarch32" ]] \
95 && [[ "$(native_shared_lib_arch)" == "AArch32" ]]; then
96 return 0
97 else
98 return 1
99 fi
100 }
101
102 # Copies the native shared library from the local host to the symbol cache
103 # which is used by simpleperf as base when searching for symbols.
104 function copy_native_shared_library_to_symbol_cache() {
105 local arm_lib="arm"
106 if [[ "$(native_shared_lib_arch)" == "AArch64" ]]; then
107 arm_lib="arm64"
108 fi
109 for num in 1 2; do
110 local dir="${SYMBOL_DIR}/data/app/${APP_NAME}-${num}/lib/${arm_lib}"
111 mkdir -p "${dir}"
112 cp -u $(native_shared_lib_path) "${dir}"
113 done
114 }
115
116 # Copy kernel symbols from device to symbol cache in tmp.
117 function copy_kernel_symbols_from_device_to_symbol_cache() {
118 local symbol_cache="${SYMBOL_DIR}/kallsyms"
119 adb pull /proc/kallsyms "${symbol_cache}"
120 } 1> /dev/null
121
122 # Download the correct version of 'simpleperf' to $DEV_TMP_DIR
123 # on the device and enable profiling.
124 function copy_simpleperf_to_device() {
125 local perf_binary
126 [[ $(dev_arch) == "aarch64" ]] \
127 && perf_binary="/arm64/simpleperf" \
128 || perf_binary="/arm/simpleperf"
129 # Copy the simpleperf binary from local host to temp folder on device.
130 adb push "${SCRIPT_DIR}/simpleperf/bin/android${perf_binary}" \
131 "${DEV_TMP_DIR}" 1> /dev/null
132 # Copy simpleperf from temp folder to the application package.
133 adb shell run-as "${APP_NAME}" cp "${DEV_TMP_DIR}/simpleperf" .
134 adb shell run-as "${APP_NAME}" chmod a+x simpleperf
135 # Enable profiling on the device.
136 enable_profiling
137 # Allows usage of running report commands on the device.
138 if image_is_root; then
139 enable_report_symbols
140 fi
141 }
142
143 # Copy the recorded 'perf.data' file from the device to the current directory.
144 # TODO(henrika): add support for specifying the destination.
145 function pull_perf_data_from_device() {
146 adb shell run-as "${APP_NAME}" cp perf.data /sdcard/perf.data
147 adb pull sdcard/perf.data .
148 } 1> /dev/null
149
150
151 # Wraps calls to simpleperf report. Used by e.g. perf_report_threads.
152 # A valid profile input file must exist in the current folder.
153 # TODO(henrika): possibly add support to add path to alternative input file.
154 function perf_report() {
155 local perf_data="perf.data"
156 is_file "${perf_data}" \
157 && simpleperf report \
158 -n \
159 -i "${perf_data}" \
160 "$@" \
161 || error "$(pwd)/${perf_data} is invalid"
162 }
163
164 # Removes the folder specified as input parameter. Mainly intended for removal
165 # of simpleperf and Flame Graph tools.
166 function remove_tool() {
167 local tool_dir="$1"
168 if is_dir "${tool_dir}"; then
169 echo "Removing ${tool_dir}..."
170 rm -rf "${tool_dir}"
171 path_remove "${tool_dir}"
172 fi
173 }
174
175 # Utility method which deletes the downloaded simpleperf tool from the repo.
176 # It also removes the simpleperf root folder from PATH.
177 function rm_simpleperf() {
178 remove_tool "${SCRIPT_DIR}/simpleperf"
179 }
180
181 # Utility method which deletes the downloaded Flame Graph tool from the repo.
182 # It also removes the Flame Graph root folder from PATH.
183 function rm_flame_graph() {
184 remove_tool "${SCRIPT_DIR}/flamegraph"
185 }
186
187 # Lists the main available functions after sourcing this script.
188 function print_function_help() {
189 printf "\nAvailable functions in this shell:\n"
190 printf " perf_record [duration, default=60sec]\n"
191 printf " perf_report_threads\n"
192 printf " perf_report_bins\n"
193 printf " perf_report_symbols\n"
194 printf " perf_report_graph\n"
195 printf " perf_report_graph_callee\n"
196 printf " perf_update\n"
197 printf " perf_cleanup\n"
198 printf " flame_graph\n"
199 printf " plot_flame_graph\n"
200 }
201
202 function cleanup() {
203 unset -f main
204 }
205
206 # -----------------------------------------------------------------------------
207 # Main methods to be used after sourcing the main script.
208 # -----------------------------------------------------------------------------
209
210 # Call this method after the application as been rebuilt and installed on the
211 # device to ensure that symbols are up-to-date.
212 function perf_update() {
213 copy_native_shared_library_to_symbol_cache
214 if image_is_root; then
215 copy_kernel_symbols_from_device_to_symbol_cache
216 fi
217 }
218
219 # Record stack frame based call graphs while using the application.
220 # We use default events (cpu-cycles), and write records to 'perf.data' in the
221 # tmp folder on the device. Default duration is 60 seconds but it can be changed
222 # by adding one parameter. As soon as the recording is done, 'perf.data' is
223 # copied to the directory from which this method is called and a summary of
224 # the load distribution per thread is printed.
225 function perf_record() {
226 if app_is_running "${APP_NAME}"; then
227 # Ensure that the latest native shared library exists in the local cache.
228 copy_native_shared_library_to_symbol_cache
229 local duration=60
230 if [ "$#" -eq 1 ]; then
231 duration="$1"
232 fi
233 local pid=$(find_app_pid "${APP_NAME}")
234 echo "Profiling PID $pid for $duration seconds (media must be is active)..."
235 adb shell run-as "${APP_NAME}" ./simpleperf record \
236 --call-graph fp \
237 -p "${pid}" \
238 -f 1000 \
239 --duration "${duration}" \
240 --log error
241 # Copy profile results from device to current directory.
242 pull_perf_data_from_device
243 # Print out a summary report (load per thread).
244 perf_report_threads | tail -n +6
245 else
246 # AppRTCMobile was not enabled. Start it up automatically and ask the user
247 # to start media and then call this method again.
248 warning "AppRTCMobile must be active"
249 app_start "${APP_NAME}"
250 echo "Start media and then call perf_record again..."
251 fi
252 }
253
254 # Analyze the profile report and show samples per threads.
255 function perf_report_threads() {
256 perf_report --sort comm
257 } 2> /dev/null
258
259 # Analyze the profile report and show samples per binary.
260 function perf_report_bins() {
261 perf_report --sort dso
262 } 2> /dev/null
263
264 # Analyze the profile report and show samples per symbol.
265 function perf_report_symbols() {
266 perf_report --sort symbol --symfs "${SYMBOL_DIR}"
267 }
268
269 # Print call graph showing how functions call others.
270 function perf_report_graph() {
271 perf_report -g caller --symfs "${SYMBOL_DIR}"
272 }
273
274 # Print call graph showing how functions are called from others.
275 function perf_report_graph_callee() {
276 perf_report -g callee --symfs "${SYMBOL_DIR}"
277 }
278
279 # Plots the default Flame Graph file if no parameter is provided.
280 # If a parameter is given, it will be used as file name instead of the default.
281 function plot_flame_graph() {
282 local file_name="flame_graph.svg"
283 if [[ "$#" -eq 1 ]]; then
284 file_name="$1"
285 fi
286 # Open up the SVG file in Chrome. Try unstable first and revert to stable
287 # if unstable fails.
288 google-chrome-unstable "${file_name}" \
289 || google-chrome-stable "${file_name}" \
290 || error "failed to find any Chrome instance"
291 } 2> /dev/null
292
293 # Generate Flame Graph in interactive SVG format.
294 # First input parameter corresponds to output file name and second input
295 # parameter is the heading of the plot.
296 # Defaults will be utilized if parameters are not provided.
297 # See https://github.com/brendangregg/FlameGraph for details on Flame Graph.
298 function flame_graph() {
299 local perf_data="perf.data"
300 if is_not_file $perf_data; then
301 error "$(pwd)/${perf_data} is invalid"
302 return 1
303 fi
304 local file_name="flame_graph.svg"
305 local title="WebRTC Flame Graph"
306 if [[ "$#" -eq 1 ]]; then
307 file_name="$1"
308 fi
309 if [[ "$#" -eq 2 ]]; then
310 file_name="$1"
311 title="$2"
312 fi
313 if image_is_not_root; then
314 report_sample.py \
315 --symfs "${SYMBOL_DIR}" \
316 perf.data >out.perf
317 else
318 report_sample.py \
319 --symfs "${SYMBOL_DIR}" \
320 --kallsyms "${SYMBOL_DIR}/kallsyms" \
321 perf.data >out.perf
322 fi
323 stackcollapse-perf.pl out.perf >out.folded
324 flamegraph.pl --title="${title}" out.folded >"${file_name}"
325 rm out.perf
326 rm out.folded
327 }
328
329 # Remove all downloaded third-party tools.
330 function perf_cleanup () {
331 rm_simpleperf
332 rm_flame_graph
333 }
334
335 main() {
336 printf "%s\n" "Preparing profiling of AppRTCMobile on Android:"
337 # Verify that this script is called from the root folder of WebRTC,
338 # i.e., the src folder one step below where the .gclient file exists.
339 local -r project_root_dir=$(pwd)
340 local dir=${project_root_dir##*/}
341 if [[ "${dir}" != "src" ]]; then
342 error "script must be called from the WebRTC project root (src) folder"
343 return 1
344 fi
345 ok "project root: ${project_root_dir}"
346
347 # Verify that user has sourced envsetup.sh.
348 # TODO(henrika): might be possible to remove this check.
349 if [[ -z "$ENVSETUP_GYP_CHROME_SRC" ]]; then
350 error "must source envsetup script first"
351 return 1
352 fi
353 ok "envsetup script has been sourced"
354
355 # Given that envsetup is sourced, the adb tool should be accessible but
356 # do one extra check just in case.
357 local adb_full_path=$(which adb);
358 if [[ ! -x "${adb_full_path}" ]]; then
359 error "unable to find the Android Debug Bridge (adb) tool"
360 return 1
361 fi
362 ok "adb tool is working"
363
364 # Exactly one Android device must be connected.
365 if ! one_device_connected; then
366 error "one device must be connected"
367 return 1
368 fi
369 ok "one device is connected via USB"
370
371 # Restart adb with root permissions if needed.
372 if image_is_root && adb_has_no_root_permissions; then
373 adb root
374 ok "adb is running as root"
375 fi
376
377 # Create an empty symbol cache in the tmp folder.
378 # TODO(henrika): it might not be required to start from a clean cache.
379 is_dir "${SYMBOL_DIR}" && rm -rf "${SYMBOL_DIR}"
380 mkdir "${SYMBOL_DIR}" \
381 && ok "empty symbol cache created at ${SYMBOL_DIR}" \
382 || error "failed to create symbol cache"
383
384 # Ensure that path to the native library with symbols is valid.
385 local native_lib=$(native_shared_lib_path)
386 if is_not_file ${native_lib}; then
387 error "${native_lib} is not a valid file"
388 return 1
389 fi
390 ok "native library: "${native_lib}""
391
392 # Verify that the architechture of the device matches the architecture
393 # of the native library.
394 if ! arch_is_ok; then
395 error "device is $(dev_arch) and lib is $(native_shared_lib_arch)"
396 return 1
397 fi
398 ok "device is $(dev_arch) and lib is $(native_shared_lib_arch)"
399
400 # Copy native shared library to symbol cache after creating an
401 # application specific tree structure under ${SYMBOL_DIR}/data.
402 copy_native_shared_library_to_symbol_cache
403 ok "native library copied to ${SYMBOL_DIR}/data/app/${APP_NAME}"
404
405 # Verify that the application is installed on the device.
406 if ! app_is_installed "${APP_NAME}"; then
407 error "${APP_NAME} is not installed on the device"
408 return 1
409 fi
410 ok "${APP_NAME} is installed on the device"
411
412 # Download simpleperf to <src>/tools-webrtc/android/profiling/simpleperf/.
413 # Cloning will only take place if the target does not already exist.
414 # The PATH variable will also be updated.
415 # TODO(henrika): would it be better to use a target outside the WebRTC repo?
416 local simpleperf_dir="${SCRIPT_DIR}/simpleperf"
417 if is_not_dir "${simpleperf_dir}"; then
418 echo "Dowloading simpleperf..."
419 git clone https://android.googlesource.com/platform/prebuilts/simpleperf \
420 "${simpleperf_dir}"
421 chmod u+x "${simpleperf_dir}/report_sample.py"
422 fi
423 path_add "${simpleperf_dir}"
424 ok "${simpleperf_dir}" is added to PATH
425
426 # Update the PATH variable with the path to the Linux version of simpleperf.
427 local simpleperf_linux_dir="${SCRIPT_DIR}/simpleperf/bin/linux/x86_64/"
428 if is_not_dir "${simpleperf_linux_dir}"; then
429 error "${simpleperf_linux_dir} is invalid"
430 return 1
431 fi
432 path_add "${simpleperf_linux_dir}"
433 ok "${simpleperf_linux_dir}" is added to PATH
434
435 # Copy correct version (arm or arm64) of simpleperf to the device
436 # and enable profiling at the same time.
437 if ! copy_simpleperf_to_device; then
438 error "failed to install simpleperf on the device"
439 return 1
440 fi
441 ok "simpleperf is installed on the device"
442
443 # Refresh the symbol cache and read kernal symbols from device if not
444 # already done.
445 perf_update
446 ok "symbol cache is updated"
447
448 # Download Flame Graph to <src>/tools-webrtc/android/profiling/flamegraph/.
449 # Cloning will only take place if the target does not already exist.
450 # The PATH variable will also be updated.
451 # TODO(henrika): would it be better to use a target outside the WebRTC repo?
452 local flamegraph_dir="${SCRIPT_DIR}/flamegraph"
453 if is_not_dir "${flamegraph_dir}"; then
454 echo "Dowloading Flame Graph visualization tool..."
455 git clone https://github.com/brendangregg/FlameGraph.git "${flamegraph_dir}"
456 fi
457 path_add "${flamegraph_dir}"
458 ok "${flamegraph_dir}" is added to PATH
459
460 print_function_help
461
462 cleanup
463
464 return 0
465 }
466
467 # Only call main() if proper input parameter has been provided.
468 if is_set $BUILD_DIR; then
469 main "$@"
470 fi
OLDNEW
« no previous file with comments | « tools-webrtc/android/build_aar.py ('k') | tools-webrtc/android/profiling/utilities.sh » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698