| OLD | NEW |
| 1 #!/bin/bash | 1 #!/bin/bash |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 # | 5 # |
| 6 # Saves the gdb index for a given binary and its shared library dependencies. | 6 # Saves the gdb index for a given binary and its shared library dependencies. |
| 7 # | 7 # |
| 8 # This will run gdb index in parallel on a number of binaries using SIGUSR1 | 8 # This will run gdb index in parallel on a number of binaries using SIGUSR1 |
| 9 # as the communication mechanism to simulate a semaphore. Because of the | 9 # as the communication mechanism to simulate a semaphore. Because of the |
| 10 # nature of this technique, using "set -e" is very difficult. The SIGUSR1 | 10 # nature of this technique, using "set -e" is very difficult. The SIGUSR1 |
| 11 # terminates a "wait" with an error which we need to interpret. | 11 # terminates a "wait" with an error which we need to interpret. |
| 12 # | 12 # |
| 13 # When modifying this code, most of the real logic is in the index_one_file | 13 # When modifying this code, most of the real logic is in the index_one_file |
| 14 # function. The rest is cleanup + sempahore plumbing. | 14 # function. The rest is cleanup + sempahore plumbing. |
| 15 | 15 |
| 16 function usage_exit { |
| 17 echo "Usage: $0 [-f] [-r] [-n] <paths-to-binaries>..." |
| 18 echo " -f forces replacement of an existing index." |
| 19 echo " -r removes the index section." |
| 20 echo " -n don't extract the dependencies of each binary with lld." |
| 21 echo " e.g., $0 -n out/Debug/lib.unstripped/lib*" |
| 22 echo |
| 23 echo " Set TOOLCHAIN_PREFIX to use a non-default set of binutils." |
| 24 exit 1 |
| 25 } |
| 26 |
| 16 # Cleanup temp directory and ensure all child jobs are dead-dead. | 27 # Cleanup temp directory and ensure all child jobs are dead-dead. |
| 17 function on_exit { | 28 function on_exit { |
| 18 trap "" EXIT USR1 # Avoid reentrancy. | 29 trap "" EXIT USR1 # Avoid reentrancy. |
| 19 | 30 |
| 20 local jobs=$(jobs -p) | 31 local jobs=$(jobs -p) |
| 21 if [ -n "$jobs" ]; then | 32 if [ -n "$jobs" ]; then |
| 22 echo -n "Killing outstanding index jobs..." | 33 echo -n "Killing outstanding index jobs..." |
| 23 kill -KILL $(jobs -p) | 34 kill -KILL $(jobs -p) |
| 24 wait | 35 wait |
| 25 echo "done" | 36 echo "done" |
| 26 fi | 37 fi |
| 27 | 38 |
| 28 if [ -f "$DIRECTORY" ]; then | 39 if [ -f "$directory" ]; then |
| 29 echo -n "Removing temp directory $DIRECTORY..." | 40 echo -n "Removing temp directory $directory..." |
| 30 rm -rf $DIRECTORY | 41 rm -rf "$directory" |
| 31 echo done | 42 echo done |
| 32 fi | 43 fi |
| 33 } | 44 } |
| 34 | 45 |
| 35 # Add index to one binary. | 46 # Add index to one binary. |
| 36 function index_one_file { | 47 function index_one_file { |
| 37 local file=$1 | 48 local file=$1 |
| 38 local basename=$(basename "$file") | 49 local basename=$(basename "$file") |
| 39 local should_index="${SHOULD_INDEX}" | 50 local should_index_this_file="${should_index}" |
| 40 | 51 |
| 41 local readelf_out=$(${TOOLCHAIN_PREFIX}readelf -S "$file") | 52 local readelf_out=$(${TOOLCHAIN_PREFIX}readelf -S "$file") |
| 42 if [[ $readelf_out =~ "gdb_index" ]]; then | 53 if [[ $readelf_out =~ "gdb_index" ]]; then |
| 43 if [ "${REMOVE_INDEX}" = 1 ]; then | 54 if $remove_index; then |
| 44 ${TOOLCHAIN_PREFIX}objcopy --remove-section .gdb_index "$file" | 55 ${TOOLCHAIN_PREFIX}objcopy --remove-section .gdb_index "$file" |
| 45 echo "Removed index from $basename." | 56 echo "Removed index from $basename." |
| 46 else | 57 else |
| 47 echo "Skipped $basename -- already contains index." | 58 echo "Skipped $basename -- already contains index." |
| 48 should_index=0 | 59 should_index_this_file=false |
| 49 fi | 60 fi |
| 50 fi | 61 fi |
| 51 | 62 |
| 52 if [ "${should_index}" = 1 ]; then | 63 if $should_index_this_file; then |
| 53 local start=$(date +"%s%N") | 64 local start=$(date +"%s%N") |
| 54 echo "Adding index to $basename..." | 65 echo "Adding index to $basename..." |
| 55 | 66 |
| 56 ${TOOLCHAIN_PREFIX}gdb -batch "$file" -ex "save gdb-index $DIRECTORY" \ | 67 ${TOOLCHAIN_PREFIX}gdb -batch "$file" -ex "save gdb-index $directory" \ |
| 57 -ex "quit" | 68 -ex "quit" |
| 58 local index_file="$DIRECTORY/$basename.gdb-index" | 69 local index_file="$directory/$basename.gdb-index" |
| 59 if [ -f "$index_file" ]; then | 70 if [ -f "$index_file" ]; then |
| 60 ${TOOLCHAIN_PREFIX}objcopy --add-section .gdb_index="$index_file" \ | 71 ${TOOLCHAIN_PREFIX}objcopy --add-section .gdb_index="$index_file" \ |
| 61 --set-section-flags .gdb_index=readonly "$file" "$file" | 72 --set-section-flags .gdb_index=readonly "$file" "$file" |
| 62 local finish=$(date +"%s%N") | 73 local finish=$(date +"%s%N") |
| 63 local elapsed=$(((finish - start)/1000000)) | 74 local elapsed=$(((finish - start) / 1000000)) |
| 64 echo " ...$basename indexed. [${elapsed}ms]" | 75 echo " ...$basename indexed. [${elapsed}ms]" |
| 65 else | 76 else |
| 66 echo " ...$basename unindexable." | 77 echo " ...$basename unindexable." |
| 67 fi | 78 fi |
| 68 fi | 79 fi |
| 69 } | 80 } |
| 70 | 81 |
| 71 # Functions that when combined, concurrently index all files in FILES_TO_INDEX | 82 # Functions that when combined, concurrently index all files in FILES_TO_INDEX |
| 72 # array. The global FILES_TO_INDEX is declared in the main body of the script. | 83 # array. The global FILES_TO_INDEX is declared in the main body of the script. |
| 73 function async_index { | 84 function async_index { |
| 74 # Start a background subshell to run the index command. | 85 # Start a background subshell to run the index command. |
| 75 { | 86 { |
| 76 index_one_file $1 | 87 index_one_file $1 |
| 77 kill -SIGUSR1 $$ # $$ resolves to the parent script. | 88 kill -SIGUSR1 $$ # $$ resolves to the parent script. |
| 78 exit 129 # See comment above wait loop at bottom. | 89 exit 129 # See comment above wait loop at bottom. |
| 79 } & | 90 } & |
| 80 } | 91 } |
| 81 | 92 |
| 82 CUR_FILE_NUM=0 | 93 cur_file_num=0 |
| 83 function index_next { | 94 function index_next { |
| 84 if (( CUR_FILE_NUM >= ${#FILES_TO_INDEX[@]} )); then | 95 if ((cur_file_num >= ${#files_to_index[@]})); then |
| 85 return | 96 return |
| 86 fi | 97 fi |
| 87 | 98 |
| 88 async_index "${FILES_TO_INDEX[CUR_FILE_NUM]}" | 99 async_index "${files_to_index[cur_file_num]}" |
| 89 ((CUR_FILE_NUM += 1)) || true | 100 ((cur_file_num += 1)) || true |
| 90 } | 101 } |
| 91 | 102 |
| 92 | |
| 93 ######## | 103 ######## |
| 94 ### Main body of the script. | 104 ### Main body of the script. |
| 95 | 105 |
| 96 REMOVE_INDEX=0 | 106 remove_index=false |
| 97 SHOULD_INDEX=1 | 107 should_index=true |
| 98 while getopts ":f:r" opt; do | 108 should_index_deps=true |
| 99 case $opt in | 109 files_to_index=() |
| 100 f) | 110 while (($# > 0)); do |
| 101 REMOVE_INDEX=1 | 111 case "$1" in |
| 102 shift | 112 -h) |
| 113 usage_exit |
| 103 ;; | 114 ;; |
| 104 r) | 115 -f) |
| 105 REMOVE_INDEX=1 | 116 remove_index=true |
| 106 SHOULD_INDEX=0 | 117 ;; |
| 107 shift | 118 -r) |
| 119 remove_index=true |
| 120 should_index=false |
| 121 ;; |
| 122 -n) |
| 123 should_index_deps=false |
| 124 ;; |
| 125 -*) |
| 126 echo "Invalid option: $1" >&2 |
| 127 usage_exit |
| 108 ;; | 128 ;; |
| 109 *) | 129 *) |
| 110 echo "Invalid option: -$OPTARG" >&2 | 130 if [[ ! -f "$1" ]]; then |
| 131 echo "Path $1 does not exist." |
| 132 exit 1 |
| 133 fi |
| 134 files_to_index+=("$1") |
| 111 ;; | 135 ;; |
| 112 esac | 136 esac |
| 137 shift |
| 113 done | 138 done |
| 114 | 139 |
| 115 if [[ ! $# == 1 ]]; then | 140 if ((${#files_to_index[@]} == 0)); then |
| 116 echo "Usage: $0 [-f] [-r] path-to-binary" | 141 usage_exit |
| 117 echo " -f forces replacement of an existing index." | |
| 118 echo " -r removes the index section." | |
| 119 exit 1 | |
| 120 fi | 142 fi |
| 121 | 143 |
| 122 FILENAME="$1" | 144 dependencies=() |
| 123 if [[ ! -f "$FILENAME" ]]; then | 145 if $should_index_deps; then |
| 124 echo "Path $FILENAME does not exist." | 146 for file in "${files_to_index[@]}"; do |
| 125 exit 1 | 147 # Append the shared library dependencies of this file that |
| 148 # have the same dirname. The dirname is a signal that these |
| 149 # shared libraries were part of the same build as the binary. |
| 150 dependencies+=( \ |
| 151 $(ldd "$file" 2>/dev/null \ |
| 152 | grep $(dirname "$file") \ |
| 153 | sed "s/.*[ \t]\(.*\) (.*/\1/") \ |
| 154 ) |
| 155 done |
| 126 fi | 156 fi |
| 157 files_to_index+=("${dependencies[@]}") |
| 127 | 158 |
| 128 # Ensure we cleanup on on exit. | 159 # Ensure we cleanup on on exit. |
| 129 trap on_exit EXIT | 160 trap on_exit EXIT INT |
| 130 | 161 |
| 131 # We're good to go! Create temp directory for index files. | 162 # We're good to go! Create temp directory for index files. |
| 132 DIRECTORY=$(mktemp -d) | 163 directory=$(mktemp -d) |
| 133 echo "Made temp directory $DIRECTORY." | 164 echo "Made temp directory $directory." |
| 134 | |
| 135 # Create array with the filename and all shared libraries that | |
| 136 # have the same dirname. The dirname is a signal that these | |
| 137 # shared libraries were part of the same build as the binary. | |
| 138 declare -a FILES_TO_INDEX=($FILENAME | |
| 139 $(ldd "$FILENAME" 2>/dev/null \ | |
| 140 | grep $(dirname "$FILENAME") \ | |
| 141 | sed "s/.*[ \t]\(.*\) (.*/\1/") | |
| 142 ) | |
| 143 | 165 |
| 144 # Start concurrent indexing. | 166 # Start concurrent indexing. |
| 145 trap index_next USR1 | 167 trap index_next USR1 |
| 146 | 168 |
| 147 # 4 is an arbitrary default. When changing, remember we are likely IO bound | 169 # 4 is an arbitrary default. When changing, remember we are likely IO bound |
| 148 # so basing this off the number of cores is not sensible. | 170 # so basing this off the number of cores is not sensible. |
| 149 INDEX_TASKS=${INDEX_TASKS:-4} | 171 index_tasks=${INDEX_TASKS:-4} |
| 150 for ((i=0;i<${INDEX_TASKS};i++)); do | 172 for ((i = 0; i < index_tasks; i++)); do |
| 151 index_next | 173 index_next |
| 152 done | 174 done |
| 153 | 175 |
| 154 # Do a wait loop. Bash waits that terminate due a trap have an exit | 176 # Do a wait loop. Bash waits that terminate due a trap have an exit |
| 155 # code > 128. We also ensure that our subshell's "normal" exit occurs with | 177 # code > 128. We also ensure that our subshell's "normal" exit occurs with |
| 156 # an exit code > 128. This allows us to do consider a > 128 exit code as | 178 # an exit code > 128. This allows us to do consider a > 128 exit code as |
| 157 # an indication that the loop should continue. Unfortunately, it also means | 179 # an indication that the loop should continue. Unfortunately, it also means |
| 158 # we cannot use set -e since technically the "wait" is failing. | 180 # we cannot use set -e since technically the "wait" is failing. |
| 159 wait | 181 wait |
| 160 while (( $? > 128 )); do | 182 while (($? > 128)); do |
| 161 wait | 183 wait |
| 162 done | 184 done |
| OLD | NEW |