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:" 8f90dde44SKonstantin Khlebnikov echo " $0 -r <release> | <vmlinux> [base path] [modules path]" 926681eb3SStephen Boyd} 10dbd1abb2SSasha Levin 11f90dde44SKonstantin Khlebnikovif [[ $1 == "-r" ]] ; then 12f90dde44SKonstantin Khlebnikov vmlinux="" 13f90dde44SKonstantin Khlebnikov basepath="auto" 14f90dde44SKonstantin Khlebnikov modpath="" 15f90dde44SKonstantin Khlebnikov release=$2 16f90dde44SKonstantin Khlebnikov 17f90dde44SKonstantin Khlebnikov for fn in {,/usr/lib/debug}/boot/vmlinux-$release{,.debug} /lib/modules/$release{,/build}/vmlinux ; do 18f90dde44SKonstantin Khlebnikov if [ -e "$fn" ] ; then 19f90dde44SKonstantin Khlebnikov vmlinux=$fn 20f90dde44SKonstantin Khlebnikov break 21f90dde44SKonstantin Khlebnikov fi 22f90dde44SKonstantin Khlebnikov done 23f90dde44SKonstantin Khlebnikov 24f90dde44SKonstantin Khlebnikov if [[ $vmlinux == "" ]] ; then 25f90dde44SKonstantin Khlebnikov echo "ERROR! vmlinux image for release $release is not found" >&2 2626681eb3SStephen Boyd usage 27f90dde44SKonstantin Khlebnikov exit 2 28f90dde44SKonstantin Khlebnikov fi 29f90dde44SKonstantin Khlebnikovelse 30dbd1abb2SSasha Levin vmlinux=$1 31ecda6e27SKonstantin Khlebnikov basepath=${2-auto} 32310c6dd0SKonstantin Khlebnikov modpath=$3 33431151b6SKonstantin Khlebnikov release="" 3426681eb3SStephen Boyd debuginfod= 3526681eb3SStephen Boyd 3626681eb3SStephen Boyd # Can we use debuginfod-find? 3726681eb3SStephen Boyd if type debuginfod-find >/dev/null 2>&1 ; then 3826681eb3SStephen Boyd debuginfod=${1-only} 3926681eb3SStephen Boyd fi 4026681eb3SStephen Boyd 4126681eb3SStephen Boyd if [[ $vmlinux == "" && -z $debuginfod ]] ; then 4226681eb3SStephen Boyd echo "ERROR! vmlinux image must be specified" >&2 4326681eb3SStephen Boyd usage 4426681eb3SStephen Boyd exit 1 4526681eb3SStephen Boyd fi 46f90dde44SKonstantin Khlebnikovfi 47431151b6SKonstantin Khlebnikov 48dbd1abb2SSasha Levindeclare -A cache 49310c6dd0SKonstantin Khlebnikovdeclare -A modcache 50dbd1abb2SSasha Levin 51431151b6SKonstantin Khlebnikovfind_module() { 5226681eb3SStephen Boyd if [[ -n $debuginfod ]] ; then 5326681eb3SStephen Boyd if [[ -n $modbuildid ]] ; then 5426681eb3SStephen Boyd debuginfod-find debuginfo $modbuildid && return 5526681eb3SStephen Boyd fi 5626681eb3SStephen Boyd 5726681eb3SStephen Boyd # Only using debuginfod so don't try to find vmlinux module path 5826681eb3SStephen Boyd if [[ $debuginfod == "only" ]] ; then 5926681eb3SStephen Boyd return 6026681eb3SStephen Boyd fi 6126681eb3SStephen Boyd fi 6226681eb3SStephen Boyd 63431151b6SKonstantin Khlebnikov if [[ "$modpath" != "" ]] ; then 64431151b6SKonstantin Khlebnikov for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do 65431151b6SKonstantin Khlebnikov if readelf -WS "$fn" | grep -qwF .debug_line ; then 66431151b6SKonstantin Khlebnikov echo $fn 67431151b6SKonstantin Khlebnikov return 68431151b6SKonstantin Khlebnikov fi 69431151b6SKonstantin Khlebnikov done 70431151b6SKonstantin Khlebnikov return 1 71431151b6SKonstantin Khlebnikov fi 72431151b6SKonstantin Khlebnikov 73431151b6SKonstantin Khlebnikov modpath=$(dirname "$vmlinux") 74431151b6SKonstantin Khlebnikov find_module && return 75431151b6SKonstantin Khlebnikov 76431151b6SKonstantin Khlebnikov if [[ $release == "" ]] ; then 77*5bf0f3bcSStephen Boyd release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" 2>/dev/null | sed -n 's/\$1 = "\(.*\)".*/\1/p') 78431151b6SKonstantin Khlebnikov fi 79431151b6SKonstantin Khlebnikov 80431151b6SKonstantin Khlebnikov for dn in {/usr/lib/debug,}/lib/modules/$release ; do 81431151b6SKonstantin Khlebnikov if [ -e "$dn" ] ; then 82431151b6SKonstantin Khlebnikov modpath="$dn" 83431151b6SKonstantin Khlebnikov find_module && return 84431151b6SKonstantin Khlebnikov fi 85431151b6SKonstantin Khlebnikov done 86431151b6SKonstantin Khlebnikov 87431151b6SKonstantin Khlebnikov modpath="" 88431151b6SKonstantin Khlebnikov return 1 89431151b6SKonstantin Khlebnikov} 90431151b6SKonstantin Khlebnikov 91dbd1abb2SSasha Levinparse_symbol() { 92dbd1abb2SSasha Levin # The structure of symbol at this point is: 93e260fe01SRobert Jarzmik # ([name]+[offset]/[total length]) 94dbd1abb2SSasha Levin # 95dbd1abb2SSasha Levin # For example: 96dbd1abb2SSasha Levin # do_basic_setup+0x9c/0xbf 97dbd1abb2SSasha Levin 98310c6dd0SKonstantin Khlebnikov if [[ $module == "" ]] ; then 99310c6dd0SKonstantin Khlebnikov local objfile=$vmlinux 100310c6dd0SKonstantin Khlebnikov elif [[ "${modcache[$module]+isset}" == "isset" ]]; then 101310c6dd0SKonstantin Khlebnikov local objfile=${modcache[$module]} 102310c6dd0SKonstantin Khlebnikov else 103431151b6SKonstantin Khlebnikov local objfile=$(find_module) 104431151b6SKonstantin Khlebnikov if [[ $objfile == "" ]] ; then 105a5dc8300SSasha Levin echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2 106a5dc8300SSasha Levin return 107a5dc8300SSasha Levin fi 108310c6dd0SKonstantin Khlebnikov modcache[$module]=$objfile 109310c6dd0SKonstantin Khlebnikov fi 110310c6dd0SKonstantin Khlebnikov 111e260fe01SRobert Jarzmik # Remove the englobing parenthesis 112e260fe01SRobert Jarzmik symbol=${symbol#\(} 113e260fe01SRobert Jarzmik symbol=${symbol%\)} 114dbd1abb2SSasha Levin 1151d6693fbSKonstantin Khlebnikov # Strip segment 1161d6693fbSKonstantin Khlebnikov local segment 1171d6693fbSKonstantin Khlebnikov if [[ $symbol == *:* ]] ; then 1181d6693fbSKonstantin Khlebnikov segment=${symbol%%:*}: 1191d6693fbSKonstantin Khlebnikov symbol=${symbol#*:} 1201d6693fbSKonstantin Khlebnikov fi 1211d6693fbSKonstantin Khlebnikov 122dbd1abb2SSasha Levin # Strip the symbol name so that we could look it up 123dbd1abb2SSasha Levin local name=${symbol%+*} 124dbd1abb2SSasha Levin 125dbd1abb2SSasha Levin # Use 'nm vmlinux' to figure out the base address of said symbol. 126dbd1abb2SSasha Levin # It's actually faster to call it every time than to load it 127dbd1abb2SSasha Levin # all into bash. 128310c6dd0SKonstantin Khlebnikov if [[ "${cache[$module,$name]+isset}" == "isset" ]]; then 129310c6dd0SKonstantin Khlebnikov local base_addr=${cache[$module,$name]} 130dbd1abb2SSasha Levin else 131*5bf0f3bcSStephen Boyd local base_addr=$(nm "$objfile" 2>/dev/null | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}') 132f643b9eeSKonstantin Khlebnikov if [[ $base_addr == "" ]] ; then 133f643b9eeSKonstantin Khlebnikov # address not found 134f643b9eeSKonstantin Khlebnikov return 135f643b9eeSKonstantin Khlebnikov fi 136310c6dd0SKonstantin Khlebnikov cache[$module,$name]="$base_addr" 137dbd1abb2SSasha Levin fi 138dbd1abb2SSasha Levin # Let's start doing the math to get the exact address into the 139dbd1abb2SSasha Levin # symbol. First, strip out the symbol total length. 140dbd1abb2SSasha Levin local expr=${symbol%/*} 141dbd1abb2SSasha Levin 142dbd1abb2SSasha Levin # Now, replace the symbol name with the base address we found 143dbd1abb2SSasha Levin # before. 144dbd1abb2SSasha Levin expr=${expr/$name/0x$base_addr} 145dbd1abb2SSasha Levin 146dbd1abb2SSasha Levin # Evaluate it to find the actual address 147dbd1abb2SSasha Levin expr=$((expr)) 148dbd1abb2SSasha Levin local address=$(printf "%x\n" "$expr") 149dbd1abb2SSasha Levin 150dbd1abb2SSasha Levin # Pass it to addr2line to get filename and line number 151dbd1abb2SSasha Levin # Could get more than one result 152310c6dd0SKonstantin Khlebnikov if [[ "${cache[$module,$address]+isset}" == "isset" ]]; then 153310c6dd0SKonstantin Khlebnikov local code=${cache[$module,$address]} 154dbd1abb2SSasha Levin else 155*5bf0f3bcSStephen Boyd local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address" 2>/dev/null) 156310c6dd0SKonstantin Khlebnikov cache[$module,$address]=$code 157dbd1abb2SSasha Levin fi 158dbd1abb2SSasha Levin 159dbd1abb2SSasha Levin # addr2line doesn't return a proper error code if it fails, so 160dbd1abb2SSasha Levin # we detect it using the value it prints so that we could preserve 161dbd1abb2SSasha Levin # the offset/size into the function and bail out 162dbd1abb2SSasha Levin if [[ $code == "??:0" ]]; then 163dbd1abb2SSasha Levin return 164dbd1abb2SSasha Levin fi 165dbd1abb2SSasha Levin 166d178770dSPi-Hsun Shih # Strip out the base of the path on each line 167d178770dSPi-Hsun Shih code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code") 168dbd1abb2SSasha Levin 169dbd1abb2SSasha Levin # In the case of inlines, move everything to same line 170dbd1abb2SSasha Levin code=${code//$'\n'/' '} 171dbd1abb2SSasha Levin 172dbd1abb2SSasha Levin # Replace old address with pretty line numbers 1731d6693fbSKonstantin Khlebnikov symbol="$segment$name ($code)" 174dbd1abb2SSasha Levin} 175dbd1abb2SSasha Levin 17626681eb3SStephen Boyddebuginfod_get_vmlinux() { 17726681eb3SStephen Boyd local vmlinux_buildid=${1##* } 17826681eb3SStephen Boyd 17926681eb3SStephen Boyd if [[ $vmlinux != "" ]]; then 18026681eb3SStephen Boyd return 18126681eb3SStephen Boyd fi 18226681eb3SStephen Boyd 18326681eb3SStephen Boyd if [[ $vmlinux_buildid =~ ^[0-9a-f]+ ]]; then 18426681eb3SStephen Boyd vmlinux=$(debuginfod-find debuginfo $vmlinux_buildid) 18526681eb3SStephen Boyd if [[ $? -ne 0 ]] ; then 18626681eb3SStephen Boyd echo "ERROR! vmlinux image not found via debuginfod-find" >&2 18726681eb3SStephen Boyd usage 18826681eb3SStephen Boyd exit 2 18926681eb3SStephen Boyd fi 19026681eb3SStephen Boyd return 19126681eb3SStephen Boyd fi 19226681eb3SStephen Boyd echo "ERROR! Build ID for vmlinux not found. Try passing -r or specifying vmlinux" >&2 19326681eb3SStephen Boyd usage 19426681eb3SStephen Boyd exit 2 19526681eb3SStephen Boyd} 19626681eb3SStephen Boyd 197dbd1abb2SSasha Levindecode_code() { 198dbd1abb2SSasha Levin local scripts=`dirname "${BASH_SOURCE[0]}"` 199dbd1abb2SSasha Levin 200dbd1abb2SSasha Levin echo "$1" | $scripts/decodecode 201dbd1abb2SSasha Levin} 202dbd1abb2SSasha Levin 203dbd1abb2SSasha Levinhandle_line() { 20426681eb3SStephen Boyd if [[ $basepath == "auto" && $vmlinux != "" ]] ; then 20526681eb3SStephen Boyd module="" 20626681eb3SStephen Boyd symbol="kernel_init+0x0/0x0" 20726681eb3SStephen Boyd parse_symbol 20826681eb3SStephen Boyd basepath=${symbol#kernel_init (} 20926681eb3SStephen Boyd basepath=${basepath%/init/main.c:*)} 21026681eb3SStephen Boyd fi 21126681eb3SStephen Boyd 212dbd1abb2SSasha Levin local words 213dbd1abb2SSasha Levin 214dbd1abb2SSasha Levin # Tokenize 215dbd1abb2SSasha Levin read -a words <<<"$1" 216dbd1abb2SSasha Levin 217dbd1abb2SSasha Levin # Remove hex numbers. Do it ourselves until it happens in the 218dbd1abb2SSasha Levin # kernel 219dbd1abb2SSasha Levin 220dbd1abb2SSasha Levin # We need to know the index of the last element before we 221dbd1abb2SSasha Levin # remove elements because arrays are sparse 222dbd1abb2SSasha Levin local last=$(( ${#words[@]} - 1 )) 223dbd1abb2SSasha Levin 224dbd1abb2SSasha Levin for i in "${!words[@]}"; do 225dbd1abb2SSasha Levin # Remove the address 226dbd1abb2SSasha Levin if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then 227dbd1abb2SSasha Levin unset words[$i] 228dbd1abb2SSasha Levin fi 229dbd1abb2SSasha Levin 230dbd1abb2SSasha Levin # Format timestamps with tabs 231dbd1abb2SSasha Levin if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then 232dbd1abb2SSasha Levin unset words[$i] 233dbd1abb2SSasha Levin words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}") 234dbd1abb2SSasha Levin fi 235dbd1abb2SSasha Levin done 236dbd1abb2SSasha Levin 23726681eb3SStephen Boyd if [[ ${words[$last]} =~ ^[0-9a-f]+\] ]]; then 23826681eb3SStephen Boyd words[$last-1]="${words[$last-1]} ${words[$last]}" 23926681eb3SStephen Boyd unset words[$last] 24026681eb3SStephen Boyd last=$(( $last - 1 )) 24126681eb3SStephen Boyd fi 24226681eb3SStephen Boyd 243310c6dd0SKonstantin Khlebnikov if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then 244310c6dd0SKonstantin Khlebnikov module=${words[$last]} 245310c6dd0SKonstantin Khlebnikov module=${module#\[} 246310c6dd0SKonstantin Khlebnikov module=${module%\]} 24726681eb3SStephen Boyd modbuildid=${module#* } 24826681eb3SStephen Boyd module=${module% *} 24926681eb3SStephen Boyd if [[ $modbuildid == $module ]]; then 25026681eb3SStephen Boyd modbuildid= 25126681eb3SStephen Boyd fi 252310c6dd0SKonstantin Khlebnikov symbol=${words[$last-1]} 253310c6dd0SKonstantin Khlebnikov unset words[$last-1] 254310c6dd0SKonstantin Khlebnikov else 255dbd1abb2SSasha Levin # The symbol is the last element, process it 256dbd1abb2SSasha Levin symbol=${words[$last]} 257310c6dd0SKonstantin Khlebnikov module= 25826681eb3SStephen Boyd modbuildid= 259310c6dd0SKonstantin Khlebnikov fi 260310c6dd0SKonstantin Khlebnikov 261dbd1abb2SSasha Levin unset words[$last] 262dbd1abb2SSasha Levin parse_symbol # modifies $symbol 263dbd1abb2SSasha Levin 264dbd1abb2SSasha Levin # Add up the line number to the symbol 265310c6dd0SKonstantin Khlebnikov echo "${words[@]}" "$symbol $module" 266dbd1abb2SSasha Levin} 267dbd1abb2SSasha Levin 268dbd1abb2SSasha Levinwhile read line; do 269dbd1abb2SSasha Levin # Let's see if we have an address in the line 27053938ee4SJosh Poimboeuf if [[ $line =~ \[\<([^]]+)\>\] ]] || 27153938ee4SJosh Poimboeuf [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then 272dbd1abb2SSasha Levin # Translate address to line numbers 273dbd1abb2SSasha Levin handle_line "$line" 274dbd1abb2SSasha Levin # Is it a code line? 275dbd1abb2SSasha Levin elif [[ $line == *Code:* ]]; then 276dbd1abb2SSasha Levin decode_code "$line" 27726681eb3SStephen Boyd # Is it a version line? 27826681eb3SStephen Boyd elif [[ -n $debuginfod && $line =~ PID:\ [0-9]+\ Comm: ]]; then 27926681eb3SStephen Boyd debuginfod_get_vmlinux "$line" 280dbd1abb2SSasha Levin else 281dbd1abb2SSasha Levin # Nothing special in this line, show it as is 282dbd1abb2SSasha Levin echo "$line" 283dbd1abb2SSasha Levin fi 284dbd1abb2SSasha Levindone 285