^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) #!/bin/bash
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) # SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) # (c) 2014, Sasha Levin <sasha.levin@oracle.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) #set -x
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) if [[ $# < 1 ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) echo "Usage:"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) echo " $0 -r <release> | <vmlinux> [base path] [modules path]"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) exit 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) if [[ $1 == "-r" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) vmlinux=""
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) basepath="auto"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) modpath=""
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) release=$2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) for fn in {,/usr/lib/debug}/boot/vmlinux-$release{,.debug} /lib/modules/$release{,/build}/vmlinux ; do
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) if [ -e "$fn" ] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) vmlinux=$fn
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) break
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) done
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) if [[ $vmlinux == "" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) echo "ERROR! vmlinux image for release $release is not found" >&2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) exit 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) vmlinux=$1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) basepath=${2-auto}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) modpath=$3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) release=""
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) declare -A cache
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) declare -A modcache
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) find_module() {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) if [[ "$modpath" != "" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) if readelf -WS "$fn" | grep -qwF .debug_line ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) echo $fn
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) done
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) return 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) modpath=$(dirname "$vmlinux")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) find_module && return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) if [[ $release == "" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" | sed -n 's/\$1 = "\(.*\)".*/\1/p')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) for dn in {/usr/lib/debug,}/lib/modules/$release ; do
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) if [ -e "$dn" ] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) modpath="$dn"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) find_module && return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) done
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) modpath=""
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) return 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) parse_symbol() {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) # The structure of symbol at this point is:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) # ([name]+[offset]/[total length])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) #
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) # For example:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) # do_basic_setup+0x9c/0xbf
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) if [[ $module == "" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) local objfile=$vmlinux
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) elif [[ "${modcache[$module]+isset}" == "isset" ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) local objfile=${modcache[$module]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) local objfile=$(find_module)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) if [[ $objfile == "" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) modcache[$module]=$objfile
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) # Remove the englobing parenthesis
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) symbol=${symbol#\(}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) symbol=${symbol%\)}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) # Strip segment
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) local segment
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) if [[ $symbol == *:* ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) segment=${symbol%%:*}:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) symbol=${symbol#*:}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) # Strip the symbol name so that we could look it up
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) local name=${symbol%+*}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) # Use 'nm vmlinux' to figure out the base address of said symbol.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) # It's actually faster to call it every time than to load it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) # all into bash.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) if [[ "${cache[$module,$name]+isset}" == "isset" ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) local base_addr=${cache[$module,$name]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) local base_addr=$(nm "$objfile" | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) if [[ $base_addr == "" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) # address not found
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) cache[$module,$name]="$base_addr"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) # Let's start doing the math to get the exact address into the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) # symbol. First, strip out the symbol total length.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) local expr=${symbol%/*}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) # Now, replace the symbol name with the base address we found
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) # before.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) expr=${expr/$name/0x$base_addr}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) # Evaluate it to find the actual address
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) expr=$((expr))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) local address=$(printf "%x\n" "$expr")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) # Pass it to addr2line to get filename and line number
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) # Could get more than one result
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) if [[ "${cache[$module,$address]+isset}" == "isset" ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) local code=${cache[$module,$address]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) cache[$module,$address]=$code
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) # addr2line doesn't return a proper error code if it fails, so
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) # we detect it using the value it prints so that we could preserve
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) # the offset/size into the function and bail out
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) if [[ $code == "??:0" ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) # Strip out the base of the path on each line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) # In the case of inlines, move everything to same line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) code=${code//$'\n'/' '}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) # Replace old address with pretty line numbers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) symbol="$segment$name ($code)"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) decode_code() {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) local scripts=`dirname "${BASH_SOURCE[0]}"`
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) echo "$1" | $scripts/decodecode
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) handle_line() {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) local words
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) # Tokenize
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) read -a words <<<"$1"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) # Remove hex numbers. Do it ourselves until it happens in the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) # kernel
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) # We need to know the index of the last element before we
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) # remove elements because arrays are sparse
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) local last=$(( ${#words[@]} - 1 ))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) for i in "${!words[@]}"; do
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) # Remove the address
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) unset words[$i]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) # Format timestamps with tabs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) unset words[$i]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) done
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) module=${words[$last]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) module=${module#\[}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) module=${module%\]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) symbol=${words[$last-1]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) unset words[$last-1]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) # The symbol is the last element, process it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) symbol=${words[$last]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) module=
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) unset words[$last]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) parse_symbol # modifies $symbol
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) # Add up the line number to the symbol
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) echo "${words[@]}" "$symbol $module"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) if [[ $basepath == "auto" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) module=""
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) symbol="kernel_init+0x0/0x0"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) parse_symbol
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) basepath=${symbol#kernel_init (}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) basepath=${basepath%/init/main.c:*)}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) while read line; do
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) # Let's see if we have an address in the line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) if [[ $line =~ \[\<([^]]+)\>\] ]] ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) # Translate address to line numbers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) handle_line "$line"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) # Is it a code line?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) elif [[ $line == *Code:* ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) decode_code "$line"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) # Nothing special in this line, show it as is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) echo "$line"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) done