linux-stable/scripts/decode_stacktrace.sh
Schspa Shi 3af8acf6af scripts/decode_stacktrace.sh: support old bash version
Old bash version don't support associative array variables.  Avoid to use
associative array variables to avoid error.

Without this, old bash version will report error as fellowing
[   15.954042] Kernel panic - not syncing: sysrq triggered crash
[   15.955252] CPU: 1 PID: 167 Comm: sh Not tainted 5.18.0-rc1-00208-gb7d075db2fd5 #4
[   15.956472] Hardware name: Hobot J5 Virtual development board (DT)
[   15.957856] Call trace:
./scripts/decode_stacktrace.sh: line 128: ,dump_backtrace: syntax error: operand expected (error token is ",dump_backtrace")

Link: https://lkml.kernel.org/r/20220409180331.24047-1-schspa@gmail.com
Signed-off-by: Schspa Shi <schspa@gmail.com>
Cc: Stephen Boyd <swboyd@chromium.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2022-04-29 14:37:57 -07:00

295 lines
6.9 KiB
Bash
Executable file

#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# (c) 2014, Sasha Levin <sasha.levin@oracle.com>
#set -x
usage() {
echo "Usage:"
echo " $0 -r <release> | <vmlinux> [<base path>|auto] [<modules path>]"
}
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
usage
exit 2
fi
else
vmlinux=$1
basepath=${2-auto}
modpath=$3
release=""
debuginfod=
# Can we use debuginfod-find?
if type debuginfod-find >/dev/null 2>&1 ; then
debuginfod=${1-only}
fi
if [[ $vmlinux == "" && -z $debuginfod ]] ; then
echo "ERROR! vmlinux image must be specified" >&2
usage
exit 1
fi
fi
declare aarray_support=true
declare -A cache 2>/dev/null
if [[ $? != 0 ]]; then
aarray_support=false
else
declare -A modcache
fi
find_module() {
if [[ -n $debuginfod ]] ; then
if [[ -n $modbuildid ]] ; then
debuginfod-find debuginfo $modbuildid && return
fi
# Only using debuginfod so don't try to find vmlinux module path
if [[ $debuginfod == "only" ]] ; then
return
fi
fi
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" 2>/dev/null | 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
}
parse_symbol() {
# The structure of symbol at this point is:
# ([name]+[offset]/[total length])
#
# For example:
# do_basic_setup+0x9c/0xbf
if [[ $module == "" ]] ; then
local objfile=$vmlinux
elif [[ $aarray_support == true && "${modcache[$module]+isset}" == "isset" ]]; then
local objfile=${modcache[$module]}
else
local objfile=$(find_module)
if [[ $objfile == "" ]] ; then
echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2
return
fi
if [[ $aarray_support == true ]]; then
modcache[$module]=$objfile
fi
fi
# Remove the englobing parenthesis
symbol=${symbol#\(}
symbol=${symbol%\)}
# Strip segment
local segment
if [[ $symbol == *:* ]] ; then
segment=${symbol%%:*}:
symbol=${symbol#*:}
fi
# 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.
if [[ $aarray_support == true && "${cache[$module,$name]+isset}" == "isset" ]]; then
local base_addr=${cache[$module,$name]}
else
local base_addr=$(nm "$objfile" 2>/dev/null | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}')
if [[ $base_addr == "" ]] ; then
# address not found
return
fi
if [[ $aarray_support == true ]]; then
cache[$module,$name]="$base_addr"
fi
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\n" "$expr")
# Pass it to addr2line to get filename and line number
# Could get more than one result
if [[ $aarray_support == true && "${cache[$module,$address]+isset}" == "isset" ]]; then
local code=${cache[$module,$address]}
else
local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address" 2>/dev/null)
if [[ $aarray_support == true ]]; then
cache[$module,$address]=$code
fi
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
# Strip out the base of the path on each line
code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code")
# In the case of inlines, move everything to same line
code=${code//$'\n'/' '}
# Replace old address with pretty line numbers
symbol="$segment$name ($code)"
}
debuginfod_get_vmlinux() {
local vmlinux_buildid=${1##* }
if [[ $vmlinux != "" ]]; then
return
fi
if [[ $vmlinux_buildid =~ ^[0-9a-f]+ ]]; then
vmlinux=$(debuginfod-find debuginfo $vmlinux_buildid)
if [[ $? -ne 0 ]] ; then
echo "ERROR! vmlinux image not found via debuginfod-find" >&2
usage
exit 2
fi
return
fi
echo "ERROR! Build ID for vmlinux not found. Try passing -r or specifying vmlinux" >&2
usage
exit 2
}
decode_code() {
local scripts=`dirname "${BASH_SOURCE[0]}"`
echo "$1" | $scripts/decodecode
}
handle_line() {
if [[ $basepath == "auto" && $vmlinux != "" ]] ; then
module=""
symbol="kernel_init+0x0/0x0"
parse_symbol
basepath=${symbol#kernel_init (}
basepath=${basepath%/init/main.c:*)}
fi
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\n" "${words[$i+1]}")
fi
done
if [[ ${words[$last]} =~ ^[0-9a-f]+\] ]]; then
words[$last-1]="${words[$last-1]} ${words[$last]}"
unset words[$last]
last=$(( $last - 1 ))
fi
if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then
module=${words[$last]}
module=${module#\[}
module=${module%\]}
modbuildid=${module#* }
module=${module% *}
if [[ $modbuildid == $module ]]; then
modbuildid=
fi
symbol=${words[$last-1]}
unset words[$last-1]
else
# The symbol is the last element, process it
symbol=${words[$last]}
module=
modbuildid=
fi
unset words[$last]
parse_symbol # modifies $symbol
# Add up the line number to the symbol
echo "${words[@]}" "$symbol $module"
}
while read line; do
# Let's see if we have an address in the line
if [[ $line =~ \[\<([^]]+)\>\] ]] ||
[[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then
# Translate address to line numbers
handle_line "$line"
# Is it a code line?
elif [[ $line == *Code:* ]]; then
decode_code "$line"
# Is it a version line?
elif [[ -n $debuginfod && $line =~ PID:\ [0-9]+\ Comm: ]]; then
debuginfod_get_vmlinux "$line"
else
# Nothing special in this line, show it as is
echo "$line"
fi
done