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 |