Blame view
scripts/decode_stacktrace.sh
5.2 KB
dbd1abb20 decode_stacktrace... |
1 |
#!/bin/bash |
b24413180 License cleanup: ... |
2 |
# SPDX-License-Identifier: GPL-2.0 |
dbd1abb20 decode_stacktrace... |
3 4 |
# (c) 2014, Sasha Levin <sasha.levin@oracle.com> #set -x |
ecda6e27f scripts/decode_st... |
5 |
if [[ $# < 1 ]]; then |
dbd1abb20 decode_stacktrace... |
6 |
echo "Usage:" |
f90dde44c scripts/decode_st... |
7 |
echo " $0 -r <release> | <vmlinux> [base path] [modules path]" |
dbd1abb20 decode_stacktrace... |
8 9 |
exit 1 fi |
f90dde44c 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 scripts/decode_st... |
33 |
|
dbd1abb20 decode_stacktrace... |
34 |
declare -A cache |
310c6dd06 scripts/decode_st... |
35 |
declare -A modcache |
dbd1abb20 decode_stacktrace... |
36 |
|
431151b64 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 decode_stacktrace... |
65 66 |
parse_symbol() { # The structure of symbol at this point is: |
e260fe01f scripts: decode_s... |
67 |
# ([name]+[offset]/[total length]) |
dbd1abb20 decode_stacktrace... |
68 69 70 |
# # For example: # do_basic_setup+0x9c/0xbf |
310c6dd06 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 scripts/decode_st... |
76 77 |
local objfile=$(find_module) if [[ $objfile == "" ]] ; then |
a5dc8300d scripts/decode_st... |
78 79 80 |
echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2 return fi |
310c6dd06 scripts/decode_st... |
81 82 |
modcache[$module]=$objfile fi |
e260fe01f scripts: decode_s... |
83 84 85 |
# Remove the englobing parenthesis symbol=${symbol#\(} symbol=${symbol%\)} |
dbd1abb20 decode_stacktrace... |
86 |
|
1d6693fb9 scripts/decode_st... |
87 88 89 90 91 92 |
# Strip segment local segment if [[ $symbol == *:* ]] ; then segment=${symbol%%:*}: symbol=${symbol#*:} fi |
dbd1abb20 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 scripts/decode_st... |
99 100 |
if [[ "${cache[$module,$name]+isset}" == "isset" ]]; then local base_addr=${cache[$module,$name]} |
dbd1abb20 decode_stacktrace... |
101 |
else |
f643b9ee9 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 scripts/decode_st... |
107 |
cache[$module,$name]="$base_addr" |
dbd1abb20 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 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 decode_stacktrace... |
126 |
else |
c04e32e91 scripts/decode_st... |
127 |
local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address") |
310c6dd06 scripts/decode_st... |
128 |
cache[$module,$address]=$code |
dbd1abb20 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 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 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 scripts/decode_st... |
145 |
symbol="$segment$name ($code)" |
dbd1abb20 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 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 decode_stacktrace... |
191 192 193 194 |
unset words[$last] parse_symbol # modifies $symbol # Add up the line number to the symbol |
310c6dd06 scripts/decode_st... |
195 |
echo "${words[@]}" "$symbol $module" |
dbd1abb20 decode_stacktrace... |
196 |
} |
ecda6e27f 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 decode_stacktrace... |
204 205 |
while read line; do # Let's see if we have an address in the line |
53938ee42 scripts/decode_st... |
206 207 |
if [[ $line =~ \[\<([^]]+)\>\] ]] || [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then |
dbd1abb20 decode_stacktrace... |
208 209 210 211 |
# Translate address to line numbers handle_line "$line" # Is it a code line? elif [[ $line == *Code:* ]]; then |
310c6dd06 scripts/decode_st... |
212 213 |
decode_code "$line" else |
dbd1abb20 decode_stacktrace... |
214 215 216 217 |
# Nothing special in this line, show it as is echo "$line" fi done |