[release] Use threaded compression with xz
[llvm-project.git] / bolt / utils / bughunter.sh
blob49831cddfdbdd10d7028de25fed02edde8d0d080
1 #!/bin/bash
2 ##===- bolt/utils/bughunter.sh - Help locate BOLT bugs -------*- Script -*-===##
4 # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 # See https://llvm.org/LICENSE.txt for license information.
6 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 # details.
9 ##===----------------------------------------------------------------------===##
11 # This script attempts to narrow down llvm-bolt bug to a single function in the
12 # input binary.
14 # If such a function is found, llvm-bolt could be run just on this function
15 # to mitigate debugging process.
17 # The following envvars are used by this script:
19 # BOLT - path to llvm-bolt
21 # BOLT_OPTIONS - options to be used by llvm-bolt
23 # INPUT_BINARY - input for llvm-bolt
25 # PRE_COMMAND - command to execute prior to running optimized binary
27 # POST_COMMAND - command to filter results of running optimized binary
29 # TIMEOUT_OR_CMD - optional timeout or command on optimized binary command
30 # if the value is a number with an optional trailing letter
31 # [smhd] it is considered a paramter to "timeout",
32 # otherwise it's a shell command that wraps the optimized
33 # binary command.
35 # COMMAND_LINE - command line options to run optimized binary with
37 # IGNORE_ERROR - ignore error codes returned from optimized binary
39 # GOLD_FILE - file containing expected output from optimized binary
41 # FUNC_NAMES - if set, path to an initial list of function names to
42 # search. Otherwise, nm is used on the original binary.
44 # OFFLINE - if set, bughunter will produce the binaries but will not
45 # run them, and will depend on you telling whether it
46 # succeeded or not.
48 # MAX_FUNCS - if set, use -max-funcs to narrow down the offending
49 # function. if non-zero, start -max-funcs at $MAX_FUNCS
50 # otherwise, count the number of symbols in the binary.
52 # MAX_FUNCS_FLAG - BOLT command line option to use for MAX_FUNCS search.
53 # Default is -max-funcs. Can also be used for relocation
54 # debugging, e.g. -max-data-relocations.
56 # VERBOSE - if non-empty, set the script to echo mode.
58 ##===----------------------------------------------------------------------===##
60 BOLT=${BOLT:=llvm-bolt}
62 ulimit -c 0
63 set -o pipefail
65 if [[ -n "$VERBOSE" ]]; then
66 set -x
69 if [[ ! -x $INPUT_BINARY ]] ; then
70 echo "INPUT_BINARY must be set to an executable file"
71 exit 1
74 if [[ -z "$PRE_COMMAND" ]] ; then
75 PRE_COMMAND=':'
78 if [[ -z "$POST_COMMAND" ]] ; then
79 POST_COMMAND='cat'
82 if [[ -n "$TIMEOUT_OR_CMD" && $TIMEOUT_OR_CMD =~ ^[0-9]+[smhd]?$ ]] ; then
83 TIMEOUT_OR_CMD="timeout -s KILL $TIMEOUT_OR_CMD"
86 if [[ -z "$MAX_FUNCS_FLAG" ]] ; then
87 MAX_FUNCS_FLAG="-max-funcs"
90 OPTIMIZED_BINARY=$(mktemp -t -u --suffix=.bolt $(basename ${INPUT_BINARY}).XXX)
91 OUTPUT_FILE="${OPTIMIZED_BINARY}.out"
92 BOLT_LOG=$(mktemp -t -u --suffix=.log boltXXX)
94 if [[ -z $OFFLINE ]]; then
95 echo "Verify input binary passes"
96 echo " INPUT_BINARY: $PRE_COMMAND && $TIMEOUT_OR_CMD $INPUT_BINARY $COMMAND_LINE |& $POST_COMMAND >& $OUTPUT_FILE"
97 ($PRE_COMMAND && $TIMEOUT_OR_CMD $INPUT_BINARY $COMMAND_LINE |& $POST_COMMAND >& $OUTPUT_FILE)
98 STATUS=$?
99 if [[ "$IGNORE_ERROR" == "1" ]]; then
100 FAIL=0
101 else
102 FAIL=$STATUS
104 if [[ -e "$GOLD_FILE" ]] ; then
105 cmp -s "$OUTPUT_FILE" "$GOLD_FILE"
106 FAIL=$?
108 if [[ $FAIL -ne "0" ]] ; then
109 echo " Warning: input binary failed"
110 else
111 echo " Input binary passes."
115 echo "Verify optimized binary fails"
116 ($BOLT $BOLT_OPTIONS $INPUT_BINARY -o $OPTIMIZED_BINARY >& $BOLT_LOG)
117 FAIL=$?
118 if [[ $FAIL -eq "0" ]]; then
119 if [[ -z $OFFLINE ]]; then
120 echo " OPTIMIZED_BINARY: $PRE_COMMAND && $TIMEOUT_OR_CMD $OPTIMIZED_BINARY $COMMAND_LINE |& $POST_COMMAND >& $OUTPUT_FILE"
121 ($PRE_COMMAND && $TIMEOUT_OR_CMD $OPTIMIZED_BINARY $COMMAND_LINE |& $POST_COMMAND >& $OUTPUT_FILE)
122 STATUS=$?
123 if [[ "$IGNORE_ERROR" == "1" ]]; then
124 FAIL=0
125 else
126 FAIL=$STATUS
128 if [[ -e "$GOLD_FILE" ]] ; then
129 cmp -s "$OUTPUT_FILE" "$GOLD_FILE"
130 FAIL=$?
132 else
133 echo "Did it pass? Type the return code [0 = pass, 1 = fail]"
134 read -n1 PASS
136 if [[ $FAIL -eq "0" ]] ; then
137 echo " Warning: optimized binary passes."
138 else
139 echo " Optimized binary fails as expected."
141 else
142 echo " Bolt crashes while generating optimized binary."
145 # Collect function names
146 FF=$(mktemp -t -u --suffix=.txt func-names.XXX)
147 nm --defined-only -p $INPUT_BINARY | grep " [TtWw] " | cut -d ' ' -f 3 | egrep -v "\._" | egrep -v '^$' | sort -u > $FF
149 # Use function names or numbers
150 if [[ -z "$MAX_FUNCS" ]] ; then
151 # Do binary search on function names
152 if [[ -n "$FUNC_NAMES" ]]; then
153 FF=$FUNC_NAMES
155 NUM_FUNCS=$(wc -l $FF | cut -d ' ' -f 1)
156 HALF=$(expr \( $NUM_FUNCS + 1 \) / 2)
157 PREFIX=$(mktemp -t -u --suffix=.txt func-names.XXX)
158 FF0=$PREFIX\aa
159 FF1=$PREFIX\ab
160 split -a 2 -l $HALF $FF $PREFIX
161 FF=$FF0
162 NUM_FUNCS=$(wc -l $FF | cut -d ' ' -f 1)
163 CONTINUE=$(expr $NUM_FUNCS \> 0)
164 else
166 if [[ "$MAX_FUNCS" -eq "0" ]]; then
167 Q=$(wc -l $FF | cut -d ' ' -f 1)
168 else
169 Q=$MAX_FUNCS
171 I=$Q
172 CONTINUE=$(expr \( $Q - $P \) \> 1)
175 ITER=0
176 while [[ "$CONTINUE" -ne "0" ]] ; do
177 rm -f $OPTIMIZED_BINARY
178 if [[ -z "$MAX_FUNCS" ]] ; then
179 echo "Iteration $ITER, trying $FF / $HALF functions"
180 SEARCH_OPT="-funcs-file-no-regex=$FF"
181 else
182 I=$(expr \( $Q + $P \) / 2)
183 echo "Iteration $ITER, P=$P, Q=$Q, I=$I"
184 SEARCH_OPT="$MAX_FUNCS_FLAG=$I"
186 echo " BOLT: $BOLT $BOLT_OPTIONS $INPUT_BINARY $SEARCH_OPT -o $OPTIMIZED_BINARY >& $BOLT_LOG"
187 ($BOLT $BOLT_OPTIONS $INPUT_BINARY $SEARCH_OPT -o $OPTIMIZED_BINARY >& $BOLT_LOG)
188 FAIL=$?
189 echo " BOLT failure=$FAIL"
190 rm -f $OUTPUT_FILE
191 if [[ $FAIL -eq "0" ]] ; then
192 if [[ -z $OFFLINE ]]; then
193 echo " OPTIMIZED_BINARY: $PRE_COMMAND && $TIMEOUT_OR_CMD $OPTIMIZED_BINARY $COMMAND_LINE |& $POST_COMMAND >& $OUTPUT_FILE"
194 ($PRE_COMMAND && $TIMEOUT_OR_CMD $OPTIMIZED_BINARY $COMMAND_LINE |& $POST_COMMAND >& $OUTPUT_FILE)
195 STATUS=$?
196 if [[ "$IGNORE_ERROR" == "1" ]]; then
197 FAIL=0
198 else
199 FAIL=$STATUS
201 if [[ -e "$GOLD_FILE" ]] ; then
202 cmp -s "$OUTPUT_FILE" "$GOLD_FILE"
203 FAIL=$?
205 echo " OPTIMIZED_BINARY failure=$FAIL"
206 else
207 echo "Did it pass? Type the return code [0 = pass, 1 = fail]"
208 read -n1 PASS
210 else
211 FAIL=1
214 if [[ -z "$MAX_FUNCS" ]] ; then
215 if [[ $FAIL -eq "0" ]] ; then
216 if [[ "$FF" == "$FF1" ]]; then
217 NUM_FUNCS=0
218 break;
220 FF=$FF1
221 NUM_FUNCS=$(wc -l $FF | cut -d ' ' -f 1)
222 else
223 HALF=$(expr \( $NUM_FUNCS + 1 \) / 2)
224 PREFIX=$(mktemp -t -u --suffix=.txt func-names.XXX)
225 split -a 2 -l $HALF $FF $PREFIX
226 FF0=$PREFIX\aa
227 FF1=$PREFIX\ab
228 FF=$FF0
229 NUM_FUNCS=$(wc -l $FF | cut -d ' ' -f 1)
230 if [[ $NUM_FUNCS -eq "1" && ! -e $FF1 ]]; then
231 break;
234 CONTINUE=$(expr $NUM_FUNCS \> 0)
235 else
236 if [[ $FAIL -eq "0" ]] ; then
237 P=$I
238 else
239 Q=$I
241 FF=$I
242 HALF=$I
243 CONTINUE=$(expr \( $Q - $P \) \> 1)
245 ITER=$(expr $ITER + 1)
246 done
248 if [[ -z "$MAX_FUNCS" ]] ; then
249 if [[ "$NUM_FUNCS" -ne "0" ]] ; then
250 FAILED="The function(s) that failed are in $FF"
252 else
253 if [[ $P -ne $Q ]] ; then
254 FF=$(grep "processing ending" $BOLT_LOG | sed -e "s/BOLT-INFO: processing ending on \(.*\)/\1/g" | tail -1)
255 FAILED="The item that failed is $FF @ $Q"
259 if [[ -n "$FAILED" ]] ; then
260 echo "$FAILED"
261 echo "To reproduce, run: $BOLT $BOLT_OPTIONS $INPUT_BINARY $SEARCH_OPT -o $OPTIMIZED_BINARY"
262 else
263 echo "Unable to reproduce bug."
266 rm $OPTIMIZED_BINARY $OUTPUT_FILE $BOLT_LOG