1076979eeSKent Overstreet#!/usr/bin/env 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:" 8a6d05e82SLuca Ceresoli echo " $0 -r <release>" 9a6d05e82SLuca Ceresoli echo " $0 [<vmlinux> [<base_path>|auto [<modules_path>]]]" 107e108359SLuca Ceresoli echo " $0 -h" 1126681eb3SStephen Boyd} 12dbd1abb2SSasha Levin 1399115db4SMiguel Ojeda# Try to find a Rust demangler 1499115db4SMiguel Ojedaif type llvm-cxxfilt >/dev/null 2>&1 ; then 1599115db4SMiguel Ojeda cppfilt=llvm-cxxfilt 1699115db4SMiguel Ojedaelif type c++filt >/dev/null 2>&1 ; then 1799115db4SMiguel Ojeda cppfilt=c++filt 1899115db4SMiguel Ojeda cppfilt_opts=-i 1999115db4SMiguel Ojedafi 2099115db4SMiguel Ojeda 21efbd6398SCarlos LlamasUTIL_SUFFIX= 22efbd6398SCarlos Llamasif [[ -z ${LLVM:-} ]]; then 23efbd6398SCarlos Llamas UTIL_PREFIX=${CROSS_COMPILE:-} 24efbd6398SCarlos Llamaselse 25efbd6398SCarlos Llamas UTIL_PREFIX=llvm- 26efbd6398SCarlos Llamas if [[ ${LLVM} == */ ]]; then 27efbd6398SCarlos Llamas UTIL_PREFIX=${LLVM}${UTIL_PREFIX} 28efbd6398SCarlos Llamas elif [[ ${LLVM} == -* ]]; then 29efbd6398SCarlos Llamas UTIL_SUFFIX=${LLVM} 30efbd6398SCarlos Llamas fi 31efbd6398SCarlos Llamasfi 32efbd6398SCarlos Llamas 33efbd6398SCarlos LlamasREADELF=${UTIL_PREFIX}readelf${UTIL_SUFFIX} 34efbd6398SCarlos LlamasADDR2LINE=${UTIL_PREFIX}addr2line${UTIL_SUFFIX} 35b41838feSXiong NandiNM=${UTIL_PREFIX}nm${UTIL_SUFFIX} 36efbd6398SCarlos Llamas 377e108359SLuca Ceresoliif [[ $1 == "-h" ]] ; then 387e108359SLuca Ceresoli usage 397e108359SLuca Ceresoli exit 0 407e108359SLuca Ceresolielif [[ $1 == "-r" ]] ; then 41f90dde44SKonstantin Khlebnikov vmlinux="" 42f90dde44SKonstantin Khlebnikov basepath="auto" 43f90dde44SKonstantin Khlebnikov modpath="" 44f90dde44SKonstantin Khlebnikov release=$2 45f90dde44SKonstantin Khlebnikov 46f90dde44SKonstantin Khlebnikov for fn in {,/usr/lib/debug}/boot/vmlinux-$release{,.debug} /lib/modules/$release{,/build}/vmlinux ; do 47f90dde44SKonstantin Khlebnikov if [ -e "$fn" ] ; then 48f90dde44SKonstantin Khlebnikov vmlinux=$fn 49f90dde44SKonstantin Khlebnikov break 50f90dde44SKonstantin Khlebnikov fi 51f90dde44SKonstantin Khlebnikov done 52f90dde44SKonstantin Khlebnikov 53f90dde44SKonstantin Khlebnikov if [[ $vmlinux == "" ]] ; then 54f90dde44SKonstantin Khlebnikov echo "ERROR! vmlinux image for release $release is not found" >&2 5526681eb3SStephen Boyd usage 56f90dde44SKonstantin Khlebnikov exit 2 57f90dde44SKonstantin Khlebnikov fi 58f90dde44SKonstantin Khlebnikovelse 59dbd1abb2SSasha Levin vmlinux=$1 60ecda6e27SKonstantin Khlebnikov basepath=${2-auto} 61310c6dd0SKonstantin Khlebnikov modpath=$3 62431151b6SKonstantin Khlebnikov release="" 6326681eb3SStephen Boyd debuginfod= 6426681eb3SStephen Boyd 6526681eb3SStephen Boyd # Can we use debuginfod-find? 6626681eb3SStephen Boyd if type debuginfod-find >/dev/null 2>&1 ; then 6726681eb3SStephen Boyd debuginfod=${1-only} 6826681eb3SStephen Boyd fi 6926681eb3SStephen Boyd 7026681eb3SStephen Boyd if [[ $vmlinux == "" && -z $debuginfod ]] ; then 7126681eb3SStephen Boyd echo "ERROR! vmlinux image must be specified" >&2 7226681eb3SStephen Boyd usage 7326681eb3SStephen Boyd exit 1 7426681eb3SStephen Boyd fi 75f90dde44SKonstantin Khlebnikovfi 76431151b6SKonstantin Khlebnikov 773af8acf6SSchspa Shideclare aarray_support=true 783af8acf6SSchspa Shideclare -A cache 2>/dev/null 793af8acf6SSchspa Shiif [[ $? != 0 ]]; then 803af8acf6SSchspa Shi aarray_support=false 813af8acf6SSchspa Shielse 82310c6dd0SKonstantin Khlebnikov declare -A modcache 833af8acf6SSchspa Shifi 84dbd1abb2SSasha Levin 85431151b6SKonstantin Khlebnikovfind_module() { 8626681eb3SStephen Boyd if [[ -n $debuginfod ]] ; then 8726681eb3SStephen Boyd if [[ -n $modbuildid ]] ; then 8826681eb3SStephen Boyd debuginfod-find debuginfo $modbuildid && return 8926681eb3SStephen Boyd fi 9026681eb3SStephen Boyd 9126681eb3SStephen Boyd # Only using debuginfod so don't try to find vmlinux module path 9226681eb3SStephen Boyd if [[ $debuginfod == "only" ]] ; then 9326681eb3SStephen Boyd return 9426681eb3SStephen Boyd fi 9526681eb3SStephen Boyd fi 9626681eb3SStephen Boyd 970f69dc29SLuca Ceresoli if [ -z $release ] ; then 980f69dc29SLuca Ceresoli release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" 2>/dev/null | sed -n 's/\$1 = "\(.*\)".*/\1/p') 990f69dc29SLuca Ceresoli fi 1000f69dc29SLuca Ceresoli if [ -n "${release}" ] ; then 1010f69dc29SLuca Ceresoli release_dirs="/usr/lib/debug/lib/modules/$release /lib/modules/$release" 1020f69dc29SLuca Ceresoli fi 1030f69dc29SLuca Ceresoli 1040f69dc29SLuca Ceresoli found_without_debug_info=false 1050f69dc29SLuca Ceresoli for dir in "$modpath" "$(dirname "$vmlinux")" ${release_dirs}; do 1060f69dc29SLuca Ceresoli if [ -n "${dir}" ] && [ -e "${dir}" ]; then 1070f69dc29SLuca Ceresoli for fn in $(find "$dir" -name "${module//_/[-_]}.ko*") ; do 108efbd6398SCarlos Llamas if ${READELF} -WS "$fn" | grep -qwF .debug_line ; then 109431151b6SKonstantin Khlebnikov echo $fn 110431151b6SKonstantin Khlebnikov return 111431151b6SKonstantin Khlebnikov fi 1120f69dc29SLuca Ceresoli found_without_debug_info=true 113431151b6SKonstantin Khlebnikov done 114431151b6SKonstantin Khlebnikov fi 115431151b6SKonstantin Khlebnikov done 116431151b6SKonstantin Khlebnikov 1170f69dc29SLuca Ceresoli if [[ ${found_without_debug_info} == true ]]; then 1180f69dc29SLuca Ceresoli echo "WARNING! No debugging info in module ${module}, rebuild with DEBUG_KERNEL and DEBUG_INFO" >&2 1190f69dc29SLuca Ceresoli else 1200f69dc29SLuca Ceresoli echo "WARNING! Cannot find .ko for module ${module}, please pass a valid module path" >&2 1210f69dc29SLuca Ceresoli fi 1220f69dc29SLuca Ceresoli 123431151b6SKonstantin Khlebnikov return 1 124431151b6SKonstantin Khlebnikov} 125431151b6SKonstantin Khlebnikov 126dbd1abb2SSasha Levinparse_symbol() { 127dbd1abb2SSasha Levin # The structure of symbol at this point is: 128e260fe01SRobert Jarzmik # ([name]+[offset]/[total length]) 129dbd1abb2SSasha Levin # 130dbd1abb2SSasha Levin # For example: 131dbd1abb2SSasha Levin # do_basic_setup+0x9c/0xbf 132dbd1abb2SSasha Levin 133310c6dd0SKonstantin Khlebnikov if [[ $module == "" ]] ; then 134310c6dd0SKonstantin Khlebnikov local objfile=$vmlinux 1353af8acf6SSchspa Shi elif [[ $aarray_support == true && "${modcache[$module]+isset}" == "isset" ]]; then 136310c6dd0SKonstantin Khlebnikov local objfile=${modcache[$module]} 137310c6dd0SKonstantin Khlebnikov else 138431151b6SKonstantin Khlebnikov local objfile=$(find_module) 139431151b6SKonstantin Khlebnikov if [[ $objfile == "" ]] ; then 140a5dc8300SSasha Levin return 141a5dc8300SSasha Levin fi 1423af8acf6SSchspa Shi if [[ $aarray_support == true ]]; then 143310c6dd0SKonstantin Khlebnikov modcache[$module]=$objfile 144310c6dd0SKonstantin Khlebnikov fi 1453af8acf6SSchspa Shi fi 146310c6dd0SKonstantin Khlebnikov 147e260fe01SRobert Jarzmik # Remove the englobing parenthesis 148e260fe01SRobert Jarzmik symbol=${symbol#\(} 149e260fe01SRobert Jarzmik symbol=${symbol%\)} 150dbd1abb2SSasha Levin 1511d6693fbSKonstantin Khlebnikov # Strip segment 1521d6693fbSKonstantin Khlebnikov local segment 1531d6693fbSKonstantin Khlebnikov if [[ $symbol == *:* ]] ; then 1541d6693fbSKonstantin Khlebnikov segment=${symbol%%:*}: 1551d6693fbSKonstantin Khlebnikov symbol=${symbol#*:} 1561d6693fbSKonstantin Khlebnikov fi 1571d6693fbSKonstantin Khlebnikov 158dbd1abb2SSasha Levin # Strip the symbol name so that we could look it up 159dbd1abb2SSasha Levin local name=${symbol%+*} 160dbd1abb2SSasha Levin 161dbd1abb2SSasha Levin # Use 'nm vmlinux' to figure out the base address of said symbol. 162dbd1abb2SSasha Levin # It's actually faster to call it every time than to load it 163dbd1abb2SSasha Levin # all into bash. 1643af8acf6SSchspa Shi if [[ $aarray_support == true && "${cache[$module,$name]+isset}" == "isset" ]]; then 165310c6dd0SKonstantin Khlebnikov local base_addr=${cache[$module,$name]} 166dbd1abb2SSasha Levin else 167b41838feSXiong Nandi local base_addr=$(${NM} "$objfile" 2>/dev/null | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}') 168f643b9eeSKonstantin Khlebnikov if [[ $base_addr == "" ]] ; then 169f643b9eeSKonstantin Khlebnikov # address not found 170f643b9eeSKonstantin Khlebnikov return 171f643b9eeSKonstantin Khlebnikov fi 1723af8acf6SSchspa Shi if [[ $aarray_support == true ]]; then 173310c6dd0SKonstantin Khlebnikov cache[$module,$name]="$base_addr" 174dbd1abb2SSasha Levin fi 1753af8acf6SSchspa Shi fi 176dbd1abb2SSasha Levin # Let's start doing the math to get the exact address into the 177dbd1abb2SSasha Levin # symbol. First, strip out the symbol total length. 178dbd1abb2SSasha Levin local expr=${symbol%/*} 179dbd1abb2SSasha Levin 180dbd1abb2SSasha Levin # Now, replace the symbol name with the base address we found 181dbd1abb2SSasha Levin # before. 182dbd1abb2SSasha Levin expr=${expr/$name/0x$base_addr} 183dbd1abb2SSasha Levin 184dbd1abb2SSasha Levin # Evaluate it to find the actual address 185dbd1abb2SSasha Levin expr=$((expr)) 186dbd1abb2SSasha Levin local address=$(printf "%x\n" "$expr") 187dbd1abb2SSasha Levin 188dbd1abb2SSasha Levin # Pass it to addr2line to get filename and line number 189dbd1abb2SSasha Levin # Could get more than one result 1903af8acf6SSchspa Shi if [[ $aarray_support == true && "${cache[$module,$address]+isset}" == "isset" ]]; then 191310c6dd0SKonstantin Khlebnikov local code=${cache[$module,$address]} 192dbd1abb2SSasha Levin else 193efbd6398SCarlos Llamas local code=$(${ADDR2LINE} -i -e "$objfile" "$address" 2>/dev/null) 1943af8acf6SSchspa Shi if [[ $aarray_support == true ]]; then 195310c6dd0SKonstantin Khlebnikov cache[$module,$address]=$code 196dbd1abb2SSasha Levin fi 1973af8acf6SSchspa Shi fi 198dbd1abb2SSasha Levin 199dbd1abb2SSasha Levin # addr2line doesn't return a proper error code if it fails, so 200dbd1abb2SSasha Levin # we detect it using the value it prints so that we could preserve 201dbd1abb2SSasha Levin # the offset/size into the function and bail out 202dbd1abb2SSasha Levin if [[ $code == "??:0" ]]; then 203dbd1abb2SSasha Levin return 204dbd1abb2SSasha Levin fi 205dbd1abb2SSasha Levin 206d178770dSPi-Hsun Shih # Strip out the base of the path on each line 207d178770dSPi-Hsun Shih code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code") 208dbd1abb2SSasha Levin 209dbd1abb2SSasha Levin # In the case of inlines, move everything to same line 210dbd1abb2SSasha Levin code=${code//$'\n'/' '} 211dbd1abb2SSasha Levin 21299115db4SMiguel Ojeda # Demangle if the name looks like a Rust symbol and if 21399115db4SMiguel Ojeda # we got a Rust demangler 21499115db4SMiguel Ojeda if [[ $name =~ ^_R && $cppfilt != "" ]] ; then 21599115db4SMiguel Ojeda name=$("$cppfilt" "$cppfilt_opts" "$name") 21699115db4SMiguel Ojeda fi 21799115db4SMiguel Ojeda 218dbd1abb2SSasha Levin # Replace old address with pretty line numbers 2191d6693fbSKonstantin Khlebnikov symbol="$segment$name ($code)" 220dbd1abb2SSasha Levin} 221dbd1abb2SSasha Levin 22226681eb3SStephen Boyddebuginfod_get_vmlinux() { 22326681eb3SStephen Boyd local vmlinux_buildid=${1##* } 22426681eb3SStephen Boyd 22526681eb3SStephen Boyd if [[ $vmlinux != "" ]]; then 22626681eb3SStephen Boyd return 22726681eb3SStephen Boyd fi 22826681eb3SStephen Boyd 22926681eb3SStephen Boyd if [[ $vmlinux_buildid =~ ^[0-9a-f]+ ]]; then 23026681eb3SStephen Boyd vmlinux=$(debuginfod-find debuginfo $vmlinux_buildid) 23126681eb3SStephen Boyd if [[ $? -ne 0 ]] ; then 23226681eb3SStephen Boyd echo "ERROR! vmlinux image not found via debuginfod-find" >&2 23326681eb3SStephen Boyd usage 23426681eb3SStephen Boyd exit 2 23526681eb3SStephen Boyd fi 23626681eb3SStephen Boyd return 23726681eb3SStephen Boyd fi 23826681eb3SStephen Boyd echo "ERROR! Build ID for vmlinux not found. Try passing -r or specifying vmlinux" >&2 23926681eb3SStephen Boyd usage 24026681eb3SStephen Boyd exit 2 24126681eb3SStephen Boyd} 24226681eb3SStephen Boyd 243dbd1abb2SSasha Levindecode_code() { 244dbd1abb2SSasha Levin local scripts=`dirname "${BASH_SOURCE[0]}"` 245dbd1abb2SSasha Levin 246dbd1abb2SSasha Levin echo "$1" | $scripts/decodecode 247dbd1abb2SSasha Levin} 248dbd1abb2SSasha Levin 249dbd1abb2SSasha Levinhandle_line() { 25026681eb3SStephen Boyd if [[ $basepath == "auto" && $vmlinux != "" ]] ; then 25126681eb3SStephen Boyd module="" 25226681eb3SStephen Boyd symbol="kernel_init+0x0/0x0" 25326681eb3SStephen Boyd parse_symbol 25426681eb3SStephen Boyd basepath=${symbol#kernel_init (} 25526681eb3SStephen Boyd basepath=${basepath%/init/main.c:*)} 25626681eb3SStephen Boyd fi 25726681eb3SStephen Boyd 258dbd1abb2SSasha Levin local words 259dbd1abb2SSasha Levin 260dbd1abb2SSasha Levin # Tokenize 261dbd1abb2SSasha Levin read -a words <<<"$1" 262dbd1abb2SSasha Levin 263dbd1abb2SSasha Levin # Remove hex numbers. Do it ourselves until it happens in the 264dbd1abb2SSasha Levin # kernel 265dbd1abb2SSasha Levin 266dbd1abb2SSasha Levin # We need to know the index of the last element before we 267dbd1abb2SSasha Levin # remove elements because arrays are sparse 268dbd1abb2SSasha Levin local last=$(( ${#words[@]} - 1 )) 269dbd1abb2SSasha Levin 270dbd1abb2SSasha Levin for i in "${!words[@]}"; do 271dbd1abb2SSasha Levin # Remove the address 272dbd1abb2SSasha Levin if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then 273dbd1abb2SSasha Levin unset words[$i] 274dbd1abb2SSasha Levin fi 275dbd1abb2SSasha Levin 276dbd1abb2SSasha Levin # Format timestamps with tabs 277dbd1abb2SSasha Levin if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then 278dbd1abb2SSasha Levin unset words[$i] 279dbd1abb2SSasha Levin words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}") 280dbd1abb2SSasha Levin fi 281dbd1abb2SSasha Levin done 282dbd1abb2SSasha Levin 28326681eb3SStephen Boyd if [[ ${words[$last]} =~ ^[0-9a-f]+\] ]]; then 28426681eb3SStephen Boyd words[$last-1]="${words[$last-1]} ${words[$last]}" 28526681eb3SStephen Boyd unset words[$last] 28626681eb3SStephen Boyd last=$(( $last - 1 )) 28726681eb3SStephen Boyd fi 28826681eb3SStephen Boyd 289*2bff77c6SLuca Ceresoli # Extract info after the symbol if present. E.g.: 290*2bff77c6SLuca Ceresoli # func_name+0x54/0x80 (P) 291*2bff77c6SLuca Ceresoli # ^^^ 292*2bff77c6SLuca Ceresoli # The regex assumes only uppercase letters will be used. To be 293*2bff77c6SLuca Ceresoli # extended if needed. 294*2bff77c6SLuca Ceresoli local info_str="" 295*2bff77c6SLuca Ceresoli if [[ ${words[$last]} =~ \([A-Z]*\) ]]; then 296*2bff77c6SLuca Ceresoli info_str=${words[$last]} 297*2bff77c6SLuca Ceresoli unset words[$last] 298*2bff77c6SLuca Ceresoli last=$(( $last - 1 )) 299*2bff77c6SLuca Ceresoli fi 300*2bff77c6SLuca Ceresoli 301310c6dd0SKonstantin Khlebnikov if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then 302310c6dd0SKonstantin Khlebnikov module=${words[$last]} 30378efbfb5SXiong Nandi # some traces format is "(%pS)", which like "(foo+0x0/0x1 [bar])" 30478efbfb5SXiong Nandi # so $module may like "[bar])". Strip the right parenthesis firstly 30578efbfb5SXiong Nandi module=${module%\)} 306310c6dd0SKonstantin Khlebnikov module=${module#\[} 307310c6dd0SKonstantin Khlebnikov module=${module%\]} 30826681eb3SStephen Boyd modbuildid=${module#* } 30926681eb3SStephen Boyd module=${module% *} 31026681eb3SStephen Boyd if [[ $modbuildid == $module ]]; then 31126681eb3SStephen Boyd modbuildid= 31226681eb3SStephen Boyd fi 313310c6dd0SKonstantin Khlebnikov symbol=${words[$last-1]} 314310c6dd0SKonstantin Khlebnikov unset words[$last-1] 315310c6dd0SKonstantin Khlebnikov else 316dbd1abb2SSasha Levin # The symbol is the last element, process it 317dbd1abb2SSasha Levin symbol=${words[$last]} 318310c6dd0SKonstantin Khlebnikov module= 31926681eb3SStephen Boyd modbuildid= 320310c6dd0SKonstantin Khlebnikov fi 321310c6dd0SKonstantin Khlebnikov 322dbd1abb2SSasha Levin unset words[$last] 323dbd1abb2SSasha Levin parse_symbol # modifies $symbol 324dbd1abb2SSasha Levin 325dbd1abb2SSasha Levin # Add up the line number to the symbol 3261bb5d660SBreno Leitao if [[ -z ${module} ]] 3271bb5d660SBreno Leitao then 328*2bff77c6SLuca Ceresoli echo "${words[@]}" "$symbol ${info_str}" 3291bb5d660SBreno Leitao else 330*2bff77c6SLuca Ceresoli echo "${words[@]}" "$symbol $module ${info_str}" 3311bb5d660SBreno Leitao fi 332dbd1abb2SSasha Levin} 333dbd1abb2SSasha Levin 334dbd1abb2SSasha Levinwhile read line; do 335436efd9eSBjorn Andersson # Strip unexpected carriage return at end of line 336436efd9eSBjorn Andersson line=${line%$'\r'} 337436efd9eSBjorn Andersson 338dbd1abb2SSasha Levin # Let's see if we have an address in the line 33953938ee4SJosh Poimboeuf if [[ $line =~ \[\<([^]]+)\>\] ]] || 34053938ee4SJosh Poimboeuf [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then 341dbd1abb2SSasha Levin # Translate address to line numbers 342dbd1abb2SSasha Levin handle_line "$line" 343dbd1abb2SSasha Levin # Is it a code line? 344dbd1abb2SSasha Levin elif [[ $line == *Code:* ]]; then 345dbd1abb2SSasha Levin decode_code "$line" 34626681eb3SStephen Boyd # Is it a version line? 34726681eb3SStephen Boyd elif [[ -n $debuginfod && $line =~ PID:\ [0-9]+\ Comm: ]]; then 34826681eb3SStephen Boyd debuginfod_get_vmlinux "$line" 349dbd1abb2SSasha Levin else 350dbd1abb2SSasha Levin # Nothing special in this line, show it as is 351dbd1abb2SSasha Levin echo "$line" 352dbd1abb2SSasha Levin fi 353dbd1abb2SSasha Levindone 354