Blame view

scripts/decode_stacktrace.sh 5.2 KB
dbd1abb20   Sasha Levin   decode_stacktrace...
1
  #!/bin/bash
b24413180   Greg Kroah-Hartman   License cleanup: ...
2
  # SPDX-License-Identifier: GPL-2.0
dbd1abb20   Sasha Levin   decode_stacktrace...
3
4
  # (c) 2014, Sasha Levin <sasha.levin@oracle.com>
  #set -x
ecda6e27f   Konstantin Khlebnikov   scripts/decode_st...
5
  if [[ $# < 1 ]]; then
dbd1abb20   Sasha Levin   decode_stacktrace...
6
  	echo "Usage:"
f90dde44c   Konstantin Khlebnikov   scripts/decode_st...
7
  	echo "	$0 -r <release> | <vmlinux> [base path] [modules path]"
dbd1abb20   Sasha Levin   decode_stacktrace...
8
9
  	exit 1
  fi
f90dde44c   Konstantin Khlebnikov   scripts/decode_st...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  if [[ $1 == "-r" ]] ; then
  	vmlinux=""
  	basepath="auto"
  	modpath=""
  	release=$2
  
  	for fn in {,/usr/lib/debug}/boot/vmlinux-$release{,.debug} /lib/modules/$release{,/build}/vmlinux ; do
  		if [ -e "$fn" ] ; then
  			vmlinux=$fn
  			break
  		fi
  	done
  
  	if [[ $vmlinux == "" ]] ; then
  		echo "ERROR! vmlinux image for release $release is not found" >&2
  		exit 2
  	fi
  else
  	vmlinux=$1
  	basepath=${2-auto}
  	modpath=$3
  	release=""
  fi
431151b64   Konstantin Khlebnikov   scripts/decode_st...
33

dbd1abb20   Sasha Levin   decode_stacktrace...
34
  declare -A cache
310c6dd06   Konstantin Khlebnikov   scripts/decode_st...
35
  declare -A modcache
dbd1abb20   Sasha Levin   decode_stacktrace...
36

431151b64   Konstantin Khlebnikov   scripts/decode_st...
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  find_module() {
  	if [[ "$modpath" != "" ]] ; then
  		for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do
  			if readelf -WS "$fn" | grep -qwF .debug_line ; then
  				echo $fn
  				return
  			fi
  		done
  		return 1
  	fi
  
  	modpath=$(dirname "$vmlinux")
  	find_module && return
  
  	if [[ $release == "" ]] ; then
  		release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" | sed -n 's/\$1 = "\(.*\)".*/\1/p')
  	fi
  
  	for dn in {/usr/lib/debug,}/lib/modules/$release ; do
  		if [ -e "$dn" ] ; then
  			modpath="$dn"
  			find_module && return
  		fi
  	done
  
  	modpath=""
  	return 1
  }
dbd1abb20   Sasha Levin   decode_stacktrace...
65
66
  parse_symbol() {
  	# The structure of symbol at this point is:
e260fe01f   Robert Jarzmik   scripts: decode_s...
67
  	#   ([name]+[offset]/[total length])
dbd1abb20   Sasha Levin   decode_stacktrace...
68
69
70
  	#
  	# For example:
  	#   do_basic_setup+0x9c/0xbf
310c6dd06   Konstantin Khlebnikov   scripts/decode_st...
71
72
73
74
75
  	if [[ $module == "" ]] ; then
  		local objfile=$vmlinux
  	elif [[ "${modcache[$module]+isset}" == "isset" ]]; then
  		local objfile=${modcache[$module]}
  	else
431151b64   Konstantin Khlebnikov   scripts/decode_st...
76
77
  		local objfile=$(find_module)
  		if [[ $objfile == "" ]] ; then
a5dc8300d   Sasha Levin   scripts/decode_st...
78
79
80
  			echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2
  			return
  		fi
310c6dd06   Konstantin Khlebnikov   scripts/decode_st...
81
82
  		modcache[$module]=$objfile
  	fi
e260fe01f   Robert Jarzmik   scripts: decode_s...
83
84
85
  	# Remove the englobing parenthesis
  	symbol=${symbol#\(}
  	symbol=${symbol%\)}
dbd1abb20   Sasha Levin   decode_stacktrace...
86

1d6693fb9   Konstantin Khlebnikov   scripts/decode_st...
87
88
89
90
91
92
  	# Strip segment
  	local segment
  	if [[ $symbol == *:* ]] ; then
  		segment=${symbol%%:*}:
  		symbol=${symbol#*:}
  	fi
dbd1abb20   Sasha Levin   decode_stacktrace...
93
94
95
96
97
98
  	# Strip the symbol name so that we could look it up
  	local name=${symbol%+*}
  
  	# Use 'nm vmlinux' to figure out the base address of said symbol.
  	# It's actually faster to call it every time than to load it
  	# all into bash.
310c6dd06   Konstantin Khlebnikov   scripts/decode_st...
99
100
  	if [[ "${cache[$module,$name]+isset}" == "isset" ]]; then
  		local base_addr=${cache[$module,$name]}
dbd1abb20   Sasha Levin   decode_stacktrace...
101
  	else
f643b9ee9   Konstantin Khlebnikov   scripts/decode_st...
102
103
104
105
106
  		local base_addr=$(nm "$objfile" | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}')
  		if [[ $base_addr == "" ]] ; then
  			# address not found
  			return
  		fi
310c6dd06   Konstantin Khlebnikov   scripts/decode_st...
107
  		cache[$module,$name]="$base_addr"
dbd1abb20   Sasha Levin   decode_stacktrace...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  	fi
  	# Let's start doing the math to get the exact address into the
  	# symbol. First, strip out the symbol total length.
  	local expr=${symbol%/*}
  
  	# Now, replace the symbol name with the base address we found
  	# before.
  	expr=${expr/$name/0x$base_addr}
  
  	# Evaluate it to find the actual address
  	expr=$((expr))
  	local address=$(printf "%x
  " "$expr")
  
  	# Pass it to addr2line to get filename and line number
310c6dd06   Konstantin Khlebnikov   scripts/decode_st...
123
124
125
  	# Could get more than one result
  	if [[ "${cache[$module,$address]+isset}" == "isset" ]]; then
  		local code=${cache[$module,$address]}
dbd1abb20   Sasha Levin   decode_stacktrace...
126
  	else
c04e32e91   Manuel Traut   scripts/decode_st...
127
  		local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address")
310c6dd06   Konstantin Khlebnikov   scripts/decode_st...
128
  		cache[$module,$address]=$code
dbd1abb20   Sasha Levin   decode_stacktrace...
129
130
131
132
133
134
135
136
  	fi
  
  	# addr2line doesn't return a proper error code if it fails, so
  	# we detect it using the value it prints so that we could preserve
  	# the offset/size into the function and bail out
  	if [[ $code == "??:0" ]]; then
  		return
  	fi
d178770d8   Pi-Hsun Shih   scripts/decode_st...
137
138
  	# Strip out the base of the path on each line
  	code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code")
dbd1abb20   Sasha Levin   decode_stacktrace...
139
140
141
142
143
144
  
  	# In the case of inlines, move everything to same line
  	code=${code//$'
  '/' '}
  
  	# Replace old address with pretty line numbers
1d6693fb9   Konstantin Khlebnikov   scripts/decode_st...
145
  	symbol="$segment$name ($code)"
dbd1abb20   Sasha Levin   decode_stacktrace...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  }
  
  decode_code() {
  	local scripts=`dirname "${BASH_SOURCE[0]}"`
  
  	echo "$1" | $scripts/decodecode
  }
  
  handle_line() {
  	local words
  
  	# Tokenize
  	read -a words <<<"$1"
  
  	# Remove hex numbers. Do it ourselves until it happens in the
  	# kernel
  
  	# We need to know the index of the last element before we
  	# remove elements because arrays are sparse
  	local last=$(( ${#words[@]} - 1 ))
  
  	for i in "${!words[@]}"; do
  		# Remove the address
  		if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then
  			unset words[$i]
  		fi
  
  		# Format timestamps with tabs
  		if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then
  			unset words[$i]
  			words[$i+1]=$(printf "[%13s
  " "${words[$i+1]}")
  		fi
  	done
310c6dd06   Konstantin Khlebnikov   scripts/decode_st...
180
181
182
183
184
185
186
187
188
189
190
  	if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then
  		module=${words[$last]}
  		module=${module#\[}
  		module=${module%\]}
  		symbol=${words[$last-1]}
  		unset words[$last-1]
  	else
  		# The symbol is the last element, process it
  		symbol=${words[$last]}
  		module=
  	fi
dbd1abb20   Sasha Levin   decode_stacktrace...
191
192
193
194
  	unset words[$last]
  	parse_symbol # modifies $symbol
  
  	# Add up the line number to the symbol
310c6dd06   Konstantin Khlebnikov   scripts/decode_st...
195
  	echo "${words[@]}" "$symbol $module"
dbd1abb20   Sasha Levin   decode_stacktrace...
196
  }
ecda6e27f   Konstantin Khlebnikov   scripts/decode_st...
197
198
199
200
201
202
203
  if [[ $basepath == "auto" ]] ; then
  	module=""
  	symbol="kernel_init+0x0/0x0"
  	parse_symbol
  	basepath=${symbol#kernel_init (}
  	basepath=${basepath%/init/main.c:*)}
  fi
dbd1abb20   Sasha Levin   decode_stacktrace...
204
205
  while read line; do
  	# Let's see if we have an address in the line
53938ee42   Josh Poimboeuf   scripts/decode_st...
206
207
  	if [[ $line =~ \[\<([^]]+)\>\] ]] ||
  	   [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then
dbd1abb20   Sasha Levin   decode_stacktrace...
208
209
210
211
  		# Translate address to line numbers
  		handle_line "$line"
  	# Is it a code line?
  	elif [[ $line == *Code:* ]]; then
310c6dd06   Konstantin Khlebnikov   scripts/decode_st...
212
213
  		decode_code "$line"
  	else
dbd1abb20   Sasha Levin   decode_stacktrace...
214
215
216
217
  		# Nothing special in this line, show it as is
  		echo "$line"
  	fi
  done