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 6ecda6e27SKonstantin Khlebnikovif [[ $# < 1 ]]; then 7dbd1abb2SSasha Levin echo "Usage:" 8ecda6e27SKonstantin Khlebnikov echo " $0 <vmlinux> [base path] [modules path]" 9dbd1abb2SSasha Levin exit 1 10dbd1abb2SSasha Levinfi 11dbd1abb2SSasha Levin 12dbd1abb2SSasha Levinvmlinux=$1 13ecda6e27SKonstantin Khlebnikovbasepath=${2-auto} 14310c6dd0SKonstantin Khlebnikovmodpath=$3 15*431151b6SKonstantin Khlebnikovrelease="" 16*431151b6SKonstantin Khlebnikov 17dbd1abb2SSasha Levindeclare -A cache 18310c6dd0SKonstantin Khlebnikovdeclare -A modcache 19dbd1abb2SSasha Levin 20*431151b6SKonstantin Khlebnikovfind_module() { 21*431151b6SKonstantin Khlebnikov if [[ "$modpath" != "" ]] ; then 22*431151b6SKonstantin Khlebnikov for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do 23*431151b6SKonstantin Khlebnikov if readelf -WS "$fn" | grep -qwF .debug_line ; then 24*431151b6SKonstantin Khlebnikov echo $fn 25*431151b6SKonstantin Khlebnikov return 26*431151b6SKonstantin Khlebnikov fi 27*431151b6SKonstantin Khlebnikov done 28*431151b6SKonstantin Khlebnikov return 1 29*431151b6SKonstantin Khlebnikov fi 30*431151b6SKonstantin Khlebnikov 31*431151b6SKonstantin Khlebnikov modpath=$(dirname "$vmlinux") 32*431151b6SKonstantin Khlebnikov find_module && return 33*431151b6SKonstantin Khlebnikov 34*431151b6SKonstantin Khlebnikov if [[ $release == "" ]] ; then 35*431151b6SKonstantin Khlebnikov release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" | sed -n 's/\$1 = "\(.*\)".*/\1/p') 36*431151b6SKonstantin Khlebnikov fi 37*431151b6SKonstantin Khlebnikov 38*431151b6SKonstantin Khlebnikov for dn in {/usr/lib/debug,}/lib/modules/$release ; do 39*431151b6SKonstantin Khlebnikov if [ -e "$dn" ] ; then 40*431151b6SKonstantin Khlebnikov modpath="$dn" 41*431151b6SKonstantin Khlebnikov find_module && return 42*431151b6SKonstantin Khlebnikov fi 43*431151b6SKonstantin Khlebnikov done 44*431151b6SKonstantin Khlebnikov 45*431151b6SKonstantin Khlebnikov modpath="" 46*431151b6SKonstantin Khlebnikov return 1 47*431151b6SKonstantin Khlebnikov} 48*431151b6SKonstantin Khlebnikov 49dbd1abb2SSasha Levinparse_symbol() { 50dbd1abb2SSasha Levin # The structure of symbol at this point is: 51e260fe01SRobert Jarzmik # ([name]+[offset]/[total length]) 52dbd1abb2SSasha Levin # 53dbd1abb2SSasha Levin # For example: 54dbd1abb2SSasha Levin # do_basic_setup+0x9c/0xbf 55dbd1abb2SSasha Levin 56310c6dd0SKonstantin Khlebnikov if [[ $module == "" ]] ; then 57310c6dd0SKonstantin Khlebnikov local objfile=$vmlinux 58310c6dd0SKonstantin Khlebnikov elif [[ "${modcache[$module]+isset}" == "isset" ]]; then 59310c6dd0SKonstantin Khlebnikov local objfile=${modcache[$module]} 60310c6dd0SKonstantin Khlebnikov else 61*431151b6SKonstantin Khlebnikov local objfile=$(find_module) 62*431151b6SKonstantin Khlebnikov if [[ $objfile == "" ]] ; then 63a5dc8300SSasha Levin echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2 64a5dc8300SSasha Levin return 65a5dc8300SSasha Levin fi 66310c6dd0SKonstantin Khlebnikov modcache[$module]=$objfile 67310c6dd0SKonstantin Khlebnikov fi 68310c6dd0SKonstantin Khlebnikov 69e260fe01SRobert Jarzmik # Remove the englobing parenthesis 70e260fe01SRobert Jarzmik symbol=${symbol#\(} 71e260fe01SRobert Jarzmik symbol=${symbol%\)} 72dbd1abb2SSasha Levin 731d6693fbSKonstantin Khlebnikov # Strip segment 741d6693fbSKonstantin Khlebnikov local segment 751d6693fbSKonstantin Khlebnikov if [[ $symbol == *:* ]] ; then 761d6693fbSKonstantin Khlebnikov segment=${symbol%%:*}: 771d6693fbSKonstantin Khlebnikov symbol=${symbol#*:} 781d6693fbSKonstantin Khlebnikov fi 791d6693fbSKonstantin Khlebnikov 80dbd1abb2SSasha Levin # Strip the symbol name so that we could look it up 81dbd1abb2SSasha Levin local name=${symbol%+*} 82dbd1abb2SSasha Levin 83dbd1abb2SSasha Levin # Use 'nm vmlinux' to figure out the base address of said symbol. 84dbd1abb2SSasha Levin # It's actually faster to call it every time than to load it 85dbd1abb2SSasha Levin # all into bash. 86310c6dd0SKonstantin Khlebnikov if [[ "${cache[$module,$name]+isset}" == "isset" ]]; then 87310c6dd0SKonstantin Khlebnikov local base_addr=${cache[$module,$name]} 88dbd1abb2SSasha Levin else 89f643b9eeSKonstantin Khlebnikov local base_addr=$(nm "$objfile" | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}') 90f643b9eeSKonstantin Khlebnikov if [[ $base_addr == "" ]] ; then 91f643b9eeSKonstantin Khlebnikov # address not found 92f643b9eeSKonstantin Khlebnikov return 93f643b9eeSKonstantin Khlebnikov fi 94310c6dd0SKonstantin Khlebnikov cache[$module,$name]="$base_addr" 95dbd1abb2SSasha Levin fi 96dbd1abb2SSasha Levin # Let's start doing the math to get the exact address into the 97dbd1abb2SSasha Levin # symbol. First, strip out the symbol total length. 98dbd1abb2SSasha Levin local expr=${symbol%/*} 99dbd1abb2SSasha Levin 100dbd1abb2SSasha Levin # Now, replace the symbol name with the base address we found 101dbd1abb2SSasha Levin # before. 102dbd1abb2SSasha Levin expr=${expr/$name/0x$base_addr} 103dbd1abb2SSasha Levin 104dbd1abb2SSasha Levin # Evaluate it to find the actual address 105dbd1abb2SSasha Levin expr=$((expr)) 106dbd1abb2SSasha Levin local address=$(printf "%x\n" "$expr") 107dbd1abb2SSasha Levin 108dbd1abb2SSasha Levin # Pass it to addr2line to get filename and line number 109dbd1abb2SSasha Levin # Could get more than one result 110310c6dd0SKonstantin Khlebnikov if [[ "${cache[$module,$address]+isset}" == "isset" ]]; then 111310c6dd0SKonstantin Khlebnikov local code=${cache[$module,$address]} 112dbd1abb2SSasha Levin else 113c04e32e9SManuel Traut local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address") 114310c6dd0SKonstantin Khlebnikov cache[$module,$address]=$code 115dbd1abb2SSasha Levin fi 116dbd1abb2SSasha Levin 117dbd1abb2SSasha Levin # addr2line doesn't return a proper error code if it fails, so 118dbd1abb2SSasha Levin # we detect it using the value it prints so that we could preserve 119dbd1abb2SSasha Levin # the offset/size into the function and bail out 120dbd1abb2SSasha Levin if [[ $code == "??:0" ]]; then 121dbd1abb2SSasha Levin return 122dbd1abb2SSasha Levin fi 123dbd1abb2SSasha Levin 124d178770dSPi-Hsun Shih # Strip out the base of the path on each line 125d178770dSPi-Hsun Shih code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code") 126dbd1abb2SSasha Levin 127dbd1abb2SSasha Levin # In the case of inlines, move everything to same line 128dbd1abb2SSasha Levin code=${code//$'\n'/' '} 129dbd1abb2SSasha Levin 130dbd1abb2SSasha Levin # Replace old address with pretty line numbers 1311d6693fbSKonstantin Khlebnikov symbol="$segment$name ($code)" 132dbd1abb2SSasha Levin} 133dbd1abb2SSasha Levin 134dbd1abb2SSasha Levindecode_code() { 135dbd1abb2SSasha Levin local scripts=`dirname "${BASH_SOURCE[0]}"` 136dbd1abb2SSasha Levin 137dbd1abb2SSasha Levin echo "$1" | $scripts/decodecode 138dbd1abb2SSasha Levin} 139dbd1abb2SSasha Levin 140dbd1abb2SSasha Levinhandle_line() { 141dbd1abb2SSasha Levin local words 142dbd1abb2SSasha Levin 143dbd1abb2SSasha Levin # Tokenize 144dbd1abb2SSasha Levin read -a words <<<"$1" 145dbd1abb2SSasha Levin 146dbd1abb2SSasha Levin # Remove hex numbers. Do it ourselves until it happens in the 147dbd1abb2SSasha Levin # kernel 148dbd1abb2SSasha Levin 149dbd1abb2SSasha Levin # We need to know the index of the last element before we 150dbd1abb2SSasha Levin # remove elements because arrays are sparse 151dbd1abb2SSasha Levin local last=$(( ${#words[@]} - 1 )) 152dbd1abb2SSasha Levin 153dbd1abb2SSasha Levin for i in "${!words[@]}"; do 154dbd1abb2SSasha Levin # Remove the address 155dbd1abb2SSasha Levin if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then 156dbd1abb2SSasha Levin unset words[$i] 157dbd1abb2SSasha Levin fi 158dbd1abb2SSasha Levin 159dbd1abb2SSasha Levin # Format timestamps with tabs 160dbd1abb2SSasha Levin if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then 161dbd1abb2SSasha Levin unset words[$i] 162dbd1abb2SSasha Levin words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}") 163dbd1abb2SSasha Levin fi 164dbd1abb2SSasha Levin done 165dbd1abb2SSasha Levin 166310c6dd0SKonstantin Khlebnikov if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then 167310c6dd0SKonstantin Khlebnikov module=${words[$last]} 168310c6dd0SKonstantin Khlebnikov module=${module#\[} 169310c6dd0SKonstantin Khlebnikov module=${module%\]} 170310c6dd0SKonstantin Khlebnikov symbol=${words[$last-1]} 171310c6dd0SKonstantin Khlebnikov unset words[$last-1] 172310c6dd0SKonstantin Khlebnikov else 173dbd1abb2SSasha Levin # The symbol is the last element, process it 174dbd1abb2SSasha Levin symbol=${words[$last]} 175310c6dd0SKonstantin Khlebnikov module= 176310c6dd0SKonstantin Khlebnikov fi 177310c6dd0SKonstantin Khlebnikov 178dbd1abb2SSasha Levin unset words[$last] 179dbd1abb2SSasha Levin parse_symbol # modifies $symbol 180dbd1abb2SSasha Levin 181dbd1abb2SSasha Levin # Add up the line number to the symbol 182310c6dd0SKonstantin Khlebnikov echo "${words[@]}" "$symbol $module" 183dbd1abb2SSasha Levin} 184dbd1abb2SSasha Levin 185ecda6e27SKonstantin Khlebnikovif [[ $basepath == "auto" ]] ; then 186ecda6e27SKonstantin Khlebnikov module="" 187ecda6e27SKonstantin Khlebnikov symbol="kernel_init+0x0/0x0" 188ecda6e27SKonstantin Khlebnikov parse_symbol 189ecda6e27SKonstantin Khlebnikov basepath=${symbol#kernel_init (} 190ecda6e27SKonstantin Khlebnikov basepath=${basepath%/init/main.c:*)} 191ecda6e27SKonstantin Khlebnikovfi 192ecda6e27SKonstantin Khlebnikov 193dbd1abb2SSasha Levinwhile read line; do 194dbd1abb2SSasha Levin # Let's see if we have an address in the line 19553938ee4SJosh Poimboeuf if [[ $line =~ \[\<([^]]+)\>\] ]] || 19653938ee4SJosh Poimboeuf [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then 197dbd1abb2SSasha Levin # Translate address to line numbers 198dbd1abb2SSasha Levin handle_line "$line" 199dbd1abb2SSasha Levin # Is it a code line? 200dbd1abb2SSasha Levin elif [[ $line == *Code:* ]]; then 201dbd1abb2SSasha Levin decode_code "$line" 202dbd1abb2SSasha Levin else 203dbd1abb2SSasha Levin # Nothing special in this line, show it as is 204dbd1abb2SSasha Levin echo "$line" 205dbd1abb2SSasha Levin fi 206dbd1abb2SSasha Levindone 207