]> git.ipfire.org Git - people/ms/linux.git/blame - scripts/decode_stacktrace.sh
Merge branch 'sunplus/newsoc' into arm/newsoc
[people/ms/linux.git] / scripts / decode_stacktrace.sh
CommitLineData
dbd1abb2 1#!/bin/bash
b2441318 2# SPDX-License-Identifier: GPL-2.0
dbd1abb2
SL
3# (c) 2014, Sasha Levin <sasha.levin@oracle.com>
4#set -x
5
26681eb3 6usage() {
dbd1abb2 7 echo "Usage:"
d5ce757d 8 echo " $0 -r <release> | <vmlinux> [<base path>|auto] [<modules path>]"
26681eb3 9}
dbd1abb2 10
f90dde44
KK
11if [[ $1 == "-r" ]] ; then
12 vmlinux=""
13 basepath="auto"
14 modpath=""
15 release=$2
16
17 for fn in {,/usr/lib/debug}/boot/vmlinux-$release{,.debug} /lib/modules/$release{,/build}/vmlinux ; do
18 if [ -e "$fn" ] ; then
19 vmlinux=$fn
20 break
21 fi
22 done
23
24 if [[ $vmlinux == "" ]] ; then
25 echo "ERROR! vmlinux image for release $release is not found" >&2
26681eb3 26 usage
f90dde44
KK
27 exit 2
28 fi
29else
30 vmlinux=$1
31 basepath=${2-auto}
32 modpath=$3
33 release=""
26681eb3
SB
34 debuginfod=
35
36 # Can we use debuginfod-find?
37 if type debuginfod-find >/dev/null 2>&1 ; then
38 debuginfod=${1-only}
39 fi
40
41 if [[ $vmlinux == "" && -z $debuginfod ]] ; then
42 echo "ERROR! vmlinux image must be specified" >&2
43 usage
44 exit 1
45 fi
f90dde44 46fi
431151b6 47
3af8acf6
SS
48declare aarray_support=true
49declare -A cache 2>/dev/null
50if [[ $? != 0 ]]; then
51 aarray_support=false
52else
53 declare -A modcache
54fi
dbd1abb2 55
431151b6 56find_module() {
26681eb3
SB
57 if [[ -n $debuginfod ]] ; then
58 if [[ -n $modbuildid ]] ; then
59 debuginfod-find debuginfo $modbuildid && return
60 fi
61
62 # Only using debuginfod so don't try to find vmlinux module path
63 if [[ $debuginfod == "only" ]] ; then
64 return
65 fi
66 fi
67
431151b6
KK
68 if [[ "$modpath" != "" ]] ; then
69 for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do
70 if readelf -WS "$fn" | grep -qwF .debug_line ; then
71 echo $fn
72 return
73 fi
74 done
75 return 1
76 fi
77
78 modpath=$(dirname "$vmlinux")
79 find_module && return
80
81 if [[ $release == "" ]] ; then
5bf0f3bc 82 release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" 2>/dev/null | sed -n 's/\$1 = "\(.*\)".*/\1/p')
431151b6
KK
83 fi
84
85 for dn in {/usr/lib/debug,}/lib/modules/$release ; do
86 if [ -e "$dn" ] ; then
87 modpath="$dn"
88 find_module && return
89 fi
90 done
91
92 modpath=""
93 return 1
94}
95
dbd1abb2
SL
96parse_symbol() {
97 # The structure of symbol at this point is:
e260fe01 98 # ([name]+[offset]/[total length])
dbd1abb2
SL
99 #
100 # For example:
101 # do_basic_setup+0x9c/0xbf
102
310c6dd0
KK
103 if [[ $module == "" ]] ; then
104 local objfile=$vmlinux
3af8acf6 105 elif [[ $aarray_support == true && "${modcache[$module]+isset}" == "isset" ]]; then
310c6dd0
KK
106 local objfile=${modcache[$module]}
107 else
431151b6
KK
108 local objfile=$(find_module)
109 if [[ $objfile == "" ]] ; then
a5dc8300
SL
110 echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2
111 return
112 fi
3af8acf6
SS
113 if [[ $aarray_support == true ]]; then
114 modcache[$module]=$objfile
115 fi
310c6dd0
KK
116 fi
117
e260fe01
RJ
118 # Remove the englobing parenthesis
119 symbol=${symbol#\(}
120 symbol=${symbol%\)}
dbd1abb2 121
1d6693fb
KK
122 # Strip segment
123 local segment
124 if [[ $symbol == *:* ]] ; then
125 segment=${symbol%%:*}:
126 symbol=${symbol#*:}
127 fi
128
dbd1abb2
SL
129 # Strip the symbol name so that we could look it up
130 local name=${symbol%+*}
131
132 # Use 'nm vmlinux' to figure out the base address of said symbol.
133 # It's actually faster to call it every time than to load it
134 # all into bash.
3af8acf6 135 if [[ $aarray_support == true && "${cache[$module,$name]+isset}" == "isset" ]]; then
310c6dd0 136 local base_addr=${cache[$module,$name]}
dbd1abb2 137 else
5bf0f3bc 138 local base_addr=$(nm "$objfile" 2>/dev/null | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}')
f643b9ee
KK
139 if [[ $base_addr == "" ]] ; then
140 # address not found
141 return
142 fi
3af8acf6
SS
143 if [[ $aarray_support == true ]]; then
144 cache[$module,$name]="$base_addr"
145 fi
dbd1abb2
SL
146 fi
147 # Let's start doing the math to get the exact address into the
148 # symbol. First, strip out the symbol total length.
149 local expr=${symbol%/*}
150
151 # Now, replace the symbol name with the base address we found
152 # before.
153 expr=${expr/$name/0x$base_addr}
154
155 # Evaluate it to find the actual address
156 expr=$((expr))
157 local address=$(printf "%x\n" "$expr")
158
159 # Pass it to addr2line to get filename and line number
310c6dd0 160 # Could get more than one result
3af8acf6 161 if [[ $aarray_support == true && "${cache[$module,$address]+isset}" == "isset" ]]; then
310c6dd0 162 local code=${cache[$module,$address]}
dbd1abb2 163 else
5bf0f3bc 164 local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address" 2>/dev/null)
3af8acf6
SS
165 if [[ $aarray_support == true ]]; then
166 cache[$module,$address]=$code
167 fi
dbd1abb2
SL
168 fi
169
170 # addr2line doesn't return a proper error code if it fails, so
171 # we detect it using the value it prints so that we could preserve
172 # the offset/size into the function and bail out
173 if [[ $code == "??:0" ]]; then
174 return
175 fi
176
d178770d
PHS
177 # Strip out the base of the path on each line
178 code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code")
dbd1abb2
SL
179
180 # In the case of inlines, move everything to same line
181 code=${code//$'\n'/' '}
182
183 # Replace old address with pretty line numbers
1d6693fb 184 symbol="$segment$name ($code)"
dbd1abb2
SL
185}
186
26681eb3
SB
187debuginfod_get_vmlinux() {
188 local vmlinux_buildid=${1##* }
189
190 if [[ $vmlinux != "" ]]; then
191 return
192 fi
193
194 if [[ $vmlinux_buildid =~ ^[0-9a-f]+ ]]; then
195 vmlinux=$(debuginfod-find debuginfo $vmlinux_buildid)
196 if [[ $? -ne 0 ]] ; then
197 echo "ERROR! vmlinux image not found via debuginfod-find" >&2
198 usage
199 exit 2
200 fi
201 return
202 fi
203 echo "ERROR! Build ID for vmlinux not found. Try passing -r or specifying vmlinux" >&2
204 usage
205 exit 2
206}
207
dbd1abb2
SL
208decode_code() {
209 local scripts=`dirname "${BASH_SOURCE[0]}"`
210
211 echo "$1" | $scripts/decodecode
212}
213
214handle_line() {
26681eb3
SB
215 if [[ $basepath == "auto" && $vmlinux != "" ]] ; then
216 module=""
217 symbol="kernel_init+0x0/0x0"
218 parse_symbol
219 basepath=${symbol#kernel_init (}
220 basepath=${basepath%/init/main.c:*)}
221 fi
222
dbd1abb2
SL
223 local words
224
225 # Tokenize
226 read -a words <<<"$1"
227
228 # Remove hex numbers. Do it ourselves until it happens in the
229 # kernel
230
231 # We need to know the index of the last element before we
232 # remove elements because arrays are sparse
233 local last=$(( ${#words[@]} - 1 ))
234
235 for i in "${!words[@]}"; do
236 # Remove the address
237 if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then
238 unset words[$i]
239 fi
240
241 # Format timestamps with tabs
242 if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then
243 unset words[$i]
244 words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}")
245 fi
246 done
247
26681eb3
SB
248 if [[ ${words[$last]} =~ ^[0-9a-f]+\] ]]; then
249 words[$last-1]="${words[$last-1]} ${words[$last]}"
250 unset words[$last]
251 last=$(( $last - 1 ))
252 fi
253
310c6dd0
KK
254 if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then
255 module=${words[$last]}
256 module=${module#\[}
257 module=${module%\]}
26681eb3
SB
258 modbuildid=${module#* }
259 module=${module% *}
260 if [[ $modbuildid == $module ]]; then
261 modbuildid=
262 fi
310c6dd0
KK
263 symbol=${words[$last-1]}
264 unset words[$last-1]
265 else
266 # The symbol is the last element, process it
267 symbol=${words[$last]}
268 module=
26681eb3 269 modbuildid=
310c6dd0
KK
270 fi
271
dbd1abb2
SL
272 unset words[$last]
273 parse_symbol # modifies $symbol
274
275 # Add up the line number to the symbol
310c6dd0 276 echo "${words[@]}" "$symbol $module"
dbd1abb2
SL
277}
278
279while read line; do
280 # Let's see if we have an address in the line
53938ee4
JP
281 if [[ $line =~ \[\<([^]]+)\>\] ]] ||
282 [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then
dbd1abb2
SL
283 # Translate address to line numbers
284 handle_line "$line"
285 # Is it a code line?
286 elif [[ $line == *Code:* ]]; then
310c6dd0 287 decode_code "$line"
26681eb3
SB
288 # Is it a version line?
289 elif [[ -n $debuginfod && $line =~ PID:\ [0-9]+\ Comm: ]]; then
290 debuginfod_get_vmlinux "$line"
310c6dd0 291 else
dbd1abb2
SL
292 # Nothing special in this line, show it as is
293 echo "$line"
294 fi
295done