New upstream version 17.11-rc3
[deb_dpdk.git] / devtools / validate-abi.sh
1 #!/usr/bin/env bash
2 #   BSD LICENSE
3 #
4 #   Copyright(c) 2015 Neil Horman. All rights reserved.
5 #   Copyright(c) 2017 6WIND S.A.
6 #   All rights reserved.
7 #
8 #   Redistribution and use in source and binary forms, with or without
9 #   modification, are permitted provided that the following conditions
10 #   are met:
11 #
12 #     * Redistributions of source code must retain the above copyright
13 #       notice, this list of conditions and the following disclaimer.
14 #     * Redistributions in binary form must reproduce the above copyright
15 #       notice, this list of conditions and the following disclaimer in
16 #       the documentation and/or other materials provided with the
17 #       distribution.
18 #
19 #   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 #   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 #   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 #   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 #   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 #   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 #   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 #   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 #   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 #   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 #   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 set -e
32
33 abicheck=abi-compliance-checker
34 abidump=abi-dumper
35 default_dst=abi-check
36 default_target=x86_64-native-linuxapp-gcc
37
38 # trap on error
39 err_report() {
40     echo "$0: error at line $1"
41 }
42 trap 'err_report $LINENO' ERR
43
44 print_usage () {
45         cat <<- END_OF_HELP
46         $(basename $0) [options] <rev1> <rev2>
47
48         This script compares the ABI of 2 git revisions of the current
49         workspace. The output is a html report and a compilation log.
50
51         The objective is to make sure that applications built against
52         DSOs from the first revision can still run when executed using
53         the DSOs built from the second revision.
54
55         <rev1> and <rev2> are git commit id or tags.
56
57         Options:
58           -h            show this help
59           -j <num>      enable parallel compilation with <num> threads
60           -v            show compilation logs on the console
61           -d <dir>      change working directory (default is ${default_dst})
62           -t <target>   the dpdk target to use (default is ${default_target})
63           -f            overwrite existing files in destination directory
64
65         The script returns 0 on success, or the value of last failing
66         call of ${abicheck} (incompatible abi or the tool has run with errors).
67         The errors returned by ${abidump} are ignored.
68
69         END_OF_HELP
70 }
71
72 # log in the file, and on stdout if verbose
73 # $1: level string
74 # $2: string to be logged
75 log() {
76         echo "$1: $2"
77         if [ "${verbose}" != "true" ]; then
78                 echo "$1: $2" >&3
79         fi
80 }
81
82 # launch a command and log it, taking care of surrounding spaces with quotes
83 cmd() {
84         local i s whitespace ret
85         s=""
86         whitespace="[[:space:]]"
87         for i in "$@"; do
88                 if [[ $i =~ $whitespace ]]; then
89                         i=\"$i\"
90                 fi
91                 if [ -z "$s" ]; then
92                         s="$i"
93                 else
94                         s="$s $i"
95                 fi
96         done
97
98         ret=0
99         log "CMD" "$s"
100         "$@" || ret=$?
101         if [ "$ret" != "0" ]; then
102                 log "CMD" "previous command returned $ret"
103         fi
104
105         return $ret
106 }
107
108 # redirect or copy stderr/stdout to a file
109 # the syntax is unfamiliar, but it makes the rest of the
110 # code easier to read, avoiding the use of pipes
111 set_log_file() {
112         # save original stdout and stderr in fd 3 and 4
113         exec 3>&1
114         exec 4>&2
115         # create a new fd 5 that send to a file
116         exec 5> >(cat > $1)
117         # send stdout and stderr to fd 5
118         if [ "${verbose}" = "true" ]; then
119                 exec 1> >(tee /dev/fd/5 >&3)
120                 exec 2> >(tee /dev/fd/5 >&4)
121         else
122                 exec 1>&5
123                 exec 2>&5
124         fi
125 }
126
127 # Make sure we configure SHARED libraries
128 # Also turn off IGB and KNI as those require kernel headers to build
129 fixup_config() {
130         local conf=config/defconfig_$target
131         cmd sed -i -e"$ a\CONFIG_RTE_BUILD_SHARED_LIB=y" $conf
132         cmd sed -i -e"$ a\CONFIG_RTE_NEXT_ABI=n" $conf
133         cmd sed -i -e"$ a\CONFIG_RTE_EAL_IGB_UIO=n" $conf
134         cmd sed -i -e"$ a\CONFIG_RTE_LIBRTE_KNI=n" $conf
135         cmd sed -i -e"$ a\CONFIG_RTE_KNI_KMOD=n" $conf
136 }
137
138 # build dpdk for the given tag and dump abi
139 # $1: hash of the revision
140 gen_abi() {
141         local i
142
143         cmd git clone ${dpdkroot} ${dst}/${1}
144         cmd cd ${dst}/${1}
145
146         log "INFO" "Checking out version ${1} of the dpdk"
147         # Move to the old version of the tree
148         cmd git checkout ${1}
149
150         fixup_config
151
152         # Now configure the build
153         log "INFO" "Configuring DPDK ${1}"
154         cmd make config T=$target O=$target
155
156         # Checking abi compliance relies on using the dwarf information in
157         # the shared objects. Build with -g to include them.
158         log "INFO" "Building DPDK ${1}. This might take a moment"
159         cmd make -j$parallel O=$target V=1 EXTRA_CFLAGS="-g -Og -Wno-error" \
160             EXTRA_LDFLAGS="-g" || log "INFO" "The build failed"
161
162         # Move to the lib directory
163         cmd cd ${PWD}/$target/lib
164         log "INFO" "Collecting ABI information for ${1}"
165         for i in *.so; do
166                 [ -e "$i" ] || break
167                 cmd $abidump ${i} -o $dst/${1}/${i}.dump -lver ${1} || true
168                 # hack to ignore empty SymbolsInfo section (no public ABI)
169                 if grep -q "'SymbolInfo' => {}," $dst/${1}/${i}.dump \
170                                 2> /dev/null; then
171                         log "INFO" "${i} has no public ABI, remove dump file"
172                         cmd rm -f $dst/${1}/${i}.dump
173                 fi
174         done
175 }
176
177 verbose=false
178 parallel=1
179 dst=${default_dst}
180 target=${default_target}
181 force=0
182 while getopts j:vd:t:fh ARG ; do
183         case $ARG in
184                 j ) parallel=$OPTARG ;;
185                 v ) verbose=true ;;
186                 d ) dst=$OPTARG ;;
187                 t ) target=$OPTARG ;;
188                 f ) force=1 ;;
189                 h ) print_usage ; exit 0 ;;
190                 ? ) print_usage ; exit 1 ;;
191         esac
192 done
193 shift $(($OPTIND - 1))
194
195 if [ $# != 2 ]; then
196         print_usage
197         exit 1
198 fi
199
200 tag1=$1
201 tag2=$2
202
203 # convert path to absolute
204 case "${dst}" in
205         /*) ;;
206         *) dst=${PWD}/${dst} ;;
207 esac
208 dpdkroot=$(readlink -e $(dirname $0)/..)
209
210 if [ -e "${dst}" -a "$force" = 0 ]; then
211         echo "The ${dst} directory is not empty. Remove it, use another"
212         echo "one (-d <dir>), or force overriding (-f)"
213         exit 1
214 fi
215
216 rm -rf ${dst}
217 mkdir -p ${dst}
218 set_log_file ${dst}/abi-check.log
219 log "INFO" "Logs available in ${dst}/abi-check.log"
220
221 command -v ${abicheck} || log "INFO" "Can't find ${abicheck} utility"
222 command -v ${abidump} || log "INFO" "Can't find ${abidump} utility"
223
224 hash1=$(git show -s --format=%h "$tag1" -- 2> /dev/null | tail -1)
225 hash2=$(git show -s --format=%h "$tag2" -- 2> /dev/null | tail -1)
226
227 # Make hashes available in output for non-local reference
228 tag1="$tag1 ($hash1)"
229 tag2="$tag2 ($hash2)"
230
231 if [ "$hash1" = "$hash2" ]; then
232         log "ERROR" "$tag1 and $tag2 are the same revisions"
233         exit 1
234 fi
235
236 cmd mkdir -p ${dst}
237
238 # dump abi for each revision
239 gen_abi ${hash1}
240 gen_abi ${hash2}
241
242 # compare the abi dumps
243 cmd cd ${dst}
244 ret=0
245 list=""
246 for i in ${hash2}/*.dump; do
247         name=`basename $i`
248         libname=${name%.dump}
249
250         if [ ! -f ${hash1}/$name ]; then
251                 log "INFO" "$NAME does not exist in $tag1. skipping..."
252                 continue
253         fi
254
255         local_ret=0
256         cmd $abicheck -l $libname \
257             -old ${hash1}/$name -new ${hash2}/$name || local_ret=$?
258         if [ $local_ret != 0 ]; then
259                 log "NOTICE" "$abicheck returned $local_ret"
260                 ret=$local_ret
261                 list="$list $libname"
262         fi
263 done
264
265 if [ $ret != 0 ]; then
266         log "NOTICE" "ABI may be incompatible, check reports/logs for details."
267         log "NOTICE" "Incompatible list: $list"
268 else
269         log "NOTICE" "No error detected, ABI is compatible."
270 fi
271
272 log "INFO" "Logs are in ${dst}/abi-check.log"
273 log "INFO" "HTML reports are in ${dst}/compat_reports directory"
274
275 exit $ret