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