drivers core: Fix oops when driver probe fails
[linux-2.6-microblaze.git] / scripts / decode_stacktrace.sh
index 0869def..5fbad61 100755 (executable)
@@ -3,18 +3,91 @@
 # (c) 2014, Sasha Levin <sasha.levin@oracle.com>
 #set -x
 
-if [[ $# < 2 ]]; then
+usage() {
        echo "Usage:"
-       echo "  $0 [vmlinux] [base path] [modules path]"
-       exit 1
+       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
 
-vmlinux=$1
-basepath=$2
-modpath=$3
 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
+                               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])
@@ -27,12 +100,11 @@ parse_symbol() {
        elif [[ "${modcache[$module]+isset}" == "isset" ]]; then
                local objfile=${modcache[$module]}
        else
-               if [[ $modpath == "" ]]; then
+               local objfile=$(find_module)
+               if [[ $objfile == "" ]] ; then
                        echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2
                        return
                fi
-               local objfile=$(find "$modpath" -name "${module//_/[-_]}.ko*" -print -quit)
-               [[ $objfile == "" ]] && return
                modcache[$module]=$objfile
        fi
 
@@ -56,7 +128,11 @@ parse_symbol() {
        if [[ "${cache[$module,$name]+isset}" == "isset" ]]; then
                local base_addr=${cache[$module,$name]}
        else
-               local base_addr=$(nm "$objfile" | grep -i ' t ' | awk "/ $name\$/ {print \$1}" | head -n1)
+               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"
        fi
        # Let's start doing the math to get the exact address into the
@@ -76,7 +152,7 @@ parse_symbol() {
        if [[ "${cache[$module,$address]+isset}" == "isset" ]]; then
                local code=${cache[$module,$address]}
        else
-               local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address")
+               local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address" 2>/dev/null)
                cache[$module,$address]=$code
        fi
 
@@ -97,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]}"`
 
@@ -104,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
@@ -129,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]
@@ -157,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"