18c2ecf20Sopenharmony_ci#!/bin/bash 28c2ecf20Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0 38c2ecf20Sopenharmony_ci# (c) 2014, Sasha Levin <sasha.levin@oracle.com> 48c2ecf20Sopenharmony_ci#set -x 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ciif [[ $# < 1 ]]; then 78c2ecf20Sopenharmony_ci echo "Usage:" 88c2ecf20Sopenharmony_ci echo " $0 -r <release> | <vmlinux> [base path] [modules path]" 98c2ecf20Sopenharmony_ci exit 1 108c2ecf20Sopenharmony_cifi 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci# Try to find a Rust demangler 138c2ecf20Sopenharmony_ciif type llvm-cxxfilt >/dev/null 2>&1 ; then 148c2ecf20Sopenharmony_ci cppfilt=llvm-cxxfilt 158c2ecf20Sopenharmony_cielif type c++filt >/dev/null 2>&1 ; then 168c2ecf20Sopenharmony_ci cppfilt=c++filt 178c2ecf20Sopenharmony_ci cppfilt_opts=-i 188c2ecf20Sopenharmony_cifi 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciUTIL_SUFFIX= 218c2ecf20Sopenharmony_ciif [[ -z ${LLVM:-} ]]; then 228c2ecf20Sopenharmony_ci UTIL_PREFIX=${CROSS_COMPILE:-} 238c2ecf20Sopenharmony_cielse 248c2ecf20Sopenharmony_ci UTIL_PREFIX=llvm- 258c2ecf20Sopenharmony_ci if [[ ${LLVM} == */ ]]; then 268c2ecf20Sopenharmony_ci UTIL_PREFIX=${LLVM}${UTIL_PREFIX} 278c2ecf20Sopenharmony_ci elif [[ ${LLVM} == -* ]]; then 288c2ecf20Sopenharmony_ci UTIL_SUFFIX=${LLVM} 298c2ecf20Sopenharmony_ci fi 308c2ecf20Sopenharmony_cifi 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciREADELF=${UTIL_PREFIX}readelf${UTIL_SUFFIX} 338c2ecf20Sopenharmony_ciADDR2LINE=${UTIL_PREFIX}addr2line${UTIL_SUFFIX} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ciif [[ $1 == "-r" ]] ; then 368c2ecf20Sopenharmony_ci vmlinux="" 378c2ecf20Sopenharmony_ci basepath="auto" 388c2ecf20Sopenharmony_ci modpath="" 398c2ecf20Sopenharmony_ci release=$2 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci for fn in {,/usr/lib/debug}/boot/vmlinux-$release{,.debug} /lib/modules/$release{,/build}/vmlinux ; do 428c2ecf20Sopenharmony_ci if [ -e "$fn" ] ; then 438c2ecf20Sopenharmony_ci vmlinux=$fn 448c2ecf20Sopenharmony_ci break 458c2ecf20Sopenharmony_ci fi 468c2ecf20Sopenharmony_ci done 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if [[ $vmlinux == "" ]] ; then 498c2ecf20Sopenharmony_ci echo "ERROR! vmlinux image for release $release is not found" >&2 508c2ecf20Sopenharmony_ci exit 2 518c2ecf20Sopenharmony_ci fi 528c2ecf20Sopenharmony_cielse 538c2ecf20Sopenharmony_ci vmlinux=$1 548c2ecf20Sopenharmony_ci basepath=${2-auto} 558c2ecf20Sopenharmony_ci modpath=$3 568c2ecf20Sopenharmony_ci release="" 578c2ecf20Sopenharmony_cifi 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cideclare aarray_support=true 608c2ecf20Sopenharmony_cideclare -A cache 2>/dev/null 618c2ecf20Sopenharmony_ciif [[ $? != 0 ]]; then 628c2ecf20Sopenharmony_ci aarray_support=false 638c2ecf20Sopenharmony_cielse 648c2ecf20Sopenharmony_ci declare -A modcache 658c2ecf20Sopenharmony_cifi 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cifind_module() { 688c2ecf20Sopenharmony_ci if [[ "$modpath" != "" ]] ; then 698c2ecf20Sopenharmony_ci for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do 708c2ecf20Sopenharmony_ci if ${READELF} -WS "$fn" | grep -qwF .debug_line ; then 718c2ecf20Sopenharmony_ci echo $fn 728c2ecf20Sopenharmony_ci return 738c2ecf20Sopenharmony_ci fi 748c2ecf20Sopenharmony_ci done 758c2ecf20Sopenharmony_ci return 1 768c2ecf20Sopenharmony_ci fi 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci modpath=$(dirname "$vmlinux") 798c2ecf20Sopenharmony_ci find_module && return 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if [[ $release == "" ]] ; then 828c2ecf20Sopenharmony_ci release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" 2>/dev/null | sed -n 's/\$1 = "\(.*\)".*/\1/p') 838c2ecf20Sopenharmony_ci fi 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci for dn in {/usr/lib/debug,}/lib/modules/$release ; do 868c2ecf20Sopenharmony_ci if [ -e "$dn" ] ; then 878c2ecf20Sopenharmony_ci modpath="$dn" 888c2ecf20Sopenharmony_ci find_module && return 898c2ecf20Sopenharmony_ci fi 908c2ecf20Sopenharmony_ci done 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci modpath="" 938c2ecf20Sopenharmony_ci return 1 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ciparse_symbol() { 978c2ecf20Sopenharmony_ci # The structure of symbol at this point is: 988c2ecf20Sopenharmony_ci # ([name]+[offset]/[total length]) 998c2ecf20Sopenharmony_ci # 1008c2ecf20Sopenharmony_ci # For example: 1018c2ecf20Sopenharmony_ci # do_basic_setup+0x9c/0xbf 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if [[ $module == "" ]] ; then 1048c2ecf20Sopenharmony_ci local objfile=$vmlinux 1058c2ecf20Sopenharmony_ci elif [[ $aarray_support == true && "${modcache[$module]+isset}" == "isset" ]]; then 1068c2ecf20Sopenharmony_ci local objfile=${modcache[$module]} 1078c2ecf20Sopenharmony_ci else 1088c2ecf20Sopenharmony_ci local objfile=$(find_module) 1098c2ecf20Sopenharmony_ci if [[ $objfile == "" ]] ; then 1108c2ecf20Sopenharmony_ci echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2 1118c2ecf20Sopenharmony_ci return 1128c2ecf20Sopenharmony_ci fi 1138c2ecf20Sopenharmony_ci if [[ $aarray_support == true ]]; then 1148c2ecf20Sopenharmony_ci modcache[$module]=$objfile 1158c2ecf20Sopenharmony_ci fi 1168c2ecf20Sopenharmony_ci fi 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci # Remove the englobing parenthesis 1198c2ecf20Sopenharmony_ci symbol=${symbol#\(} 1208c2ecf20Sopenharmony_ci symbol=${symbol%\)} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci # Strip segment 1238c2ecf20Sopenharmony_ci local segment 1248c2ecf20Sopenharmony_ci if [[ $symbol == *:* ]] ; then 1258c2ecf20Sopenharmony_ci segment=${symbol%%:*}: 1268c2ecf20Sopenharmony_ci symbol=${symbol#*:} 1278c2ecf20Sopenharmony_ci fi 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci # Strip the symbol name so that we could look it up 1308c2ecf20Sopenharmony_ci local name=${symbol%+*} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci # Use 'nm vmlinux' to figure out the base address of said symbol. 1338c2ecf20Sopenharmony_ci # It's actually faster to call it every time than to load it 1348c2ecf20Sopenharmony_ci # all into bash. 1358c2ecf20Sopenharmony_ci if [[ $aarray_support == true && "${cache[$module,$name]+isset}" == "isset" ]]; then 1368c2ecf20Sopenharmony_ci local base_addr=${cache[$module,$name]} 1378c2ecf20Sopenharmony_ci else 1388c2ecf20Sopenharmony_ci local base_addr=$(nm "$objfile" 2>/dev/null | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}') 1398c2ecf20Sopenharmony_ci if [[ $base_addr == "" ]] ; then 1408c2ecf20Sopenharmony_ci # address not found 1418c2ecf20Sopenharmony_ci return 1428c2ecf20Sopenharmony_ci fi 1438c2ecf20Sopenharmony_ci if [[ $aarray_support == true ]]; then 1448c2ecf20Sopenharmony_ci cache[$module,$name]="$base_addr" 1458c2ecf20Sopenharmony_ci fi 1468c2ecf20Sopenharmony_ci fi 1478c2ecf20Sopenharmony_ci # Let's start doing the math to get the exact address into the 1488c2ecf20Sopenharmony_ci # symbol. First, strip out the symbol total length. 1498c2ecf20Sopenharmony_ci local expr=${symbol%/*} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci # Now, replace the symbol name with the base address we found 1528c2ecf20Sopenharmony_ci # before. 1538c2ecf20Sopenharmony_ci expr=${expr/$name/0x$base_addr} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci # Evaluate it to find the actual address 1568c2ecf20Sopenharmony_ci expr=$((expr)) 1578c2ecf20Sopenharmony_ci local address=$(printf "%x\n" "$expr") 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci # Pass it to addr2line to get filename and line number 1608c2ecf20Sopenharmony_ci # Could get more than one result 1618c2ecf20Sopenharmony_ci if [[ $aarray_support == true && "${cache[$module,$address]+isset}" == "isset" ]]; then 1628c2ecf20Sopenharmony_ci local code=${cache[$module,$address]} 1638c2ecf20Sopenharmony_ci else 1648c2ecf20Sopenharmony_ci local code=$(${ADDR2LINE} -i -e "$objfile" "$address" 2>/dev/null) 1658c2ecf20Sopenharmony_ci if [[ $aarray_support == true ]]; then 1668c2ecf20Sopenharmony_ci cache[$module,$address]=$code 1678c2ecf20Sopenharmony_ci fi 1688c2ecf20Sopenharmony_ci fi 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci # addr2line doesn't return a proper error code if it fails, so 1718c2ecf20Sopenharmony_ci # we detect it using the value it prints so that we could preserve 1728c2ecf20Sopenharmony_ci # the offset/size into the function and bail out 1738c2ecf20Sopenharmony_ci if [[ $code == "??:0" ]]; then 1748c2ecf20Sopenharmony_ci return 1758c2ecf20Sopenharmony_ci fi 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci # Strip out the base of the path on each line 1788c2ecf20Sopenharmony_ci code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code") 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci # In the case of inlines, move everything to same line 1818c2ecf20Sopenharmony_ci code=${code//$'\n'/' '} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci # Demangle if the name looks like a Rust symbol and if 1848c2ecf20Sopenharmony_ci # we got a Rust demangler 1858c2ecf20Sopenharmony_ci if [[ $name =~ ^_R && $cppfilt != "" ]] ; then 1868c2ecf20Sopenharmony_ci name=$("$cppfilt" "$cppfilt_opts" "$name") 1878c2ecf20Sopenharmony_ci fi 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci # Replace old address with pretty line numbers 1908c2ecf20Sopenharmony_ci symbol="$segment$name ($code)" 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cidecode_code() { 1948c2ecf20Sopenharmony_ci local scripts=`dirname "${BASH_SOURCE[0]}"` 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci echo "$1" | $scripts/decodecode 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cihandle_line() { 2008c2ecf20Sopenharmony_ci local words 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci # Tokenize 2038c2ecf20Sopenharmony_ci read -a words <<<"$1" 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci # Remove hex numbers. Do it ourselves until it happens in the 2068c2ecf20Sopenharmony_ci # kernel 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci # We need to know the index of the last element before we 2098c2ecf20Sopenharmony_ci # remove elements because arrays are sparse 2108c2ecf20Sopenharmony_ci local last=$(( ${#words[@]} - 1 )) 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci for i in "${!words[@]}"; do 2138c2ecf20Sopenharmony_ci # Remove the address 2148c2ecf20Sopenharmony_ci if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then 2158c2ecf20Sopenharmony_ci unset words[$i] 2168c2ecf20Sopenharmony_ci fi 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci # Format timestamps with tabs 2198c2ecf20Sopenharmony_ci if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then 2208c2ecf20Sopenharmony_ci unset words[$i] 2218c2ecf20Sopenharmony_ci words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}") 2228c2ecf20Sopenharmony_ci fi 2238c2ecf20Sopenharmony_ci done 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then 2268c2ecf20Sopenharmony_ci module=${words[$last]} 2278c2ecf20Sopenharmony_ci module=${module#\[} 2288c2ecf20Sopenharmony_ci module=${module%\]} 2298c2ecf20Sopenharmony_ci symbol=${words[$last-1]} 2308c2ecf20Sopenharmony_ci unset words[$last-1] 2318c2ecf20Sopenharmony_ci else 2328c2ecf20Sopenharmony_ci # The symbol is the last element, process it 2338c2ecf20Sopenharmony_ci symbol=${words[$last]} 2348c2ecf20Sopenharmony_ci module= 2358c2ecf20Sopenharmony_ci fi 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci unset words[$last] 2388c2ecf20Sopenharmony_ci parse_symbol # modifies $symbol 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci # Add up the line number to the symbol 2418c2ecf20Sopenharmony_ci echo "${words[@]}" "$symbol $module" 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ciif [[ $basepath == "auto" ]] ; then 2458c2ecf20Sopenharmony_ci module="" 2468c2ecf20Sopenharmony_ci symbol="kernel_init+0x0/0x0" 2478c2ecf20Sopenharmony_ci parse_symbol 2488c2ecf20Sopenharmony_ci basepath=${symbol#kernel_init (} 2498c2ecf20Sopenharmony_ci basepath=${basepath%/init/main.c:*)} 2508c2ecf20Sopenharmony_cifi 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ciwhile read line; do 2538c2ecf20Sopenharmony_ci # Let's see if we have an address in the line 2548c2ecf20Sopenharmony_ci if [[ $line =~ \[\<([^]]+)\>\] ]] || 2558c2ecf20Sopenharmony_ci [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then 2568c2ecf20Sopenharmony_ci # Translate address to line numbers 2578c2ecf20Sopenharmony_ci handle_line "$line" 2588c2ecf20Sopenharmony_ci # Is it a code line? 2598c2ecf20Sopenharmony_ci elif [[ $line == *Code:* ]]; then 2608c2ecf20Sopenharmony_ci decode_code "$line" 2618c2ecf20Sopenharmony_ci else 2628c2ecf20Sopenharmony_ci # Nothing special in this line, show it as is 2638c2ecf20Sopenharmony_ci echo "$line" 2648c2ecf20Sopenharmony_ci fi 2658c2ecf20Sopenharmony_cidone 266