| #!/bin/bash | 
 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 | # | 
 | # Saves the gdb index for a given binary and its shared library dependencies. | 
 | # | 
 | # This will run gdb index in parallel on a number of binaries using SIGUSR1 | 
 | # as the communication mechanism to simulate a semaphore. Because of the | 
 | # nature of this technique, using "set -e" is very difficult. The SIGUSR1 | 
 | # terminates a "wait" with an error which we need to interpret. | 
 | # | 
 | # When modifying this code, most of the real logic is in the index_one_file | 
 | # function. The rest is cleanup + sempahore plumbing. | 
 |  | 
 | # Cleanup temp directory and ensure all child jobs are dead-dead. | 
 | function on_exit { | 
 |   trap "" EXIT USR1  # Avoid reentrancy. | 
 |  | 
 |   local jobs=$(jobs -p) | 
 |   if [ -n "$jobs" ]; then | 
 |     echo -n "Killing outstanding index jobs..." | 
 |     kill -KILL $(jobs -p) | 
 |     wait | 
 |     echo "done" | 
 |   fi | 
 |  | 
 |   if [ -f "$DIRECTORY" ]; then | 
 |     echo -n "Removing temp directory $DIRECTORY..." | 
 |     rm -rf $DIRECTORY | 
 |     echo done | 
 |   fi | 
 | } | 
 |  | 
 | # Add index to one binary. | 
 | function index_one_file { | 
 |   local file=$1 | 
 |   local basename=$(basename "$file") | 
 |   local should_index="${SHOULD_INDEX}" | 
 |  | 
 |   local readelf_out=$(${TOOLCHAIN_PREFIX}readelf -S "$file") | 
 |   if [[ $readelf_out =~ "gdb_index" ]]; then | 
 |     if [ "${REMOVE_INDEX}" = 1 ]; then | 
 |       ${TOOLCHAIN_PREFIX}objcopy --remove-section .gdb_index "$file" | 
 |       echo "Removed index from $basename." | 
 |     else | 
 |       echo "Skipped $basename -- already contains index." | 
 |       should_index=0 | 
 |     fi | 
 |   fi | 
 |  | 
 |   if [ "${should_index}" = 1 ]; then | 
 |     local start=$(date +"%s%N") | 
 |     echo "Adding index to $basename..." | 
 |  | 
 |     ${TOOLCHAIN_PREFIX}gdb -batch "$file" -ex "save gdb-index $DIRECTORY" \ | 
 |       -ex "quit" | 
 |     local index_file="$DIRECTORY/$basename.gdb-index" | 
 |     if [ -f "$index_file" ]; then | 
 |       ${TOOLCHAIN_PREFIX}objcopy --add-section .gdb_index="$index_file" \ | 
 |         --set-section-flags .gdb_index=readonly "$file" "$file" | 
 |       local finish=$(date +"%s%N") | 
 |       local elapsed=$(((finish - start)/1000000)) | 
 |       echo "   ...$basename indexed. [${elapsed}ms]" | 
 |     else | 
 |       echo "   ...$basename unindexable." | 
 |     fi | 
 |   fi | 
 | } | 
 |  | 
 | # Functions that when combined, concurrently index all files in FILES_TO_INDEX | 
 | # array. The global FILES_TO_INDEX is declared in the main body of the script. | 
 | function async_index { | 
 |   # Start a background subshell to run the index command. | 
 |   { | 
 |     index_one_file $1 | 
 |     kill -SIGUSR1 $$  # $$ resolves to the parent script. | 
 |     exit 129  # See comment above wait loop at bottom. | 
 |   } & | 
 | } | 
 |  | 
 | CUR_FILE_NUM=0 | 
 | function index_next { | 
 |   if (( CUR_FILE_NUM >= ${#FILES_TO_INDEX[@]} )); then | 
 |     return | 
 |   fi | 
 |  | 
 |   async_index "${FILES_TO_INDEX[CUR_FILE_NUM]}" | 
 |   ((CUR_FILE_NUM += 1)) || true | 
 | } | 
 |  | 
 |  | 
 | ######## | 
 | ### Main body of the script. | 
 |  | 
 | REMOVE_INDEX=0 | 
 | SHOULD_INDEX=1 | 
 | while getopts ":f:r" opt; do | 
 |   case $opt in | 
 |     f) | 
 |       REMOVE_INDEX=1 | 
 |       shift | 
 |       ;; | 
 |     r) | 
 |       REMOVE_INDEX=1 | 
 |       SHOULD_INDEX=0 | 
 |       shift | 
 |       ;; | 
 |     *) | 
 |       echo "Invalid option: -$OPTARG" >&2 | 
 |       ;; | 
 |   esac | 
 | done | 
 |  | 
 | if [[ ! $# == 1 ]]; then | 
 |   echo "Usage: $0 [-f] [-r] path-to-binary" | 
 |   echo "  -f forces replacement of an existing index." | 
 |   echo "  -r removes the index section." | 
 |   exit 1 | 
 | fi | 
 |  | 
 | FILENAME="$1" | 
 | if [[ ! -f "$FILENAME" ]]; then | 
 |   echo "Path $FILENAME does not exist." | 
 |   exit 1 | 
 | fi | 
 |  | 
 | # Ensure we cleanup on on exit. | 
 | trap on_exit EXIT | 
 |  | 
 | # We're good to go! Create temp directory for index files. | 
 | DIRECTORY=$(mktemp -d) | 
 | echo "Made temp directory $DIRECTORY." | 
 |  | 
 | # Create array with the filename and all shared libraries that | 
 | # have the same dirname. The dirname is a signal that these | 
 | # shared libraries were part of the same build as the binary. | 
 | declare -a FILES_TO_INDEX=($FILENAME | 
 |  $(ldd "$FILENAME" 2>/dev/null \ | 
 |   | grep $(dirname "$FILENAME") \ | 
 |   | sed "s/.*[ \t]\(.*\) (.*/\1/") | 
 | ) | 
 |  | 
 | # Start concurrent indexing. | 
 | trap index_next USR1 | 
 |  | 
 | # 4 is an arbitrary default. When changing, remember we are likely IO bound | 
 | # so basing this off the number of cores is not sensible. | 
 | INDEX_TASKS=${INDEX_TASKS:-4} | 
 | for ((i=0;i<${INDEX_TASKS};i++)); do | 
 |   index_next | 
 | done | 
 |  | 
 | # Do a wait loop. Bash waits that terminate due a trap have an exit | 
 | # code > 128. We also ensure that our subshell's "normal" exit occurs with | 
 | # an exit code > 128. This allows us to do consider a > 128 exit code as | 
 | # an indication that the loop should continue. Unfortunately, it also means | 
 | # we cannot use set -e since technically the "wait" is failing. | 
 | wait | 
 | while (( $? > 128 )); do | 
 |   wait | 
 | done |