[PATCH v3 07/12] scripts/decode_stacktrace.sh: Support debuginfod

Stephen Boyd
2021-03-31T03:06:12Z

Now that stacktraces contain the build ID information we can update this
script to use debuginfod-find to locate the debuginfo for the vmlinux
and modules automatically. This can replace the existing code that
requires specifying a path to vmlinux or tries to find the vmlinux and
modules automatically by using the release number. Work it into the
script as a fallback option if the vmlinux isn't specified on the
commandline.

Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Jessica Yu <jeyu@kernel.org>
Cc: Evan Green <evgreen@chromium.org>
Cc: Hsin-Yi Wang <hsinyi@chromium.org>
Cc: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
Cc: Sasha Levin <sashal@kernel.org>
Cc: Petr Mladek <pmladek@suse.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
---
scripts/decode_stacktrace.sh | 81 +++++++++++++++++++++++++++++++-----
1 file changed, 70 insertions(+), 11 deletions(-)

diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh
index 90398347e366..ca21f8bdf5f2 100755
--- a/scripts/decode_stacktrace.sh
+++ b/scripts/decode_stacktrace.sh
@@ -3,11 +3,10 @@
# (c) 2014, Sasha Levin <sasha.levin@oracle.com>
#set -x

-if [[ $# < 1 ]]; then
+usage() {
echo "Usage:"
echo " $0 -r <release> | <vmlinux> [base path] [modules path]"
- exit 1
-fi
+}

if [[ $1 == "-r" ]] ; then
vmlinux=""
@@ -24,6 +23,7 @@ if [[ $1 == "-r" ]] ; then

if [[ $vmlinux == "" ]] ; then
echo "ERROR! vmlinux image for release $release is not found" >&2
+ usage
exit 2
fi
else
@@ -31,12 +31,35 @@ else
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 -A cache
declare -A modcache

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
@@ -150,6 +173,27 @@ parse_symbol() {
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]}"`

@@ -157,6 +201,14 @@ decode_code() {
}

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
@@ -182,16 +234,28 @@ handle_line() {
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]
@@ -201,14 +265,6 @@ handle_line() {
echo "${words[@]}" "$symbol $module"
}

-if [[ $basepath == "auto" ]] ; then
- module=""
- symbol="kernel_init+0x0/0x0"
- parse_symbol
- basepath=${symbol#kernel_init (}
- basepath=${basepath%/init/main.c:*)}
-fi
-
while read line; do
# Let's see if we have an address in the line
if [[ $line =~ \[\<([^]]+)\>\] ]] ||
@@ -218,6 +274,9 @@ while read line; do
# 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"
--