1dbd1abb2SSasha Levin#!/bin/bash 2b2441318SGreg Kroah-Hartman# SPDX-License-Identifier: GPL-2.0 3dbd1abb2SSasha Levin# (c) 2014, Sasha Levin <[email protected]> 4dbd1abb2SSasha Levin#set -x 5dbd1abb2SSasha Levin 626681eb3SStephen Boydusage() { 7dbd1abb2SSasha Levin echo "Usage:" 8d5ce757dSStephen Boyd echo " $0 -r <release> | <vmlinux> [<base path>|auto] [<modules path>]" 926681eb3SStephen Boyd} 10dbd1abb2SSasha Levin 1199115db4SMiguel Ojeda# Try to find a Rust demangler 1299115db4SMiguel Ojedaif type llvm-cxxfilt >/dev/null 2>&1 ; then 1399115db4SMiguel Ojeda cppfilt=llvm-cxxfilt 1499115db4SMiguel Ojedaelif type c++filt >/dev/null 2>&1 ; then 1599115db4SMiguel Ojeda cppfilt=c++filt 1699115db4SMiguel Ojeda cppfilt_opts=-i 1799115db4SMiguel Ojedafi 1899115db4SMiguel Ojeda 19*efbd6398SCarlos LlamasUTIL_SUFFIX= 20*efbd6398SCarlos Llamasif [[ -z ${LLVM:-} ]]; then 21*efbd6398SCarlos Llamas UTIL_PREFIX=${CROSS_COMPILE:-} 22*efbd6398SCarlos Llamaselse 23*efbd6398SCarlos Llamas UTIL_PREFIX=llvm- 24*efbd6398SCarlos Llamas if [[ ${LLVM} == */ ]]; then 25*efbd6398SCarlos Llamas UTIL_PREFIX=${LLVM}${UTIL_PREFIX} 26*efbd6398SCarlos Llamas elif [[ ${LLVM} == -* ]]; then 27*efbd6398SCarlos Llamas UTIL_SUFFIX=${LLVM} 28*efbd6398SCarlos Llamas fi 29*efbd6398SCarlos Llamasfi 30*efbd6398SCarlos Llamas 31*efbd6398SCarlos LlamasREADELF=${UTIL_PREFIX}readelf${UTIL_SUFFIX} 32*efbd6398SCarlos LlamasADDR2LINE=${UTIL_PREFIX}addr2line${UTIL_SUFFIX} 33*efbd6398SCarlos Llamas 34f90dde44SKonstantin Khlebnikovif [[ $1 == "-r" ]] ; then 35f90dde44SKonstantin Khlebnikov vmlinux="" 36f90dde44SKonstantin Khlebnikov basepath="auto" 37f90dde44SKonstantin Khlebnikov modpath="" 38f90dde44SKonstantin Khlebnikov release=$2 39f90dde44SKonstantin Khlebnikov 40f90dde44SKonstantin Khlebnikov for fn in {,/usr/lib/debug}/boot/vmlinux-$release{,.debug} /lib/modules/$release{,/build}/vmlinux ; do 41f90dde44SKonstantin Khlebnikov if [ -e "$fn" ] ; then 42f90dde44SKonstantin Khlebnikov vmlinux=$fn 43f90dde44SKonstantin Khlebnikov break 44f90dde44SKonstantin Khlebnikov fi 45f90dde44SKonstantin Khlebnikov done 46f90dde44SKonstantin Khlebnikov 47f90dde44SKonstantin Khlebnikov if [[ $vmlinux == "" ]] ; then 48f90dde44SKonstantin Khlebnikov echo "ERROR! vmlinux image for release $release is not found" >&2 4926681eb3SStephen Boyd usage 50f90dde44SKonstantin Khlebnikov exit 2 51f90dde44SKonstantin Khlebnikov fi 52f90dde44SKonstantin Khlebnikovelse 53dbd1abb2SSasha Levin vmlinux=$1 54ecda6e27SKonstantin Khlebnikov basepath=${2-auto} 55310c6dd0SKonstantin Khlebnikov modpath=$3 56431151b6SKonstantin Khlebnikov release="" 5726681eb3SStephen Boyd debuginfod= 5826681eb3SStephen Boyd 5926681eb3SStephen Boyd # Can we use debuginfod-find? 6026681eb3SStephen Boyd if type debuginfod-find >/dev/null 2>&1 ; then 6126681eb3SStephen Boyd debuginfod=${1-only} 6226681eb3SStephen Boyd fi 6326681eb3SStephen Boyd 6426681eb3SStephen Boyd if [[ $vmlinux == "" && -z $debuginfod ]] ; then 6526681eb3SStephen Boyd echo "ERROR! vmlinux image must be specified" >&2 6626681eb3SStephen Boyd usage 6726681eb3SStephen Boyd exit 1 6826681eb3SStephen Boyd fi 69f90dde44SKonstantin Khlebnikovfi 70431151b6SKonstantin Khlebnikov 713af8acf6SSchspa Shideclare aarray_support=true 723af8acf6SSchspa Shideclare -A cache 2>/dev/null 733af8acf6SSchspa Shiif [[ $? != 0 ]]; then 743af8acf6SSchspa Shi aarray_support=false 753af8acf6SSchspa Shielse 76310c6dd0SKonstantin Khlebnikov declare -A modcache 773af8acf6SSchspa Shifi 78dbd1abb2SSasha Levin 79431151b6SKonstantin Khlebnikovfind_module() { 8026681eb3SStephen Boyd if [[ -n $debuginfod ]] ; then 8126681eb3SStephen Boyd if [[ -n $modbuildid ]] ; then 8226681eb3SStephen Boyd debuginfod-find debuginfo $modbuildid && return 8326681eb3SStephen Boyd fi 8426681eb3SStephen Boyd 8526681eb3SStephen Boyd # Only using debuginfod so don't try to find vmlinux module path 8626681eb3SStephen Boyd if [[ $debuginfod == "only" ]] ; then 8726681eb3SStephen Boyd return 8826681eb3SStephen Boyd fi 8926681eb3SStephen Boyd fi 9026681eb3SStephen Boyd 91431151b6SKonstantin Khlebnikov if [[ "$modpath" != "" ]] ; then 92431151b6SKonstantin Khlebnikov for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do 93*efbd6398SCarlos Llamas if ${READELF} -WS "$fn" | grep -qwF .debug_line ; then 94431151b6SKonstantin Khlebnikov echo $fn 95431151b6SKonstantin Khlebnikov return 96431151b6SKonstantin Khlebnikov fi 97431151b6SKonstantin Khlebnikov done 98431151b6SKonstantin Khlebnikov return 1 99431151b6SKonstantin Khlebnikov fi 100431151b6SKonstantin Khlebnikov 101431151b6SKonstantin Khlebnikov modpath=$(dirname "$vmlinux") 102431151b6SKonstantin Khlebnikov find_module && return 103431151b6SKonstantin Khlebnikov 104431151b6SKonstantin Khlebnikov if [[ $release == "" ]] ; then 1055bf0f3bcSStephen Boyd release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" 2>/dev/null | sed -n 's/\$1 = "\(.*\)".*/\1/p') 106431151b6SKonstantin Khlebnikov fi 107431151b6SKonstantin Khlebnikov 108431151b6SKonstantin Khlebnikov for dn in {/usr/lib/debug,}/lib/modules/$release ; do 109431151b6SKonstantin Khlebnikov if [ -e "$dn" ] ; then 110431151b6SKonstantin Khlebnikov modpath="$dn" 111431151b6SKonstantin Khlebnikov find_module && return 112431151b6SKonstantin Khlebnikov fi 113431151b6SKonstantin Khlebnikov done 114431151b6SKonstantin Khlebnikov 115431151b6SKonstantin Khlebnikov modpath="" 116431151b6SKonstantin Khlebnikov return 1 117431151b6SKonstantin Khlebnikov} 118431151b6SKonstantin Khlebnikov 119dbd1abb2SSasha Levinparse_symbol() { 120dbd1abb2SSasha Levin # The structure of symbol at this point is: 121e260fe01SRobert Jarzmik # ([name]+[offset]/[total length]) 122dbd1abb2SSasha Levin # 123dbd1abb2SSasha Levin # For example: 124dbd1abb2SSasha Levin # do_basic_setup+0x9c/0xbf 125dbd1abb2SSasha Levin 126310c6dd0SKonstantin Khlebnikov if [[ $module == "" ]] ; then 127310c6dd0SKonstantin Khlebnikov local objfile=$vmlinux 1283af8acf6SSchspa Shi elif [[ $aarray_support == true && "${modcache[$module]+isset}" == "isset" ]]; then 129310c6dd0SKonstantin Khlebnikov local objfile=${modcache[$module]} 130310c6dd0SKonstantin Khlebnikov else 131431151b6SKonstantin Khlebnikov local objfile=$(find_module) 132431151b6SKonstantin Khlebnikov if [[ $objfile == "" ]] ; then 133a5dc8300SSasha Levin echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2 134a5dc8300SSasha Levin return 135a5dc8300SSasha Levin fi 1363af8acf6SSchspa Shi if [[ $aarray_support == true ]]; then 137310c6dd0SKonstantin Khlebnikov modcache[$module]=$objfile 138310c6dd0SKonstantin Khlebnikov fi 1393af8acf6SSchspa Shi fi 140310c6dd0SKonstantin Khlebnikov 141e260fe01SRobert Jarzmik # Remove the englobing parenthesis 142e260fe01SRobert Jarzmik symbol=${symbol#\(} 143e260fe01SRobert Jarzmik symbol=${symbol%\)} 144dbd1abb2SSasha Levin 1451d6693fbSKonstantin Khlebnikov # Strip segment 1461d6693fbSKonstantin Khlebnikov local segment 1471d6693fbSKonstantin Khlebnikov if [[ $symbol == *:* ]] ; then 1481d6693fbSKonstantin Khlebnikov segment=${symbol%%:*}: 1491d6693fbSKonstantin Khlebnikov symbol=${symbol#*:} 1501d6693fbSKonstantin Khlebnikov fi 1511d6693fbSKonstantin Khlebnikov 152dbd1abb2SSasha Levin # Strip the symbol name so that we could look it up 153dbd1abb2SSasha Levin local name=${symbol%+*} 154dbd1abb2SSasha Levin 155dbd1abb2SSasha Levin # Use 'nm vmlinux' to figure out the base address of said symbol. 156dbd1abb2SSasha Levin # It's actually faster to call it every time than to load it 157dbd1abb2SSasha Levin # all into bash. 1583af8acf6SSchspa Shi if [[ $aarray_support == true && "${cache[$module,$name]+isset}" == "isset" ]]; then 159310c6dd0SKonstantin Khlebnikov local base_addr=${cache[$module,$name]} 160dbd1abb2SSasha Levin else 1615bf0f3bcSStephen Boyd local base_addr=$(nm "$objfile" 2>/dev/null | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}') 162f643b9eeSKonstantin Khlebnikov if [[ $base_addr == "" ]] ; then 163f643b9eeSKonstantin Khlebnikov # address not found 164f643b9eeSKonstantin Khlebnikov return 165f643b9eeSKonstantin Khlebnikov fi 1663af8acf6SSchspa Shi if [[ $aarray_support == true ]]; then 167310c6dd0SKonstantin Khlebnikov cache[$module,$name]="$base_addr" 168dbd1abb2SSasha Levin fi 1693af8acf6SSchspa Shi fi 170dbd1abb2SSasha Levin # Let's start doing the math to get the exact address into the 171dbd1abb2SSasha Levin # symbol. First, strip out the symbol total length. 172dbd1abb2SSasha Levin local expr=${symbol%/*} 173dbd1abb2SSasha Levin 174dbd1abb2SSasha Levin # Now, replace the symbol name with the base address we found 175dbd1abb2SSasha Levin # before. 176dbd1abb2SSasha Levin expr=${expr/$name/0x$base_addr} 177dbd1abb2SSasha Levin 178dbd1abb2SSasha Levin # Evaluate it to find the actual address 179dbd1abb2SSasha Levin expr=$((expr)) 180dbd1abb2SSasha Levin local address=$(printf "%x\n" "$expr") 181dbd1abb2SSasha Levin 182dbd1abb2SSasha Levin # Pass it to addr2line to get filename and line number 183dbd1abb2SSasha Levin # Could get more than one result 1843af8acf6SSchspa Shi if [[ $aarray_support == true && "${cache[$module,$address]+isset}" == "isset" ]]; then 185310c6dd0SKonstantin Khlebnikov local code=${cache[$module,$address]} 186dbd1abb2SSasha Levin else 187*efbd6398SCarlos Llamas local code=$(${ADDR2LINE} -i -e "$objfile" "$address" 2>/dev/null) 1883af8acf6SSchspa Shi if [[ $aarray_support == true ]]; then 189310c6dd0SKonstantin Khlebnikov cache[$module,$address]=$code 190dbd1abb2SSasha Levin fi 1913af8acf6SSchspa Shi fi 192dbd1abb2SSasha Levin 193dbd1abb2SSasha Levin # addr2line doesn't return a proper error code if it fails, so 194dbd1abb2SSasha Levin # we detect it using the value it prints so that we could preserve 195dbd1abb2SSasha Levin # the offset/size into the function and bail out 196dbd1abb2SSasha Levin if [[ $code == "??:0" ]]; then 197dbd1abb2SSasha Levin return 198dbd1abb2SSasha Levin fi 199dbd1abb2SSasha Levin 200d178770dSPi-Hsun Shih # Strip out the base of the path on each line 201d178770dSPi-Hsun Shih code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code") 202dbd1abb2SSasha Levin 203dbd1abb2SSasha Levin # In the case of inlines, move everything to same line 204dbd1abb2SSasha Levin code=${code//$'\n'/' '} 205dbd1abb2SSasha Levin 20699115db4SMiguel Ojeda # Demangle if the name looks like a Rust symbol and if 20799115db4SMiguel Ojeda # we got a Rust demangler 20899115db4SMiguel Ojeda if [[ $name =~ ^_R && $cppfilt != "" ]] ; then 20999115db4SMiguel Ojeda name=$("$cppfilt" "$cppfilt_opts" "$name") 21099115db4SMiguel Ojeda fi 21199115db4SMiguel Ojeda 212dbd1abb2SSasha Levin # Replace old address with pretty line numbers 2131d6693fbSKonstantin Khlebnikov symbol="$segment$name ($code)" 214dbd1abb2SSasha Levin} 215dbd1abb2SSasha Levin 21626681eb3SStephen Boyddebuginfod_get_vmlinux() { 21726681eb3SStephen Boyd local vmlinux_buildid=${1##* } 21826681eb3SStephen Boyd 21926681eb3SStephen Boyd if [[ $vmlinux != "" ]]; then 22026681eb3SStephen Boyd return 22126681eb3SStephen Boyd fi 22226681eb3SStephen Boyd 22326681eb3SStephen Boyd if [[ $vmlinux_buildid =~ ^[0-9a-f]+ ]]; then 22426681eb3SStephen Boyd vmlinux=$(debuginfod-find debuginfo $vmlinux_buildid) 22526681eb3SStephen Boyd if [[ $? -ne 0 ]] ; then 22626681eb3SStephen Boyd echo "ERROR! vmlinux image not found via debuginfod-find" >&2 22726681eb3SStephen Boyd usage 22826681eb3SStephen Boyd exit 2 22926681eb3SStephen Boyd fi 23026681eb3SStephen Boyd return 23126681eb3SStephen Boyd fi 23226681eb3SStephen Boyd echo "ERROR! Build ID for vmlinux not found. Try passing -r or specifying vmlinux" >&2 23326681eb3SStephen Boyd usage 23426681eb3SStephen Boyd exit 2 23526681eb3SStephen Boyd} 23626681eb3SStephen Boyd 237dbd1abb2SSasha Levindecode_code() { 238dbd1abb2SSasha Levin local scripts=`dirname "${BASH_SOURCE[0]}"` 239dbd1abb2SSasha Levin 240dbd1abb2SSasha Levin echo "$1" | $scripts/decodecode 241dbd1abb2SSasha Levin} 242dbd1abb2SSasha Levin 243dbd1abb2SSasha Levinhandle_line() { 24426681eb3SStephen Boyd if [[ $basepath == "auto" && $vmlinux != "" ]] ; then 24526681eb3SStephen Boyd module="" 24626681eb3SStephen Boyd symbol="kernel_init+0x0/0x0" 24726681eb3SStephen Boyd parse_symbol 24826681eb3SStephen Boyd basepath=${symbol#kernel_init (} 24926681eb3SStephen Boyd basepath=${basepath%/init/main.c:*)} 25026681eb3SStephen Boyd fi 25126681eb3SStephen Boyd 252dbd1abb2SSasha Levin local words 253dbd1abb2SSasha Levin 254dbd1abb2SSasha Levin # Tokenize 255dbd1abb2SSasha Levin read -a words <<<"$1" 256dbd1abb2SSasha Levin 257dbd1abb2SSasha Levin # Remove hex numbers. Do it ourselves until it happens in the 258dbd1abb2SSasha Levin # kernel 259dbd1abb2SSasha Levin 260dbd1abb2SSasha Levin # We need to know the index of the last element before we 261dbd1abb2SSasha Levin # remove elements because arrays are sparse 262dbd1abb2SSasha Levin local last=$(( ${#words[@]} - 1 )) 263dbd1abb2SSasha Levin 264dbd1abb2SSasha Levin for i in "${!words[@]}"; do 265dbd1abb2SSasha Levin # Remove the address 266dbd1abb2SSasha Levin if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then 267dbd1abb2SSasha Levin unset words[$i] 268dbd1abb2SSasha Levin fi 269dbd1abb2SSasha Levin 270dbd1abb2SSasha Levin # Format timestamps with tabs 271dbd1abb2SSasha Levin if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then 272dbd1abb2SSasha Levin unset words[$i] 273dbd1abb2SSasha Levin words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}") 274dbd1abb2SSasha Levin fi 275dbd1abb2SSasha Levin done 276dbd1abb2SSasha Levin 27726681eb3SStephen Boyd if [[ ${words[$last]} =~ ^[0-9a-f]+\] ]]; then 27826681eb3SStephen Boyd words[$last-1]="${words[$last-1]} ${words[$last]}" 27926681eb3SStephen Boyd unset words[$last] 28026681eb3SStephen Boyd last=$(( $last - 1 )) 28126681eb3SStephen Boyd fi 28226681eb3SStephen Boyd 283310c6dd0SKonstantin Khlebnikov if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then 284310c6dd0SKonstantin Khlebnikov module=${words[$last]} 285310c6dd0SKonstantin Khlebnikov module=${module#\[} 286310c6dd0SKonstantin Khlebnikov module=${module%\]} 28726681eb3SStephen Boyd modbuildid=${module#* } 28826681eb3SStephen Boyd module=${module% *} 28926681eb3SStephen Boyd if [[ $modbuildid == $module ]]; then 29026681eb3SStephen Boyd modbuildid= 29126681eb3SStephen Boyd fi 292310c6dd0SKonstantin Khlebnikov symbol=${words[$last-1]} 293310c6dd0SKonstantin Khlebnikov unset words[$last-1] 294310c6dd0SKonstantin Khlebnikov else 295dbd1abb2SSasha Levin # The symbol is the last element, process it 296dbd1abb2SSasha Levin symbol=${words[$last]} 297310c6dd0SKonstantin Khlebnikov module= 29826681eb3SStephen Boyd modbuildid= 299310c6dd0SKonstantin Khlebnikov fi 300310c6dd0SKonstantin Khlebnikov 301dbd1abb2SSasha Levin unset words[$last] 302dbd1abb2SSasha Levin parse_symbol # modifies $symbol 303dbd1abb2SSasha Levin 304dbd1abb2SSasha Levin # Add up the line number to the symbol 305310c6dd0SKonstantin Khlebnikov echo "${words[@]}" "$symbol $module" 306dbd1abb2SSasha Levin} 307dbd1abb2SSasha Levin 308dbd1abb2SSasha Levinwhile read line; do 309436efd9eSBjorn Andersson # Strip unexpected carriage return at end of line 310436efd9eSBjorn Andersson line=${line%$'\r'} 311436efd9eSBjorn Andersson 312dbd1abb2SSasha Levin # Let's see if we have an address in the line 31353938ee4SJosh Poimboeuf if [[ $line =~ \[\<([^]]+)\>\] ]] || 31453938ee4SJosh Poimboeuf [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then 315dbd1abb2SSasha Levin # Translate address to line numbers 316dbd1abb2SSasha Levin handle_line "$line" 317dbd1abb2SSasha Levin # Is it a code line? 318dbd1abb2SSasha Levin elif [[ $line == *Code:* ]]; then 319dbd1abb2SSasha Levin decode_code "$line" 32026681eb3SStephen Boyd # Is it a version line? 32126681eb3SStephen Boyd elif [[ -n $debuginfod && $line =~ PID:\ [0-9]+\ Comm: ]]; then 32226681eb3SStephen Boyd debuginfod_get_vmlinux "$line" 323dbd1abb2SSasha Levin else 324dbd1abb2SSasha Levin # Nothing special in this line, show it as is 325dbd1abb2SSasha Levin echo "$line" 326dbd1abb2SSasha Levin fi 327dbd1abb2SSasha Levindone 328