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