summaryrefslogtreecommitdiff
path: root/scripts/decode_stacktrace.sh
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/decode_stacktrace.sh')
-rwxr-xr-xscripts/decode_stacktrace.sh160
1 files changed, 113 insertions, 47 deletions
diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh
index 5fbad61fe490..8d01b741de62 100755
--- a/scripts/decode_stacktrace.sh
+++ b/scripts/decode_stacktrace.sh
@@ -1,14 +1,43 @@
-#!/bin/bash
+#!/usr/bin/env 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>]"
+ echo " $0 -r <release>"
+ echo " $0 [<vmlinux> [<base_path>|auto [<modules_path>]]]"
+ echo " $0 -h"
}
-if [[ $1 == "-r" ]] ; then
+# Try to find a Rust demangler
+if type llvm-cxxfilt >/dev/null 2>&1 ; then
+ cppfilt=llvm-cxxfilt
+elif type c++filt >/dev/null 2>&1 ; then
+ cppfilt=c++filt
+ cppfilt_opts=-i
+fi
+
+UTIL_SUFFIX=
+if [[ -z ${LLVM:-} ]]; then
+ UTIL_PREFIX=${CROSS_COMPILE:-}
+else
+ UTIL_PREFIX=llvm-
+ if [[ ${LLVM} == */ ]]; then
+ UTIL_PREFIX=${LLVM}${UTIL_PREFIX}
+ elif [[ ${LLVM} == -* ]]; then
+ UTIL_SUFFIX=${LLVM}
+ fi
+fi
+
+READELF=${UTIL_PREFIX}readelf${UTIL_SUFFIX}
+ADDR2LINE=${UTIL_PREFIX}addr2line${UTIL_SUFFIX}
+NM=${UTIL_PREFIX}nm${UTIL_SUFFIX}
+
+if [[ $1 == "-h" ]] ; then
+ usage
+ exit 0
+elif [[ $1 == "-r" ]] ; then
vmlinux=""
basepath="auto"
modpath=""
@@ -45,8 +74,13 @@ else
fi
fi
-declare -A cache
-declare -A modcache
+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
@@ -60,31 +94,32 @@ find_module() {
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
+ if [ -z $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
+ if [ -n "${release}" ] ; then
+ release_dirs="/usr/lib/debug/lib/modules/$release /lib/modules/$release"
+ fi
- for dn in {/usr/lib/debug,}/lib/modules/$release ; do
- if [ -e "$dn" ] ; then
- modpath="$dn"
- find_module && return
+ found_without_debug_info=false
+ for dir in "$modpath" "$(dirname "$vmlinux")" ${release_dirs}; do
+ if [ -n "${dir}" ] && [ -e "${dir}" ]; then
+ for fn in $(find "$dir" -name "${module//_/[-_]}.ko*") ; do
+ if ${READELF} -WS "$fn" | grep -qwF .debug_line ; then
+ echo $fn
+ return
+ fi
+ found_without_debug_info=true
+ done
fi
done
- modpath=""
+ if [[ ${found_without_debug_info} == true ]]; then
+ echo "WARNING! No debugging info in module ${module}, rebuild with DEBUG_KERNEL and DEBUG_INFO" >&2
+ else
+ echo "WARNING! Cannot find .ko for module ${module}, please pass a valid module path" >&2
+ fi
+
return 1
}
@@ -97,15 +132,16 @@ parse_symbol() {
if [[ $module == "" ]] ; then
local objfile=$vmlinux
- elif [[ "${modcache[$module]+isset}" == "isset" ]]; then
+ 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
- modcache[$module]=$objfile
+ if [[ $aarray_support == true ]]; then
+ modcache[$module]=$objfile
+ fi
fi
# Remove the englobing parenthesis
@@ -125,15 +161,17 @@ parse_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 [[ "${cache[$module,$name]+isset}" == "isset" ]]; then
+ 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}')
+ 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
- cache[$module,$name]="$base_addr"
+ 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.
@@ -149,11 +187,13 @@ parse_symbol() {
# Pass it to addr2line to get filename and line number
# Could get more than one result
- if [[ "${cache[$module,$address]+isset}" == "isset" ]]; then
+ 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)
- cache[$module,$address]=$code
+ local code=$(${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
@@ -169,6 +209,12 @@ parse_symbol() {
# In the case of inlines, move everything to same line
code=${code//$'\n'/' '}
+ # Demangle if the name looks like a Rust symbol and if
+ # we got a Rust demangler
+ if [[ $name =~ ^_R && $cppfilt != "" ]] ; then
+ name=$("$cppfilt" "$cppfilt_opts" "$name")
+ fi
+
# Replace old address with pretty line numbers
symbol="$segment$name ($code)"
}
@@ -196,8 +242,10 @@ debuginfod_get_vmlinux() {
decode_code() {
local scripts=`dirname "${BASH_SOURCE[0]}"`
+ local lim="Code: "
- echo "$1" | $scripts/decodecode
+ echo -n "${1%%${lim}*}"
+ echo "${lim}${1##*${lim}}" | $scripts/decodecode
}
handle_line() {
@@ -209,10 +257,11 @@ handle_line() {
basepath=${basepath%/init/main.c:*)}
fi
- local words
+ local words spaces
- # Tokenize
- read -a words <<<"$1"
+ # Tokenize: words and spaces to preserve the alignment
+ read -ra words <<<"$1"
+ IFS='#' read -ra spaces <<<"$(shopt -s extglob; echo "${1//+([^[:space:]])/#}")"
# Remove hex numbers. Do it ourselves until it happens in the
# kernel
@@ -224,24 +273,35 @@ handle_line() {
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]}")
+ unset words[$i] spaces[$i]
fi
done
+ # Extract info after the symbol if present. E.g.:
+ # func_name+0x54/0x80 (P)
+ # ^^^
+ # The regex assumes only uppercase letters will be used. To be
+ # extended if needed.
+ local info_str=""
+ if [[ ${words[$last]} =~ \([A-Z]*\) ]]; then
+ info_str=${words[$last]}
+ unset words[$last] spaces[$last]
+ last=$(( $last - 1 ))
+ fi
+
+ # Join module name with its build id if present, as these were
+ # split during tokenization (e.g. "[module" and "modbuildid]").
if [[ ${words[$last]} =~ ^[0-9a-f]+\] ]]; then
words[$last-1]="${words[$last-1]} ${words[$last]}"
- unset words[$last]
+ unset words[$last] spaces[$last]
last=$(( $last - 1 ))
fi
if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then
module=${words[$last]}
+ # some traces format is "(%pS)", which like "(foo+0x0/0x1 [bar])"
+ # so $module may like "[bar])". Strip the right parenthesis firstly
+ module=${module%\)}
module=${module#\[}
module=${module%\]}
modbuildid=${module#* }
@@ -250,7 +310,7 @@ handle_line() {
modbuildid=
fi
symbol=${words[$last-1]}
- unset words[$last-1]
+ unset words[$last-1] spaces[$last-1]
else
# The symbol is the last element, process it
symbol=${words[$last]}
@@ -262,10 +322,16 @@ handle_line() {
parse_symbol # modifies $symbol
# Add up the line number to the symbol
- echo "${words[@]}" "$symbol $module"
+ for i in "${!words[@]}"; do
+ echo -n "${spaces[i]}${words[i]}"
+ done
+ echo "${spaces[$last]}${symbol}${module:+ ${module}}${info_str:+ ${info_str}}"
}
while read line; do
+ # Strip unexpected carriage return at end of line
+ line=${line%$'\r'}
+
# Let's see if we have an address in the line
if [[ $line =~ \[\<([^]]+)\>\] ]] ||
[[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then