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

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

Issue 2705063002: Adding support for native profiling of AppRTCMobile (Closed)
Patch Set: Added plot_flame_graph Created 3 years, 9 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 | « no previous file | 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
the sun 2017/02/24 10:31:06 I'm no bash expert, but I've learned that using se
henrika_webrtc 2017/02/24 11:54:05 Works in a clean script but this file must be sour
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 a rooted and attached Android device.
15 #
16 # Source this script once from the WebRTC source directory and resolve any
the sun 2017/02/24 10:31:06 Replace "WebRTC source directory" with webrtc/src/
kjellander_webrtc 2017/02/24 11:02:26 I'd suggest just WebRTC src/ directory (since what
henrika_webrtc 2017/02/24 11:54:05 Done.
henrika_webrtc 2017/02/24 11:54:05 Using proposal from kjellander ;-)
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 # Example usage:
25 #
26 # > . tools-webrtc/android/profiling/perf_setup.sh out/Release
27 # > perf_record
the sun 2017/02/24 10:31:06 perf_record 120 ?
henrika_webrtc 2017/02/24 11:54:05 As is, 30 will be used as default. But I can add a
28 # > flame_graph
29 # > plot_flame_graph
30 # > perf_cleanup
the sun 2017/02/24 10:31:06 Add a note about running perf_update after uploadi
henrika_webrtc 2017/02/24 11:54:05 It is actually no longer needed. I added it to per
31
32 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
33 source "${SCRIPT_DIR}/utilities.sh"
34
35 # Root directory for local symbol cache.
36 SYMBOL_DIR="${TMPDIR:-/tmp}/android_symbols"
37 # Used as a temporary folder on the Android device for data storage.
38 DEV_TMP_DIR="/data/local/tmp"
39 # Relative path to native shared library containing symbols.
40 NATIVE_LIB_PATH="/lib.unstripped/libjingle_peerconnection_so.so"
41 # Name of application package for the AppRTCMobile demo.
42 APP_NAME="org.appspot.apprtc"
43
44 # Make sure we're being sourced.
45 if [[ -n "${BASH_VERSION}" && "${BASH_SOURCE:-$0}" == "$0" ]]; then
46 error "perf_setup must be sourced"
47 exit 1
48 fi
49
50 function usage() {
51 printf "usage: . perf_setup.sh <build_dir>\n"
52 }
53
54 # Ensure that user includes name of build directory (e.g. out/Release) as
55 # input parameter. Store path in BUILD_DIR.
56 if [[ "$#" -eq 1 ]]; then
57 if is_not_dir "$1"; then
58 error "$1 is invalid"
59 return 1
60 fi
61 BUILD_DIR="$1"
62 else
63 unset BUILD_DIR
64 error "Missing required parameter".
65 usage
66 fi
67
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.
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 kernal symbols from device to symbol cache in tmp.
the sun 2017/02/24 10:31:06 kernal -> kernel
henrika_webrtc 2017/02/24 11:54:05 Done.
117 function copy_kernal_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 local simpleperf="${DEV_TMP_DIR}/simpleperf"
130 # Avoid copying to device if simpleperf already exists.
131 if [[ ! $(dev_ls "${simpleperf}") ]]; then
132 adb push "${SIMPLE_PERF_DIR}${perf_binary}" "${DEV_TMP_DIR}"
133 adb shell chmod a+x $simpleperf
134 fi
135 # Enable profiling on the device.
136 enable_profiling
137 # Allows usage of running report commands on the device.
138 enable_report_symbols
139 }
140
141 # Copy the recorded 'perf.data' file from the device to the current directory.
142 # TODO(henrika): add support for specifying the destination.
143 function pull_perf_data_from_device() {
144 adb pull "${DEV_TMP_DIR}/perf.data" .
145 } 1> /dev/null
146
147
148 # Wraps calls to simpleperf report. Used by e.g. perf_report_threads.
149 # A valid profile input file must exist in the current folder.
150 # TODO(henrika): possibly add support to add path to alternative input file.
151 function perf_report() {
152 local perf_data="perf.data"
153 is_file "${perf_data}" \
154 && simpleperf report \
155 -n \
156 -i "${perf_data}" \
157 "$@" \
158 || error "$(pwd)/${perf_data} is invalid"
159 }
160
161 # Removes the folder specified as input parameter. Mainly intended for removal
162 # of simpleperf and Flame Graph tools.
163 function remove_tool() {
164 local tool_dir="$1"
165 if is_dir "${tool_dir}"; then
166 echo "Removing ${tool_dir}..."
167 rm -rf "${tool_dir}"
168 path_remove "${tool_dir}"
169 fi
170 }
171
172 # Utility method which deletes the downloaded simpleperf tool from the repo.
173 # It also removes the simpleperf root folder from PATH.
174 function rm_simpleperf() {
175 remove_tool "${SCRIPT_DIR}/simpleperf"
176 }
177
178 # Utility method which deletes the downloaded Flame Graph tool from the repo.
179 # It also removes the Flame Graph root folder from PATH.
180 function rm_flame_graph() {
181 remove_tool "${SCRIPT_DIR}/flamegraph"
182 }
183
184 # Lists the main available functions after sourcing this script.
185 function print_function_help() {
186 printf "\nAvailable functions in this shell:\n"
187 printf " perf_record [duration, default=30sec]\n"
188 printf " perf_report_threads\n"
189 printf " perf_report_bins\n"
190 printf " perf_report_symbols\n"
191 printf " perf_report_graph\n"
192 printf " perf_report_graph_callee\n"
193 printf " perf_update\n"
194 printf " perf_clean\n"
195 printf " flame_graph\n"
196 printf " plot_flame_graph\n"
197 }
198
199 function cleanup() {
200 unset -f main
201 unset BUILD_DIR
202 }
203
204 # -----------------------------------------------------------------------------
205 # Main methods to be used after sourcing the main script.
206 # -----------------------------------------------------------------------------
207
208 # Call this method after the application as been rebuilt and installed on the
209 # device to ensure that symbols are up-to-date.
210 function perf_update() {
211 copy_native_shared_library_to_symbol_cache
212 copy_kernal_symbols_from_device_to_symbol_cache
213 }
214
215 # 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
217 # tmp folder on the device. Default duration is 30 seconds but it can be changed
218 # by adding one parameter. As soon as the recording is done, 'perf.data' is
219 # copied to the directoty from which this method is called and a summary of
220 # the load distribution per thread is printed.
221 function perf_record() {
222 if app_is_running "${APP_NAME}"; then
kjellander_webrtc 2017/02/24 11:02:26 I suggest setting set -e in the functions that exe
henrika_webrtc 2017/02/24 11:54:05 Discussed off-line. Resolved.
223 # Ensure that the latest native shared library exists in the local cache.
224 copy_native_shared_library_to_symbol_cache
225 local duration=30
226 if [ "$#" -eq 1 ]; then
227 duration="$1"
228 fi
229 local pid=$(find_app_pid "${APP_NAME}")
230 echo "Profiling PID $pid for $duration seconds (media must be is active)..."
231 local output_file="${DEV_TMP_DIR}/perf.data"
232 simpleperf_android record \
233 --call-graph fp \
234 -p "${pid}" \
235 -o $output_file \
236 -f 1000 \
237 --duration "${duration}" \
238 --log error
239 app_stop "${APP_NAME}"
240 # Copy profile results from device to current directory.
241 pull_perf_data_from_device
242 # Print out a summary report (load per thread).
243 perf_report_threads | tail -n +6
244 else
245 # AppRTCMobile was not enabled. Start it up automatically and ask the user
246 # to start media and then call this method again.
247 warning "AppRTCMobile must be active"
248 app_start "${APP_NAME}"
249 echo "Start media and then call perf_record again..."
250 fi 2> /dev/null
251 }
252
253 # Analyze the profile report and show samples per threads.
254 function perf_report_threads() {
255 perf_report --sort comm
256 } 2> /dev/null
257
258 # Analyze the profile report and show samples per binary.
259 function perf_report_bins() {
260 perf_report --sort dso
261 } 2> /dev/null
262
263 # Analyze the profile report and show samples per symbol.
264 function perf_report_symbols() {
265 perf_report --sort symbol --symfs "${SYMBOL_DIR}"
266 }
267
268 # Print call graph showing how functions call others.
269 function perf_report_graph() {
270 perf_report -g caller --symfs "${SYMBOL_DIR}"
271 }
the sun 2017/02/24 10:31:06 nit: blank line
henrika_webrtc 2017/02/24 11:54:05 Done.
272 # Print call graph showing how functions are called from others.
273 function perf_report_graph_callee() {
274 perf_report -g callee --symfs "${SYMBOL_DIR}"
275 }
276
277 # Plots the default Flame Graph file if no parameter is provided.
278 # If a parameter is given, it will be used as file name instead of the default.
279 function plot_flame_graph() {
280 local file_name="flame_graph.svg"
281 if [[ "$#" -eq 1 ]]; then
282 file_name="$1"
283 fi
284 # Open up the SVG file in Chrome. Try unstable first and revert to stable
285 # if unstable fails.
286 google-chrome-unstable "${file_name}" \
287 || google-chrome-stable "${file_name}" \
288 || error "failed to find any Chrome instance"
289 } 2> /dev/null
290
291 # Generate Flame Graph in interactive SVG format.
292 # First input parameter corresponds to output file name and second input
293 # parameter is the heading of the plot.
294 # Defaults will be utilized if parameters are not provided.
295 # See https://github.com/brendangregg/FlameGraph for details on Flame Graph.
296 function flame_graph() {
297 local perf_data="perf.data"
298 if is_not_file $perf_data; then
299 error "$(pwd)/${perf_data} is invalid"
300 return 1
301 fi
302 local file_name="flame_graph.svg"
303 local title="WebRTC Flame Graph"
304 if [[ "$#" -eq 1 ]]; then
305 file_name="$1"
306 fi
307 if [[ "$#" -eq 2 ]]; then
308 file_name="$1"
309 title="$2"
310 fi
311 report_sample.py \
312 --symfs "${SYMBOL_DIR}" \
313 --kallsyms "${SYMBOL_DIR}/kallsyms" \
314 perf.data >out.perf
315 stackcollapse-perf.pl out.perf >out.folded
316 flamegraph.pl --title="${title}" out.folded >"${file_name}"
317 rm out.perf
318 rm out.folded
319 }
320
321 # Remove all downloaded third-party tools.
322 function perf_cleanup () {
323 rm_simpleperf
324 rm_flame_graph
325 }
326
327 main() {
328 printf "%s\n" "Preparing profiling of AppRTCMobile on Android:"
329 # Verify that this script is called from the root folder of WebRTC,
330 # i.e., the src folder one step below where the .gclient file exists.
331 local -r project_root_dir=$(pwd)
332 local dir=${project_root_dir##*/}
333 if [[ "${dir}" != "src" ]]; then
334 error "script must be called from the WebRTC project root (src) folder"
335 return 1
336 fi
337 ok "project root: ${project_root_dir}"
338
339 # Verify that user has sourced envsetup.sh.
340 # TODO(henrika): might be possible to remove this check.
341 if [[ -z "$ENVSETUP_GYP_CHROME_SRC" ]]; then
342 error "must source envsetup script first"
343 return 1
344 fi
345 ok "envsetup script has been sourced"
346
347 # Given that envsetup is sourced, the adb tool should be accessible but
348 # do one extra check just in case.
349 local adb_full_path=$(which adb);
350 if [[ ! -x "${adb_full_path}" ]]; then
351 error "unable to find the Android Debug Bridge (adb) tool"
352 return 1
353 fi
354 ok "adb tool is working"
355
356 # Exactly one Android device must be connected.
357 if [[ ! one_device_connected ]]; then
358 error "one device must be connected"
359 return 1
360 fi
361 ok "one device is connected via USB"
362
363 # Ensure that the device is rooted.
364 if image_is_not_root; then
365 error "device is not rooted"
366 return 1
367 fi
368 ok "device is rooted"
369
370 # Restart adb with root permissions if needed.
371 if adb_has_no_root_permissions; then
372 adb root
373 fi
374 ok "adbd is running as root"
375
376 # Create an empty symbol cache in the tmp folder.
377 # TODO(henrika): it might not be required to start from a clean cache.
378 is_dir "${SYMBOL_DIR}" && rm -rf "${SYMBOL_DIR}"
379 mkdir "${SYMBOL_DIR}" \
380 && ok "empty symbol cache created at ${SYMBOL_DIR}" \
381 || error "failed to create symbol cache"
382
383 # Ensure that path to the native library with symbols is valid.
384 local native_lib=$(native_shared_lib_path)
385 if is_not_file ${native_lib}; then
386 error "${native_lib} is not a valid file"
387 return 1
388 fi
389 ok "native library: "${native_lib}""
390
391 # Verify that the architechture of the device matches the architecture
392 # of the native library.
393 if ! arch_is_ok; then
394 error "device is $(dev_arch) and lib is $(native_shared_lib_arch)"
395 return 1
396 fi
397 ok "device is $(dev_arch) and lib is $(native_shared_lib_arch)"
398
399 # Copy native shared library to symbol cache after creating an
400 # application specific tree structure under ${SYMBOL_DIR}/data.
401 copy_native_shared_library_to_symbol_cache
402 ok "native library copied to ${SYMBOL_DIR}/data/app/${APP_NAME}"
403
404 # Verify that the application is installed on the device.
405 if ! app_is_installed "${APP_NAME}"; then
406 error "${APP_NAME} is not installed on the device"
407 return 1
408 fi
409 ok "${APP_NAME} is installed on the device"
410
411 # Download simpleperf to <src>/tools-webrtc/android/profiling/simpleperf/.
kjellander_webrtc 2017/02/24 11:02:26 Please add this to .gitignore so one doesn't have
henrika_webrtc 2017/02/24 11:54:05 Good idea. Will do.
412 # Cloning will only take place if the target does not already exist.
413 # The PATH variable will also be updated.
414 # TODO(henrika): would it be better to use a target outside the WebRTC repo?
415 local simpleperf_dir="${SCRIPT_DIR}/simpleperf"
416 if is_not_dir "${simpleperf_dir}"; then
417 echo "Dowloading simpleperf..."
418 git clone https://android.googlesource.com/platform/prebuilts/simpleperf \
419 "${simpleperf_dir}"
420 chmod u+x "${simpleperf_dir}/report_sample.py"
421 fi
422 path_add "${simpleperf_dir}"
423 ok "${simpleperf_dir}" is added to PATH
424
425 # Update the PATH variable with the path to the Linux version of simpleperf.
426 local simpleperf_linux_dir="${SCRIPT_DIR}/simpleperf/bin/linux/x86_64/"
427 if is_not_dir "${simpleperf_linux_dir}"; then
428 error "${simpleperf_linux_dir} is invalid"
429 return 1
430 fi
431 path_add "${simpleperf_linux_dir}"
432 ok "${simpleperf_linux_dir}" is added to PATH
433
434 # Copy correct version (arm or arm64) of simpleperf to the device
435 # and enable profiling at the same time.
436 if ! copy_simpleperf_to_device; then
437 error "failed to install simpleperf on the device"
438 return 1
439 fi
440 ok "simpleperf is installed on the device"
441
442 # Refresh the symbol cache and read kernal symbols from device if not
443 # already done.
444 perf_update
445 ok "symbol cache is updated"
446
447 # Download Flame Graph to <src>/tools-webrtc/android/profiling/flamegraph/.
kjellander_webrtc 2017/02/24 11:02:26 add another .gitignore entry for this
henrika_webrtc 2017/02/24 11:54:05 Done.
448 # Cloning will only take place if the target does not already exist.
449 # The PATH variable will also be updated.
450 # TODO(henrika): would it be better to use a target outside the WebRTC repo?
451 local flamegraph_dir="${SCRIPT_DIR}/flamegraph"
452 if is_not_dir "${flamegraph_dir}"; then
453 echo "Dowloading Flame Graph visualization tool..."
454 git clone https://github.com/brendangregg/FlameGraph.git "${flamegraph_dir}"
455 fi
456 path_add "${flamegraph_dir}"
457 ok "${flamegraph_dir}" is added to PATH
458
459 print_function_help
460
461 cleanup
462
463 return 0
464 }
465
466 # Only call main() if proper input parameter has been provided.
467 if is_set $BUILD_DIR; then
468 main "$@"
469 fi
OLDNEW
« no previous file with comments | « no previous file | tools-webrtc/android/profiling/utilities.sh » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698