# -*- shell-script -*- # Call Stack routines # # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010, 2014 # Rocky Bernstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 59 Temple Place, Suite 330, Boston, # MA 02111 USA. #================ VARIABLE INITIALIZATIONS ====================# # _Dbg_stack_size: the number of entries on the call stack at the time # the hook was entered. Note that bash updates the stack inside the # debugger so it is important to save this value on entry. Also # note that the most recent entries are pushed or at position 0. # Thus to get position 0 of the debugged program we need to ignore leading # any debugger frames. typeset -i _Dbg_stack_size # Where are we in stack? This can be changed by "up", "down" or # "frame" commands. 0 means the most recent stack frame with respect # to the debugged program and _Dbg_stack_size is the # least-recent. Note that inside the debugger the stack is still # updated. On debugger entry, the value is set to 0 typeset -i _Dbg_stack_pos # Save the last-entered frame for to determine stopping when # "set force" or step+ is in effect. typeset _Dbg_frame_last_filename='' typeset -i _Dbg_frame_last_lineno=0 #======================== FUNCTIONS ============================# proc _Dbg_frame_adjust { sh-expr '$# != 2)'&& return 255 typeset -i count=$1 typeset -i signum=$2 typeset -i retval _Dbg_frame_int_setup $count || return 2 typeset -i pos if sh-expr ' signum==0 )' { if sh-expr ' count < 0 )' { sh-expr 'pos = _Dbg_stack_size + count - 1' } else { sh-expr 'pos = count' } } else { sh-expr 'pos=_Dbg_stack_pos+(count*signum)' } if sh-expr ' pos < 0 )' { _Dbg_errmsg 'Would be beyond bottom-most (most recent) entry.' return 1 } elif sh-expr ' pos >= _Dbg_stack_size - 1 )' { _Dbg_errmsg 'Would be beyond top-most (least recent) entry.' return 1 } typeset -i adjusted_pos setglobal adjusted_pos = $[_Dbg_frame_adjusted_pos $pos] setglobal _Dbg_stack_pos = $pos ## DEBUG ## typeset -p pos ## typeset -p adjusted_pos ## typeset -p BASH_LINENO ## typeset -p BASH_SOURCE setglobal _Dbg_listline = $(BASH_LINENO[adjusted_pos-1]) setglobal _Dbg_frame_last_filename = $(BASH_SOURCE[adjusted_pos]) typeset filename; setglobal filename = $[_Dbg_file_canonic $_Dbg_frame_last_filename] _Dbg_frame_print '->' $_Dbg_stack_pos '' $filename $_Dbg_listline '' _Dbg_print_location_and_command $_Dbg_listline return 0 } # Set $_Dbg_frame_filename to be frame file for the call stack at # given position $1 or _Dbg_stack_pos if $1 is omitted. If $2 is # given, it indicates if we want the basename only. Otherwise the # $_Dbg_set_basename setting is used. 0 is returned if no error, # nonzero means some sort of error. proc _Dbg_frame_file { sh-expr '$# > 2)'&& return 2 # FIXME check to see that $1 doesn't run off the end. typeset -i pos=$(1:-$_Dbg_stack_pos) typeset -i basename_only=$(2:-$_Dbg_set_basename) setglobal _Dbg_frame_filename = $(BASH_SOURCE[pos]) sh-expr ' basename_only )'&& setglobal _Dbg_frame_filename = $(_Dbg_frame_filename##*/) return 0 } # Tests for a signed integer parameter and set global retval # if everything is okay. Retval is set to 1 on error proc _Dbg_frame_int_setup { _Dbg_not_running && return 1 eval $_seteglob if [[ $1 != '' && $1 != $_Dbg_signed_int_pat ]] { _Dbg_errmsg "Bad integer parameter: $1" eval $_resteglob return 1 } eval $_resteglob return 0 } # Turn position $1 which uses 0 to represent the most-recent stack entry # into which may have additional internal debugger frames pushed on. proc _Dbg_frame_adjusted_pos { if sh-expr '$# != 1)' { echo -n '-1' return 1 } typeset -i pos sh-expr 'pos=${#FUNCNAME[@]} - _Dbg_stack_size + $1' echo -n $pos return 0 } # Creates a parameter string for return in non-local variable # _Dbg_parm_str. This is obtained from BASH_ARGC and BASH_ARGV. On # entry, _Dbg_next_argc, and _Dbg_next_argv should be set. These # variables and _Dbg_parm_str are updated on exit. _Dbg_next_argc is # and integer index into BASH_ARGC and _Dbg_next_argv is and index # into BASH_ARGV. On return proc _Dbg_frame_fn_param_str { sh-expr '$# == 0)'|| return 1 _Dbg_is_int $_Dbg_next_argc || return 2 _Dbg_is_int $_Dbg_next_argv || return 3 # add 1 to argument count to compensate for this call (of zero # parameters) and at the same time we update _Dbg_next_argc for the # next call. # sh-expr '_Dbg_next_argc++' typeset -i arg_count=BASH_ARGC[$_Dbg_next_argc] if sh-expr 'arg_count == 0)' { setglobal _Dbg_parm_str = '''' } else { typeset -i i setglobal _Dbg_parm_str = ""\"$(BASH_ARGV[$_Dbg_next_argv+arg_count-1])"\"" for (( i=1; i <= arg_count-1; i++ )) ; do _Dbg_parm_str+=", \"${BASH_ARGV[$_Dbg_next_argv+arg_count-i-1]}\"" done sh-expr '_Dbg_next_argv+=arg_count' } return 0 } proc _Dbg_frame_set_fn_param { sh-expr '$# == 1)'|| return 1 typeset -i skip_count=$1 # Set to ignore this call in computation setglobal _Dbg_next_argc = '1' setglobal _Dbg_next_argv = '1' typeset -i i for (( i=1; i <= skip_count; i++ )) ; do typeset -i arg_count=${BASH_ARGC[$i]} ((_Dbg_next_argv+=arg_count)) done # After this function returns argv will be one greater. So adjust # for that now. sh-expr '_Dbg_next_argc=skip_count' sh-expr '_Dbg_next_argv--' ## Debug: ## typeset -p BASH_ARGC ## typeset -p BASH_ARGV ## typeset -p FUNCNAME ## typeset -p _Dbg_next_argc ## typeset -p _Dbg_next_argv } # Print "##" or "->" depending on whether or not $1 (POS) is a number # between 0 and _Dbg_stack_size-1. For POS, 0 is the top-most # (newest) entry. For _Dbg_stack_pos, 0 is the bottom-most entry. # 0 is returnd on success, nonzero on failure. proc _Dbg_frame_prefix { typeset prefix='??' typeset -i rc=0 if sh-expr '$# == 1)' { typeset -i pos=$1 if sh-expr 'pos < 0)' { setglobal rc = '2' } elif sh-expr 'pos >= _Dbg_stack_size)' { setglobal rc = '3' } elif sh-expr ' pos == _Dbg_stack_pos )' { setglobal prefix = ''->'' } else { setglobal prefix = ''##'' } } else { setglobal rc = '1' } echo -n $prefix return $rc } # Print one line in a call stack proc _Dbg_frame_print { typeset prefix=$1 typeset -i pos=$2 typeset fn=$3 typeset filename="$4" typeset -i line=$5 typeset args="$6" typeset callstr=$fn [[ -n $args ]] && setglobal callstr = ""$callstr($args)"" _Dbg_msg "$prefix$pos in file \`$filename' at line $line" }