[CSE]  Advanced Operating Systems 
COMP9242 2012/S2 
UNSW
CRICOS Provider
Number: 00098G

PRINTER Printer-Friendly Version

backtrace_symbol.sh

#!/bin/bash
#This script matches the program counter values returned by 
#backtrace() in sos to the function names. 
#the argument could be: -l -S for objdump extra options
#-w width for print out line width
#-f filename for get the input from the file

BACKTRACE_PC=""
LEVEL=0
OBJDUMP_OPTIONS="-d"
WIDTH=4

#help function for the backtrace_symbol script
print_help()
{
    echo "USEAGE:   backtrace_symbol [OPTIONS] <<EOF"
    echo "           PCs"
    echo "           <<EOF" 
    echo "OPTIONS:  -l printout line number for disassembly"
    echo "          -S printout source code for disassembly"
    echo "          -w WIDTH extra lines of disassembly for each side of the target PC, between 4 and 10 (default is 4)"
    echo "          --start-address=0xADDR only process data whose address is >= ADDR"
    echo "          --stop-address=0xADDR  only process data whose address is <= ADDR"
    echo "          --help printout this message"
    echo "Format:   the PCs should be started with a new line each"
    echo "          PCs are hex numbers, start with \"0x\""
    exit 0
    return
}


#process options for the script
process_options()
{
    #no options to process, return
    if [ $# -eq 0 ]
    then
	return
    fi

    #if the argument list is not empty
    while [ -n "$1" ]
    do 
	if [ "$1" = "-l" ]
	then
       	#add -l to the objdump options
	    OBJDUMP_OPTIONS="$OBJDUMP_OPTIONS -l"
	    
	elif [ "$1" = "-S" ] 
	then	    
	#add -S to the objdump options
	    OBJDUMP_OPTIONS="$OBJDUMP_OPTIONS -S"

	elif [ "$1" = "-w" ]
	then
	#resovle the line number surround each pc,[4-10]
	    if [ $2 -lt 11 ] && [ $2 -gt 3 ]; then
		WIDTH=$2
		shift
	    else
	    #call the help function
		print_help
	    fi	    
	    	    
	elif [ `expr "$1" : '--start-address=0x'` -eq 18 ]
	then 
	   #passing start address address to the objdump
	    OBJDUMP_OPTIONS="$OBJDUMP_OPTIONS $1"

	elif [ `expr "$1" : '--stop-address=0x'` -eq 17 ]
	then 
	   #passing stop address to the objdump
	    OBJDUMP_OPTIONS="$OBJDUMP_OPTIONS $1"

	elif [ "$1" = "--help" ]
	then
	    print_help

	else
	    #wrong option, printout help message
	    print_help
	fi
	#shift the arguement right to one position
	shift

    done

    return 
}

#process stdin
process_stdin()
{
    #read in the line if there is one available
    while read var1 var2
    do 

	if [ "$var2" != "" ]
	then
	    #maximum variables in each line is two
	    print_help

	elif [ ${#var1} -le 10 ] && [ `expr "$var1" : '0x'` -eq 2 ] && [[ "$var1" -gt "0x0" ]] && [[ "$var1" -lt "0xffffffff" ]]
	then
	    #record the backtrace pc if it is in the right range 0-0xffffffff
	    #the string is less than 10, start with "0x"
	    BACKTRACE_PC="$BACKTRACE_PC $var1"
	    
	else
       	#wrong input, print out help function
	    print_help
	    
	fi
	
    done
    return
}



#get the input from stdin, call function 
process_options $@
process_stdin


#if the backtrace_pc list is null, print out the help function and exit
if [ "$BACKTRACE_PC" = "" ]
then 
    echo "ERROR: empty PC list"
    print_help
fi

#list the symbol in increasing order
armeb-oe-linux-gnueabi-nm -n build/arm/nslu2/sos/sos.bin > sos.nm
if [ $? -ne 0 ]
then
    print_help
fi

armeb-oe-linux-gnueabi-objdump $OBJDUMP_OPTIONS build/arm/nslu2/sos/sos.bin > sos.dis
if [ $? -ne 0 ]
then
    print_help
fi


#for each address in the back trace pc list, do the search
for ADDRESS in $BACKTRACE_PC
do 
    echo "--------------------------------------------------------------------------------"
#search the function name with a given PC
    awk -v searchadd=$ADDRESS -v le=$LEVEL ' BEGIN {
           target = searchadd + 0   #numeric the searchadd
           resolve = 0
           }
         { 
          #the first string in each line is an 8 digits hex number
          if (length($1) != 8)
              next

          a1 = ("0x" $1) + 0  #numeric the string
          if (a1 < target) {
              pervious_name = $3
              pervious_add = a1 
          } else if (a1 == target) {
              #printout the name in this line
              #target find at the start of the function
              resolve = 1
              printf "    #%2d  0x%08x in %-30s\n", le, target, $3
              exit 1
          } else { 
               #printout the pervious recorded name
              resolve = 1
              printf "    #%2d  0x%08x in  %-30s\n",le,target, pervious_name
              exit 1
                }}
          END { if (resolve == 0) {
                    #the address can not be resolved
                    printf "    #%2d: can not resolve 0x%08x\n",le,target 
                    exit 2 }} ' sos.nm

#printout the relative disassembled code if the pc is resolved
    EXIT_STATUS=$?
    if [ $EXIT_STATUS -eq 1 ]; then
	echo ""
	awk -v searchadd=$ADDRESS -v width=$WIDTH ' BEGIN {
           add =  searchadd + 0     #numeric the search address
           start = add - width * 4
           end = add + width * 4
           flag = 0
          }
         {
          #for a correct disassembly line, the second string should be the machine code
          if (length($2) == 8 && $2 ~ /^[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]/) {

                #the first line should be the address of the pc, in the form hex:
                if (length($1) > 9)
                   next

                a1 = ("0x" $1) + 0 #numeric the pc string
                if (a1 == end) {
                    print $0
                    exit   #print finish
                 }

                 if (a1 == start) {
                    flag = 1   #set the print flag
                  }
            } 
            #print out the line if the flag is set    
            if (flag == 1) {
               print $0
            }
  

         }' sos.dis
    fi
    echo "--------------------------------------------------------------------------------"

    #increase the stack frame number by one
    LEVEL=`expr $LEVEL + 1`
done

#remove the temp files
#rm -f sos.nm
#rm -f sos.dis