OLD | NEW |
---|---|
(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 a rooted and attached Android device. | |
15 # | |
16 # Source this script once from the WebRTC source directory and resolve any | |
17 # reported issues. Add relative path to build directory as parameter. | |
18 # | |
19 # Once all tests are passed, a list of available functions will be listed. | |
20 # Use these functions to do the actual profiling and visualization of the | |
21 # results. | |
22 # | |
23 # Example: | |
24 # | |
25 # >> . tools-webrtc/android/profiling/perf_setup.sh out/Release | |
26 # >> perf_record 60 | |
27 # >> perf_report_symbols | |
28 # | |
29 # TODO(henrika): add more here for Flame Graph plots... | |
30 | |
31 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" | |
32 source "${SCRIPT_DIR}/utilities.sh" | |
33 | |
34 # Root directory for local symbol cache. | |
35 SYMBOL_DIR="${TMPDIR:-/tmp}/android_symbols" | |
36 # Root directory to prebuilt (Android and Linux) versions of simpleperf. | |
37 SIMPLE_PERF_ROOT_DIR="${SCRIPT_DIR}/simpleperf/bin" | |
38 # Used as a temporary folder on the Android device for data storage. | |
39 DEV_TMP_DIR="/data/local/tmp" | |
40 # Relative path to native shared library containing symbols. | |
41 NATIVE_LIB_PATH="/lib.unstripped/libjingle_peerconnection_so.so" | |
42 # Name of application package for the AppRTCMobile demo. | |
43 APP_NAME="org.appspot.apprtc" | |
44 | |
45 # Make sure we're being sourced. | |
46 if [[ -n "${BASH_VERSION}" && "${BASH_SOURCE:-$0}" == "$0" ]]; then | |
47 error "perf_setup must be sourced!" | |
48 exit 1 | |
49 fi | |
50 | |
51 function usage() { | |
52 printf "usage: . perf_setup.sh <build_dir>\n" | |
53 printf "Example: . perf_setup.sh out/Release\n" | |
54 } | |
55 | |
56 # Ensure that user includes name of build directory (e.g. out/Release) as | |
57 # input parameter. Store path in BUILD_DIR. | |
58 if [[ "$#" -eq 1 ]]; then | |
59 BUILD_DIR="$1" | |
60 else | |
61 unset BUILD_DIR | |
kjellander_webrtc
2017/02/22 14:46:57
Why unset it?
henrika_webrtc
2017/02/22 15:47:11
Long story. But essentially. If I don't do it, we
kjellander_webrtc
2017/02/24 11:02:25
Acknowledged.
| |
62 error "Missing required parameter". | |
63 usage | |
64 fi | |
65 | |
66 # Helper method to simpify usage of the simpleperf binary on the device. | |
67 function simpleperf_android() { | |
68 local simpleperf="${DEV_TMP_DIR}/simpleperf" | |
69 if [ ! -z "$1" ]; then | |
70 adb shell "${simpleperf}" "$@" | |
71 else | |
72 adb shell $simpleperf --help | |
73 fi | |
74 } | |
75 | |
76 # Full (relative) path to the libjingle_peerconnection_so.so file. | |
77 function native_shared_lib_path() { | |
78 echo "${BUILD_DIR}${NATIVE_LIB_PATH}" | |
79 } | |
80 | |
81 # Target CPU architecture for the native shared library. | |
82 # Example: AArch64. | |
83 function native_shared_lib_arch() { | |
84 readelf -h $(native_shared_lib_path) | grep Machine | awk '{print $2}' | |
85 } | |
86 | |
87 # Returns true if the device architecture and the build target are the same. | |
88 function arch_is_ok() { | |
89 if [[ "$(dev_arch)" == "aarch64" ]] \ | |
90 && [[ "$(native_shared_lib_arch)" == "AArch64" ]]; then | |
91 return 0 | |
92 elif [[ "$(dev_arch)" == "aarch32" ]] \ | |
93 && [[ "$(native_shared_lib_arch)" == "AArch32" ]]; then | |
94 return 0 | |
95 else | |
96 return 1 | |
97 fi | |
98 } | |
99 | |
100 # Copies the native shared library from the local host to the symbol cache | |
101 # which is used by simpleperf as base when searching for symbols. | |
102 function copy_native_shared_library_to_symbol_cache() { | |
103 local arm_lib="arm" | |
104 if [ "$(native_shared_lib_arch)" == "AArch64" ]; then | |
105 arm_lib="arm64" | |
106 fi | |
107 for num in 1 2; do | |
108 local dir="${SYMBOL_DIR}/data/app/${APP_NAME}-${num}/lib/${arm_lib}" | |
109 mkdir -p "${dir}" | |
110 cp -u $(native_shared_lib_path) "${dir}" | |
111 done | |
112 } | |
113 | |
114 # Copy kernal symbols from device to symbol cache in tmp. | |
115 function copy_kernal_symbols_from_device_to_symbol_cache() { | |
116 local symbol_cache="${SYMBOL_DIR}/kallsyms" | |
117 adb pull /proc/kallsyms "${symbol_cache}" | |
118 } 1> /dev/null | |
119 | |
120 # Download the correct version of 'simpleperf' to $DEV_TMP_DIR | |
121 # on the device and enable profiling. | |
122 function copy_simpleperf_to_device() { | |
123 local perf_binary | |
124 [[ $(dev_arch) == "aarch64" ]] \ | |
125 && perf_binary="/arm64/simpleperf" \ | |
126 || perf_binary="/arm/simpleperf" | |
127 local simpleperf="${DEV_TMP_DIR}/simpleperf" | |
128 # Avoid copying to device if simpleperf already exists. | |
129 if [[ ! $(dev_ls "${simpleperf}") ]]; then | |
130 adb push "${SIMPLE_PERF_DIR}${perf_binary}" "${DEV_TMP_DIR}" | |
131 adb shell chmod a+x $simpleperf | |
132 fi | |
133 # Enable profiling on the device. | |
134 enable_profiling | |
135 # Allows usgage of running report commands on the device. | |
kjellander_webrtc
2017/02/22 14:46:57
usgage -> usage?
henrika_webrtc
2017/02/22 15:47:11
Done.
| |
136 enable_report_symbols | |
137 } | |
138 | |
139 # Copy the recorded 'perf.data' file from the device to the current directory. | |
140 # TODO(henrika): add support for specifying the destination. | |
141 function pull_perf_data_from_device() { | |
142 adb pull "${DEV_TMP_DIR}/perf.data" . | |
143 } 1> /dev/null | |
144 | |
145 | |
146 # Wraps calls to simpleperf report. Used by e.g. perf_report_threads. | |
147 # A valid profile input file must exist in the current folder. | |
148 # TODO(henrika): possibly add support to add path to alternative input file. | |
149 function perf_report() { | |
150 local perf_data="perf.data" | |
151 is_file "${perf_data}" \ | |
152 && simpleperf report \ | |
153 -n \ | |
154 -i "${perf_data}" \ | |
155 "$@" \ | |
156 || error "$(pwd)/${perf_data} is invalid!" | |
157 } | |
158 | |
159 # Lists the main available functions after sourcing this script. | |
160 function print_function_help() { | |
161 printf "\nAvailable functions:\n" | |
162 printf " perf_record [duration, default=30sec]\n" | |
163 printf " perf_report_threads\n" | |
164 printf " perf_report_bins\n" | |
165 printf " perf_report_symbols\n" | |
166 printf " perf_update\n" | |
167 } | |
168 | |
169 function cleanup() { | |
170 unset -f main | |
171 } | |
172 | |
173 # ----------------------------------------------------------------------------- | |
174 # Main methods to be used after sourcing the main script. | |
175 # ----------------------------------------------------------------------------- | |
176 | |
177 # Call this method after the application as been rebuilt and installed on the | |
178 # device to ensure that symbols are up-to-date. | |
179 function perf_update() { | |
180 copy_native_shared_library_to_symbol_cache | |
181 copy_kernal_symbols_from_device_to_symbol_cache | |
182 } | |
183 | |
184 # Record stack frame based call graphs while using the application. | |
185 # We use default events (cpu-cycles), and write records to 'perf.data' in the | |
186 # tmp folder on the device. Default duration is 30 seconds but it can be changed | |
187 # by adding one parameter. As soon as the recording is done, 'perf.data' is | |
188 # copied to the directoty from which this method is called. | |
189 function perf_record() { | |
190 if app_is_running "${APP_NAME}"; then | |
191 # Ensure that the latest native shared library exists in the local cache. | |
192 copy_native_shared_library_to_symbol_cache | |
193 local duration=30 | |
194 if [ "$#" -eq 1 ]; then | |
195 duration="$1" | |
196 fi | |
197 local pid=$(find_app_pid "${APP_NAME}") | |
198 echo "Profiling PID $pid for $duration seconds (media must be is active)..." | |
199 local output_file="${DEV_TMP_DIR}/perf.data" | |
200 simpleperf_android record \ | |
201 --call-graph fp \ | |
202 -p "${pid}" \ | |
203 -o $output_file \ | |
204 -f 1000 \ | |
205 --duration "${duration}" \ | |
206 --log error | |
207 app_stop "${APP_NAME}" | |
208 # Copy profile results from device to current directory. | |
209 pull_perf_data_from_device | |
210 # Print out a summary report (load per thread). | |
211 perf_report_threads | tail -n +6 | |
212 else | |
213 # AppRTCMobile was not enabled. Start it up automatically and ask the user | |
214 # to start media and then call this method again. | |
215 warning "AppRTCMobile must be active" | |
216 app_start "${APP_NAME}" | |
217 echo "Start media and then call perf_record() again..." | |
218 fi 2> /dev/null | |
219 } | |
220 | |
221 # Analyze the profile report and show samples per threads. | |
222 function perf_report_threads() { | |
223 perf_report --sort comm | |
224 } 2> /dev/null | |
225 | |
226 # Analyze the profile report and show samples per binary. | |
227 function perf_report_bins() { | |
228 perf_report --sort dso | |
229 } 2> /dev/null | |
230 | |
231 # Analyze the profile report and show samples per symbol. | |
232 function perf_report_symbols() { | |
233 perf_report --sort symbol --symfs "${SYMBOL_DIR}" | |
234 } | |
235 | |
236 # ----------------------------------------------------------------------------- | |
237 # TODO(henrika): add comments... | |
kjellander_webrtc
2017/02/22 14:46:57
Which ones are you referring to? I think it's pret
henrika_webrtc
2017/02/22 15:47:10
Removed.
| |
238 # ----------------------------------------------------------------------------- | |
239 | |
240 main() { | |
241 printf "%s\n" "Preparing profiling of AppRTCMobile on Android:" | |
242 # Verify that this script is called from the root folder of WebRTC, | |
243 # i.e., the src folder one step below where the .gclient file exists. | |
244 local -r project_root_dir=$(pwd) | |
245 local dir=${project_root_dir##*/} | |
246 if [[ "${dir}" != "src" ]]; then | |
247 error "script must be called from the WebRTC project root (src) folder" | |
248 return 1 | |
249 fi | |
250 ok "project root: ${project_root_dir}" | |
251 | |
252 # Verify that user has sourced envsetup.sh. | |
253 # TODO(henrika): might be possible to remove this check. | |
254 if [[ -z "$ENVSETUP_GYP_CHROME_SRC" ]]; then | |
kjellander_webrtc
2017/02/22 14:46:57
This sounds like something that might go away. May
henrika_webrtc
2017/02/22 15:47:11
I have not found any other way actually. It works
| |
255 error "must source envsetup script first" | |
256 return 1 | |
257 fi | |
258 ok "envsetup script has been sourced" | |
259 | |
260 # Given that envsetup is sourced, the adb tool should be accessible but | |
261 # do one extra check just in case. | |
262 local adb_full_path=$(which adb); | |
263 if [[ ! -x "${adb_full_path}" ]]; then | |
264 error "unable to find the Android Debug Bridge (adb) tool" | |
265 return 1 | |
266 fi | |
267 ok "adb tool is working" | |
268 | |
269 # Exactly one Android device must be connected. | |
270 if [[ ! one_device_connected ]]; then | |
271 error "one device must be connected!" | |
272 return 1 | |
273 fi | |
274 ok "one device is connected via USB" | |
275 | |
276 # Ensure that the device is rooted. | |
277 if image_is_not_root; then | |
278 error "device is not rooted!" | |
279 return 1 | |
280 fi | |
281 ok "device is rooted" | |
282 | |
283 # Restart adb with root permissions if needed. | |
284 if adb_has_no_root_permissions; then | |
285 adb root | |
286 fi | |
287 ok "adbd is running as root" | |
288 | |
289 # Create an empty symbol cache in the tmp folder. | |
290 # TODO(henrika): it might not be required to start from a clean cache. | |
291 is_dir "${SYMBOL_DIR}" && rm -rf "${SYMBOL_DIR}" | |
292 mkdir "${SYMBOL_DIR}" \ | |
293 && ok "empty symbol cache created at ${SYMBOL_DIR}" \ | |
294 || error "failed to create symbol cache" | |
295 | |
296 # Ensure that path to the native library with symbols is valid. | |
297 local native_lib=$(native_shared_lib_path) | |
298 if is_not_file ${native_lib}; then | |
299 error "${native_lib} is not a valid file" | |
300 return 1 | |
301 fi | |
302 ok "native library: "${native_lib}"" | |
303 | |
304 # Verify that the architechture of the device matches the architecture | |
305 # of the native library. | |
306 if ! arch_is_ok; then | |
307 error "device is $(dev_arch) and lib is $(native_shared_lib_arch)" | |
308 return 1 | |
309 fi | |
310 ok "device is $(dev_arch) and lib is $(native_shared_lib_arch)" | |
311 | |
312 # Copy native shared library to symbol cache after creating an | |
313 # application specific tree structure under ${SYMBOL_DIR}/data. | |
314 copy_native_shared_library_to_symbol_cache | |
315 ok "native library copied to ${SYMBOL_DIR}/data/app/${APP_NAME}" | |
316 | |
317 # Verify that the application is installed on the device. | |
318 if ! app_is_installed "${APP_NAME}"; then | |
319 error "${APP_NAME} is not installed on the device" | |
320 return 1 | |
321 fi | |
322 ok "${APP_NAME} is installed on the device" | |
323 | |
324 # Ensure that folder for simpleperf executables (Android and Linux) exist | |
325 # as it should relative to the working directory (source of WebRTC). | |
326 if is_not_dir "${SIMPLE_PERF_ROOT_DIR}"; then | |
327 error "${SIMPLE_PERF_ROOT_DIR} is invalid" | |
328 return 1 | |
329 fi | |
330 ok "${SIMPLE_PERF_ROOT_DIR} is valid" | |
331 | |
332 # Update the PATH variable with the path to the Linux version of simpleperf. | |
333 local simpleperf_linux_dir="${SCRIPT_DIR}/simpleperf/bin/linux/x86_64/" | |
334 if is_not_dir "${simpleperf_linux_dir}"; then | |
335 error "${simpleperf_linux_dir} is invalid" | |
336 return 1 | |
337 fi | |
338 path_update "${simpleperf_linux_dir}" | |
339 ok "${simpleperf_linux_dir}" is added to PATH | |
340 | |
341 # Copy correct version (arm or arm64) of simpleperf to the device | |
342 # and enable profiling at the same time. | |
343 if ! copy_simpleperf_to_device; then | |
344 error "failed to install simpleperf on the device" | |
345 return 1 | |
346 fi | |
347 ok "simpleperf is installed on the device" | |
348 | |
349 # Refresh the symbol cache as last step. Now also reading kernal symbols | |
350 # from device if not already done. | |
351 perf_update | |
352 ok "symbol cache is updated" | |
353 | |
354 print_function_help | |
355 | |
356 cleanup | |
357 | |
358 return 0 | |
359 } | |
360 | |
361 # Only call main() if proper input parameter has been provided. | |
362 if is_set $BUILD_DIR; then | |
363 main "$@" | |
364 fi | |
OLD | NEW |