162306a36Sopenharmony_ci#!/bin/bash 262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0 362306a36Sopenharmony_ci# (c) 2014, Sasha Levin <sasha.levin@oracle.com> 462306a36Sopenharmony_ci#set -x 562306a36Sopenharmony_ci 662306a36Sopenharmony_ciusage() { 762306a36Sopenharmony_ci echo "Usage:" 862306a36Sopenharmony_ci echo " $0 -r <release> | <vmlinux> [<base path>|auto] [<modules path>]" 962306a36Sopenharmony_ci} 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci# Try to find a Rust demangler 1262306a36Sopenharmony_ciif type llvm-cxxfilt >/dev/null 2>&1 ; then 1362306a36Sopenharmony_ci cppfilt=llvm-cxxfilt 1462306a36Sopenharmony_cielif type c++filt >/dev/null 2>&1 ; then 1562306a36Sopenharmony_ci cppfilt=c++filt 1662306a36Sopenharmony_ci cppfilt_opts=-i 1762306a36Sopenharmony_cifi 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciUTIL_SUFFIX= 2062306a36Sopenharmony_ciif [[ -z ${LLVM:-} ]]; then 2162306a36Sopenharmony_ci UTIL_PREFIX=${CROSS_COMPILE:-} 2262306a36Sopenharmony_cielse 2362306a36Sopenharmony_ci UTIL_PREFIX=llvm- 2462306a36Sopenharmony_ci if [[ ${LLVM} == */ ]]; then 2562306a36Sopenharmony_ci UTIL_PREFIX=${LLVM}${UTIL_PREFIX} 2662306a36Sopenharmony_ci elif [[ ${LLVM} == -* ]]; then 2762306a36Sopenharmony_ci UTIL_SUFFIX=${LLVM} 2862306a36Sopenharmony_ci fi 2962306a36Sopenharmony_cifi 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciREADELF=${UTIL_PREFIX}readelf${UTIL_SUFFIX} 3262306a36Sopenharmony_ciADDR2LINE=${UTIL_PREFIX}addr2line${UTIL_SUFFIX} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciif [[ $1 == "-r" ]] ; then 3562306a36Sopenharmony_ci vmlinux="" 3662306a36Sopenharmony_ci basepath="auto" 3762306a36Sopenharmony_ci modpath="" 3862306a36Sopenharmony_ci release=$2 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci for fn in {,/usr/lib/debug}/boot/vmlinux-$release{,.debug} /lib/modules/$release{,/build}/vmlinux ; do 4162306a36Sopenharmony_ci if [ -e "$fn" ] ; then 4262306a36Sopenharmony_ci vmlinux=$fn 4362306a36Sopenharmony_ci break 4462306a36Sopenharmony_ci fi 4562306a36Sopenharmony_ci done 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if [[ $vmlinux == "" ]] ; then 4862306a36Sopenharmony_ci echo "ERROR! vmlinux image for release $release is not found" >&2 4962306a36Sopenharmony_ci usage 5062306a36Sopenharmony_ci exit 2 5162306a36Sopenharmony_ci fi 5262306a36Sopenharmony_cielse 5362306a36Sopenharmony_ci vmlinux=$1 5462306a36Sopenharmony_ci basepath=${2-auto} 5562306a36Sopenharmony_ci modpath=$3 5662306a36Sopenharmony_ci release="" 5762306a36Sopenharmony_ci debuginfod= 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci # Can we use debuginfod-find? 6062306a36Sopenharmony_ci if type debuginfod-find >/dev/null 2>&1 ; then 6162306a36Sopenharmony_ci debuginfod=${1-only} 6262306a36Sopenharmony_ci fi 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if [[ $vmlinux == "" && -z $debuginfod ]] ; then 6562306a36Sopenharmony_ci echo "ERROR! vmlinux image must be specified" >&2 6662306a36Sopenharmony_ci usage 6762306a36Sopenharmony_ci exit 1 6862306a36Sopenharmony_ci fi 6962306a36Sopenharmony_cifi 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cideclare aarray_support=true 7262306a36Sopenharmony_cideclare -A cache 2>/dev/null 7362306a36Sopenharmony_ciif [[ $? != 0 ]]; then 7462306a36Sopenharmony_ci aarray_support=false 7562306a36Sopenharmony_cielse 7662306a36Sopenharmony_ci declare -A modcache 7762306a36Sopenharmony_cifi 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cifind_module() { 8062306a36Sopenharmony_ci if [[ -n $debuginfod ]] ; then 8162306a36Sopenharmony_ci if [[ -n $modbuildid ]] ; then 8262306a36Sopenharmony_ci debuginfod-find debuginfo $modbuildid && return 8362306a36Sopenharmony_ci fi 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci # Only using debuginfod so don't try to find vmlinux module path 8662306a36Sopenharmony_ci if [[ $debuginfod == "only" ]] ; then 8762306a36Sopenharmony_ci return 8862306a36Sopenharmony_ci fi 8962306a36Sopenharmony_ci fi 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if [[ "$modpath" != "" ]] ; then 9262306a36Sopenharmony_ci for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do 9362306a36Sopenharmony_ci if ${READELF} -WS "$fn" | grep -qwF .debug_line ; then 9462306a36Sopenharmony_ci echo $fn 9562306a36Sopenharmony_ci return 9662306a36Sopenharmony_ci fi 9762306a36Sopenharmony_ci done 9862306a36Sopenharmony_ci return 1 9962306a36Sopenharmony_ci fi 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci modpath=$(dirname "$vmlinux") 10262306a36Sopenharmony_ci find_module && return 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if [[ $release == "" ]] ; then 10562306a36Sopenharmony_ci release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" 2>/dev/null | sed -n 's/\$1 = "\(.*\)".*/\1/p') 10662306a36Sopenharmony_ci fi 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci for dn in {/usr/lib/debug,}/lib/modules/$release ; do 10962306a36Sopenharmony_ci if [ -e "$dn" ] ; then 11062306a36Sopenharmony_ci modpath="$dn" 11162306a36Sopenharmony_ci find_module && return 11262306a36Sopenharmony_ci fi 11362306a36Sopenharmony_ci done 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci modpath="" 11662306a36Sopenharmony_ci return 1 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ciparse_symbol() { 12062306a36Sopenharmony_ci # The structure of symbol at this point is: 12162306a36Sopenharmony_ci # ([name]+[offset]/[total length]) 12262306a36Sopenharmony_ci # 12362306a36Sopenharmony_ci # For example: 12462306a36Sopenharmony_ci # do_basic_setup+0x9c/0xbf 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if [[ $module == "" ]] ; then 12762306a36Sopenharmony_ci local objfile=$vmlinux 12862306a36Sopenharmony_ci elif [[ $aarray_support == true && "${modcache[$module]+isset}" == "isset" ]]; then 12962306a36Sopenharmony_ci local objfile=${modcache[$module]} 13062306a36Sopenharmony_ci else 13162306a36Sopenharmony_ci local objfile=$(find_module) 13262306a36Sopenharmony_ci if [[ $objfile == "" ]] ; then 13362306a36Sopenharmony_ci echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2 13462306a36Sopenharmony_ci return 13562306a36Sopenharmony_ci fi 13662306a36Sopenharmony_ci if [[ $aarray_support == true ]]; then 13762306a36Sopenharmony_ci modcache[$module]=$objfile 13862306a36Sopenharmony_ci fi 13962306a36Sopenharmony_ci fi 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci # Remove the englobing parenthesis 14262306a36Sopenharmony_ci symbol=${symbol#\(} 14362306a36Sopenharmony_ci symbol=${symbol%\)} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci # Strip segment 14662306a36Sopenharmony_ci local segment 14762306a36Sopenharmony_ci if [[ $symbol == *:* ]] ; then 14862306a36Sopenharmony_ci segment=${symbol%%:*}: 14962306a36Sopenharmony_ci symbol=${symbol#*:} 15062306a36Sopenharmony_ci fi 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci # Strip the symbol name so that we could look it up 15362306a36Sopenharmony_ci local name=${symbol%+*} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci # Use 'nm vmlinux' to figure out the base address of said symbol. 15662306a36Sopenharmony_ci # It's actually faster to call it every time than to load it 15762306a36Sopenharmony_ci # all into bash. 15862306a36Sopenharmony_ci if [[ $aarray_support == true && "${cache[$module,$name]+isset}" == "isset" ]]; then 15962306a36Sopenharmony_ci local base_addr=${cache[$module,$name]} 16062306a36Sopenharmony_ci else 16162306a36Sopenharmony_ci local base_addr=$(nm "$objfile" 2>/dev/null | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}') 16262306a36Sopenharmony_ci if [[ $base_addr == "" ]] ; then 16362306a36Sopenharmony_ci # address not found 16462306a36Sopenharmony_ci return 16562306a36Sopenharmony_ci fi 16662306a36Sopenharmony_ci if [[ $aarray_support == true ]]; then 16762306a36Sopenharmony_ci cache[$module,$name]="$base_addr" 16862306a36Sopenharmony_ci fi 16962306a36Sopenharmony_ci fi 17062306a36Sopenharmony_ci # Let's start doing the math to get the exact address into the 17162306a36Sopenharmony_ci # symbol. First, strip out the symbol total length. 17262306a36Sopenharmony_ci local expr=${symbol%/*} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci # Now, replace the symbol name with the base address we found 17562306a36Sopenharmony_ci # before. 17662306a36Sopenharmony_ci expr=${expr/$name/0x$base_addr} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci # Evaluate it to find the actual address 17962306a36Sopenharmony_ci expr=$((expr)) 18062306a36Sopenharmony_ci local address=$(printf "%x\n" "$expr") 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci # Pass it to addr2line to get filename and line number 18362306a36Sopenharmony_ci # Could get more than one result 18462306a36Sopenharmony_ci if [[ $aarray_support == true && "${cache[$module,$address]+isset}" == "isset" ]]; then 18562306a36Sopenharmony_ci local code=${cache[$module,$address]} 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci local code=$(${ADDR2LINE} -i -e "$objfile" "$address" 2>/dev/null) 18862306a36Sopenharmony_ci if [[ $aarray_support == true ]]; then 18962306a36Sopenharmony_ci cache[$module,$address]=$code 19062306a36Sopenharmony_ci fi 19162306a36Sopenharmony_ci fi 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci # addr2line doesn't return a proper error code if it fails, so 19462306a36Sopenharmony_ci # we detect it using the value it prints so that we could preserve 19562306a36Sopenharmony_ci # the offset/size into the function and bail out 19662306a36Sopenharmony_ci if [[ $code == "??:0" ]]; then 19762306a36Sopenharmony_ci return 19862306a36Sopenharmony_ci fi 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci # Strip out the base of the path on each line 20162306a36Sopenharmony_ci code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code") 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci # In the case of inlines, move everything to same line 20462306a36Sopenharmony_ci code=${code//$'\n'/' '} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci # Demangle if the name looks like a Rust symbol and if 20762306a36Sopenharmony_ci # we got a Rust demangler 20862306a36Sopenharmony_ci if [[ $name =~ ^_R && $cppfilt != "" ]] ; then 20962306a36Sopenharmony_ci name=$("$cppfilt" "$cppfilt_opts" "$name") 21062306a36Sopenharmony_ci fi 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci # Replace old address with pretty line numbers 21362306a36Sopenharmony_ci symbol="$segment$name ($code)" 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cidebuginfod_get_vmlinux() { 21762306a36Sopenharmony_ci local vmlinux_buildid=${1##* } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if [[ $vmlinux != "" ]]; then 22062306a36Sopenharmony_ci return 22162306a36Sopenharmony_ci fi 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if [[ $vmlinux_buildid =~ ^[0-9a-f]+ ]]; then 22462306a36Sopenharmony_ci vmlinux=$(debuginfod-find debuginfo $vmlinux_buildid) 22562306a36Sopenharmony_ci if [[ $? -ne 0 ]] ; then 22662306a36Sopenharmony_ci echo "ERROR! vmlinux image not found via debuginfod-find" >&2 22762306a36Sopenharmony_ci usage 22862306a36Sopenharmony_ci exit 2 22962306a36Sopenharmony_ci fi 23062306a36Sopenharmony_ci return 23162306a36Sopenharmony_ci fi 23262306a36Sopenharmony_ci echo "ERROR! Build ID for vmlinux not found. Try passing -r or specifying vmlinux" >&2 23362306a36Sopenharmony_ci usage 23462306a36Sopenharmony_ci exit 2 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cidecode_code() { 23862306a36Sopenharmony_ci local scripts=`dirname "${BASH_SOURCE[0]}"` 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci echo "$1" | $scripts/decodecode 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cihandle_line() { 24462306a36Sopenharmony_ci if [[ $basepath == "auto" && $vmlinux != "" ]] ; then 24562306a36Sopenharmony_ci module="" 24662306a36Sopenharmony_ci symbol="kernel_init+0x0/0x0" 24762306a36Sopenharmony_ci parse_symbol 24862306a36Sopenharmony_ci basepath=${symbol#kernel_init (} 24962306a36Sopenharmony_ci basepath=${basepath%/init/main.c:*)} 25062306a36Sopenharmony_ci fi 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci local words 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci # Tokenize 25562306a36Sopenharmony_ci read -a words <<<"$1" 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci # Remove hex numbers. Do it ourselves until it happens in the 25862306a36Sopenharmony_ci # kernel 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci # We need to know the index of the last element before we 26162306a36Sopenharmony_ci # remove elements because arrays are sparse 26262306a36Sopenharmony_ci local last=$(( ${#words[@]} - 1 )) 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci for i in "${!words[@]}"; do 26562306a36Sopenharmony_ci # Remove the address 26662306a36Sopenharmony_ci if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then 26762306a36Sopenharmony_ci unset words[$i] 26862306a36Sopenharmony_ci fi 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci # Format timestamps with tabs 27162306a36Sopenharmony_ci if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then 27262306a36Sopenharmony_ci unset words[$i] 27362306a36Sopenharmony_ci words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}") 27462306a36Sopenharmony_ci fi 27562306a36Sopenharmony_ci done 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if [[ ${words[$last]} =~ ^[0-9a-f]+\] ]]; then 27862306a36Sopenharmony_ci words[$last-1]="${words[$last-1]} ${words[$last]}" 27962306a36Sopenharmony_ci unset words[$last] 28062306a36Sopenharmony_ci last=$(( $last - 1 )) 28162306a36Sopenharmony_ci fi 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then 28462306a36Sopenharmony_ci module=${words[$last]} 28562306a36Sopenharmony_ci module=${module#\[} 28662306a36Sopenharmony_ci module=${module%\]} 28762306a36Sopenharmony_ci modbuildid=${module#* } 28862306a36Sopenharmony_ci module=${module% *} 28962306a36Sopenharmony_ci if [[ $modbuildid == $module ]]; then 29062306a36Sopenharmony_ci modbuildid= 29162306a36Sopenharmony_ci fi 29262306a36Sopenharmony_ci symbol=${words[$last-1]} 29362306a36Sopenharmony_ci unset words[$last-1] 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci # The symbol is the last element, process it 29662306a36Sopenharmony_ci symbol=${words[$last]} 29762306a36Sopenharmony_ci module= 29862306a36Sopenharmony_ci modbuildid= 29962306a36Sopenharmony_ci fi 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci unset words[$last] 30262306a36Sopenharmony_ci parse_symbol # modifies $symbol 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci # Add up the line number to the symbol 30562306a36Sopenharmony_ci echo "${words[@]}" "$symbol $module" 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ciwhile read line; do 30962306a36Sopenharmony_ci # Let's see if we have an address in the line 31062306a36Sopenharmony_ci if [[ $line =~ \[\<([^]]+)\>\] ]] || 31162306a36Sopenharmony_ci [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then 31262306a36Sopenharmony_ci # Translate address to line numbers 31362306a36Sopenharmony_ci handle_line "$line" 31462306a36Sopenharmony_ci # Is it a code line? 31562306a36Sopenharmony_ci elif [[ $line == *Code:* ]]; then 31662306a36Sopenharmony_ci decode_code "$line" 31762306a36Sopenharmony_ci # Is it a version line? 31862306a36Sopenharmony_ci elif [[ -n $debuginfod && $line =~ PID:\ [0-9]+\ Comm: ]]; then 31962306a36Sopenharmony_ci debuginfod_get_vmlinux "$line" 32062306a36Sopenharmony_ci else 32162306a36Sopenharmony_ci # Nothing special in this line, show it as is 32262306a36Sopenharmony_ci echo "$line" 32362306a36Sopenharmony_ci fi 32462306a36Sopenharmony_cidone 325