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 6310c6dd0SKonstantin Khlebnikovif [[ $# < 2 ]]; then 7dbd1abb2SSasha Levin echo "Usage:" 8310c6dd0SKonstantin Khlebnikov echo " $0 [vmlinux] [base path] [modules path]" 9dbd1abb2SSasha Levin exit 1 10dbd1abb2SSasha Levinfi 11dbd1abb2SSasha Levin 12dbd1abb2SSasha Levinvmlinux=$1 13dbd1abb2SSasha Levinbasepath=$2 14310c6dd0SKonstantin Khlebnikovmodpath=$3 15dbd1abb2SSasha Levindeclare -A cache 16310c6dd0SKonstantin Khlebnikovdeclare -A modcache 17dbd1abb2SSasha Levin 18dbd1abb2SSasha Levinparse_symbol() { 19dbd1abb2SSasha Levin # The structure of symbol at this point is: 20e260fe01SRobert Jarzmik # ([name]+[offset]/[total length]) 21dbd1abb2SSasha Levin # 22dbd1abb2SSasha Levin # For example: 23dbd1abb2SSasha Levin # do_basic_setup+0x9c/0xbf 24dbd1abb2SSasha Levin 25310c6dd0SKonstantin Khlebnikov if [[ $module == "" ]] ; then 26310c6dd0SKonstantin Khlebnikov local objfile=$vmlinux 27310c6dd0SKonstantin Khlebnikov elif [[ "${modcache[$module]+isset}" == "isset" ]]; then 28310c6dd0SKonstantin Khlebnikov local objfile=${modcache[$module]} 29310c6dd0SKonstantin Khlebnikov else 30*a5dc8300SSasha Levin if [[ $modpath == "" ]]; then 31*a5dc8300SSasha Levin echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2 32*a5dc8300SSasha Levin return 33*a5dc8300SSasha Levin fi 34ca90bbd4SEvan Green local objfile=$(find "$modpath" -name "${module//_/[-_]}.ko*" -print -quit) 35310c6dd0SKonstantin Khlebnikov [[ $objfile == "" ]] && return 36310c6dd0SKonstantin Khlebnikov modcache[$module]=$objfile 37310c6dd0SKonstantin Khlebnikov fi 38310c6dd0SKonstantin Khlebnikov 39e260fe01SRobert Jarzmik # Remove the englobing parenthesis 40e260fe01SRobert Jarzmik symbol=${symbol#\(} 41e260fe01SRobert Jarzmik symbol=${symbol%\)} 42dbd1abb2SSasha Levin 431d6693fbSKonstantin Khlebnikov # Strip segment 441d6693fbSKonstantin Khlebnikov local segment 451d6693fbSKonstantin Khlebnikov if [[ $symbol == *:* ]] ; then 461d6693fbSKonstantin Khlebnikov segment=${symbol%%:*}: 471d6693fbSKonstantin Khlebnikov symbol=${symbol#*:} 481d6693fbSKonstantin Khlebnikov fi 491d6693fbSKonstantin Khlebnikov 50dbd1abb2SSasha Levin # Strip the symbol name so that we could look it up 51dbd1abb2SSasha Levin local name=${symbol%+*} 52dbd1abb2SSasha Levin 53dbd1abb2SSasha Levin # Use 'nm vmlinux' to figure out the base address of said symbol. 54dbd1abb2SSasha Levin # It's actually faster to call it every time than to load it 55dbd1abb2SSasha Levin # all into bash. 56310c6dd0SKonstantin Khlebnikov if [[ "${cache[$module,$name]+isset}" == "isset" ]]; then 57310c6dd0SKonstantin Khlebnikov local base_addr=${cache[$module,$name]} 58dbd1abb2SSasha Levin else 59310c6dd0SKonstantin Khlebnikov local base_addr=$(nm "$objfile" | grep -i ' t ' | awk "/ $name\$/ {print \$1}" | head -n1) 60310c6dd0SKonstantin Khlebnikov cache[$module,$name]="$base_addr" 61dbd1abb2SSasha Levin fi 62dbd1abb2SSasha Levin # Let's start doing the math to get the exact address into the 63dbd1abb2SSasha Levin # symbol. First, strip out the symbol total length. 64dbd1abb2SSasha Levin local expr=${symbol%/*} 65dbd1abb2SSasha Levin 66dbd1abb2SSasha Levin # Now, replace the symbol name with the base address we found 67dbd1abb2SSasha Levin # before. 68dbd1abb2SSasha Levin expr=${expr/$name/0x$base_addr} 69dbd1abb2SSasha Levin 70dbd1abb2SSasha Levin # Evaluate it to find the actual address 71dbd1abb2SSasha Levin expr=$((expr)) 72dbd1abb2SSasha Levin local address=$(printf "%x\n" "$expr") 73dbd1abb2SSasha Levin 74dbd1abb2SSasha Levin # Pass it to addr2line to get filename and line number 75dbd1abb2SSasha Levin # Could get more than one result 76310c6dd0SKonstantin Khlebnikov if [[ "${cache[$module,$address]+isset}" == "isset" ]]; then 77310c6dd0SKonstantin Khlebnikov local code=${cache[$module,$address]} 78dbd1abb2SSasha Levin else 79c04e32e9SManuel Traut local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address") 80310c6dd0SKonstantin Khlebnikov cache[$module,$address]=$code 81dbd1abb2SSasha Levin fi 82dbd1abb2SSasha Levin 83dbd1abb2SSasha Levin # addr2line doesn't return a proper error code if it fails, so 84dbd1abb2SSasha Levin # we detect it using the value it prints so that we could preserve 85dbd1abb2SSasha Levin # the offset/size into the function and bail out 86dbd1abb2SSasha Levin if [[ $code == "??:0" ]]; then 87dbd1abb2SSasha Levin return 88dbd1abb2SSasha Levin fi 89dbd1abb2SSasha Levin 90dbd1abb2SSasha Levin # Strip out the base of the path 9131013836SNicolas Boichat code=${code#$basepath/} 92dbd1abb2SSasha Levin 93dbd1abb2SSasha Levin # In the case of inlines, move everything to same line 94dbd1abb2SSasha Levin code=${code//$'\n'/' '} 95dbd1abb2SSasha Levin 96dbd1abb2SSasha Levin # Replace old address with pretty line numbers 971d6693fbSKonstantin Khlebnikov symbol="$segment$name ($code)" 98dbd1abb2SSasha Levin} 99dbd1abb2SSasha Levin 100dbd1abb2SSasha Levindecode_code() { 101dbd1abb2SSasha Levin local scripts=`dirname "${BASH_SOURCE[0]}"` 102dbd1abb2SSasha Levin 103dbd1abb2SSasha Levin echo "$1" | $scripts/decodecode 104dbd1abb2SSasha Levin} 105dbd1abb2SSasha Levin 106dbd1abb2SSasha Levinhandle_line() { 107dbd1abb2SSasha Levin local words 108dbd1abb2SSasha Levin 109dbd1abb2SSasha Levin # Tokenize 110dbd1abb2SSasha Levin read -a words <<<"$1" 111dbd1abb2SSasha Levin 112dbd1abb2SSasha Levin # Remove hex numbers. Do it ourselves until it happens in the 113dbd1abb2SSasha Levin # kernel 114dbd1abb2SSasha Levin 115dbd1abb2SSasha Levin # We need to know the index of the last element before we 116dbd1abb2SSasha Levin # remove elements because arrays are sparse 117dbd1abb2SSasha Levin local last=$(( ${#words[@]} - 1 )) 118dbd1abb2SSasha Levin 119dbd1abb2SSasha Levin for i in "${!words[@]}"; do 120dbd1abb2SSasha Levin # Remove the address 121dbd1abb2SSasha Levin if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then 122dbd1abb2SSasha Levin unset words[$i] 123dbd1abb2SSasha Levin fi 124dbd1abb2SSasha Levin 125dbd1abb2SSasha Levin # Format timestamps with tabs 126dbd1abb2SSasha Levin if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then 127dbd1abb2SSasha Levin unset words[$i] 128dbd1abb2SSasha Levin words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}") 129dbd1abb2SSasha Levin fi 130dbd1abb2SSasha Levin done 131dbd1abb2SSasha Levin 132310c6dd0SKonstantin Khlebnikov if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then 133310c6dd0SKonstantin Khlebnikov module=${words[$last]} 134310c6dd0SKonstantin Khlebnikov module=${module#\[} 135310c6dd0SKonstantin Khlebnikov module=${module%\]} 136310c6dd0SKonstantin Khlebnikov symbol=${words[$last-1]} 137310c6dd0SKonstantin Khlebnikov unset words[$last-1] 138310c6dd0SKonstantin Khlebnikov else 139dbd1abb2SSasha Levin # The symbol is the last element, process it 140dbd1abb2SSasha Levin symbol=${words[$last]} 141310c6dd0SKonstantin Khlebnikov module= 142310c6dd0SKonstantin Khlebnikov fi 143310c6dd0SKonstantin Khlebnikov 144dbd1abb2SSasha Levin unset words[$last] 145dbd1abb2SSasha Levin parse_symbol # modifies $symbol 146dbd1abb2SSasha Levin 147dbd1abb2SSasha Levin # Add up the line number to the symbol 148310c6dd0SKonstantin Khlebnikov echo "${words[@]}" "$symbol $module" 149dbd1abb2SSasha Levin} 150dbd1abb2SSasha Levin 151dbd1abb2SSasha Levinwhile read line; do 152dbd1abb2SSasha Levin # Let's see if we have an address in the line 15353938ee4SJosh Poimboeuf if [[ $line =~ \[\<([^]]+)\>\] ]] || 15453938ee4SJosh Poimboeuf [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then 155dbd1abb2SSasha Levin # Translate address to line numbers 156dbd1abb2SSasha Levin handle_line "$line" 157dbd1abb2SSasha Levin # Is it a code line? 158dbd1abb2SSasha Levin elif [[ $line == *Code:* ]]; then 159dbd1abb2SSasha Levin decode_code "$line" 160dbd1abb2SSasha Levin else 161dbd1abb2SSasha Levin # Nothing special in this line, show it as is 162dbd1abb2SSasha Levin echo "$line" 163dbd1abb2SSasha Levin fi 164dbd1abb2SSasha Levindone 165