1# -*- shell-script -*- 2# Call Stack routines 3# 4# Copyright (C) 2002-2006, 2008-2010, 2014, 2017 5# Rocky Bernstein <rocky@gnu.org> 6# 7# This program is free software; you can redistribute it and/or 8# modify it under the terms of the GNU General Public License as 9# published by the Free Software Foundation; either version 2, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15# General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program; see the file COPYING. If not, write to 19# the Free Software Foundation, 59 Temple Place, Suite 330, Boston, 20# MA 02111 USA. 21 22#================ VARIABLE INITIALIZATIONS ====================# 23 24# _Dbg_stack_size: the number of entries on the call stack at the time 25# the hook was entered. Note that bash updates the stack inside the 26# debugger so it is important to save this value on entry. Also 27# note that the most recent entries are pushed or at position 0. 28# Thus to get position 0 of the debugged program we need to ignore leading 29# any debugger frames. 30typeset -i _Dbg_stack_size 31 32# Where are we in stack? This can be changed by "up", "down" or 33# "frame" commands. 0 means the most recent stack frame with respect 34# to the debugged program and _Dbg_stack_size is the 35# least-recent. Note that inside the debugger the stack is still 36# updated. On debugger entry, the value is set to 0 37typeset -i _Dbg_stack_pos 38 39# Save the last-entered frame for to determine stopping when 40# "set force" or step+ is in effect. 41typeset _Dbg_frame_last_filename='' 42typeset -i _Dbg_frame_last_lineno=0 43 44#======================== FUNCTIONS ============================# 45 46function _Dbg_frame_adjust { 47 (($# != 2)) && return 255 48 49 typeset -i count=$1 50 typeset -i signum=$2 51 52 typeset -i retval 53 _Dbg_frame_int_setup $count || return 2 54 55 typeset -i pos 56 if (( signum==0 )) ; then 57 if (( count < 0 )) ; then 58 ((pos = _Dbg_stack_size + count - 1)) 59 else 60 ((pos = count)) 61 fi 62 else 63 ((pos=_Dbg_stack_pos+(count*signum))) 64 fi 65 66 if (( pos < 0 )) ; then 67 _Dbg_errmsg 'Would be beyond bottom-most (most recent) entry.' 68 return 1 69 70 elif (( pos >= _Dbg_stack_size - 1 )) ; then 71 _Dbg_errmsg 'Would be beyond top-most (least recent) entry.' 72 return 1 73 fi 74 75 typeset -i adjusted_pos 76 adjusted_pos=$(_Dbg_frame_adjusted_pos $pos) 77 _Dbg_stack_pos=$pos 78 79 ## DEBUG 80 ## typeset -p pos 81 ## typeset -p adjusted_pos 82 ## typeset -p BASH_LINENO 83 ## typeset -p BASH_SOURCE 84 85 _Dbg_listline="${BASH_LINENO[adjusted_pos-1]}" 86 # _Dbg_frame_last_lineno="$_Dbg_listline" 87 _Dbg_frame_last_filename="${BASH_SOURCE[adjusted_pos]}" 88 typeset filename; filename="$(_Dbg_file_canonic "$_Dbg_frame_last_filename")" 89 _Dbg_frame_print '->' $_Dbg_stack_pos '' "$filename" $_Dbg_listline '' 90 _Dbg_print_location_and_command "$_Dbg_listline" 91 return 0 92} 93 94# Set $_Dbg_frame_filename to be frame file for the call stack at 95# given position $1 or _Dbg_stack_pos if $1 is omitted. If $2 is 96# given, it indicates if we want the basename only. Otherwise the 97# $_Dbg_set_basename setting is used. 0 is returned if no error, 98# nonzero means some sort of error. 99_Dbg_frame_file() { 100 (($# > 2)) && return 2 101 # FIXME check to see that $1 doesn't run off the end. 102 typeset -i pos=${1:-$_Dbg_stack_pos} 103 typeset -i basename_only=${2:-$_Dbg_set_basename} 104 _Dbg_frame_filename=${BASH_SOURCE[pos]} 105 (( basename_only )) && _Dbg_frame_filename=${_Dbg_frame_filename##*/} 106 return 0 107} 108 109# Set $_Dbg_frame_filename to be frame line for the call stack at 110# given position $1 or _Dbg_stack_pos if $1 is omitted. 0 is returned 111# if no error, nonzero means some sort of error. 112_Dbg_frame_line() { 113 (($# > 1)) && return 2 114 # FIXME check to see that $1 doesn't run off the end. 115 typeset -i pos=${1:-$_Dbg_stack_pos} 116 _Dbg_frame_last_lineno="${BASH_LINENO[pos]}" 117 return 0 118} 119 120# Tests for a signed integer parameter and set global retval 121# if everything is okay. Retval is set to 1 on error 122_Dbg_frame_int_setup() { 123 124 _Dbg_not_running && return 1 125 eval "$_seteglob" 126 if [[ $1 != '' && $1 != $_Dbg_signed_int_pat ]] ; then 127 _Dbg_errmsg "Bad integer parameter: $1" 128 eval "$_resteglob" 129 return 1 130 fi 131 eval "$_resteglob" 132 return 0 133} 134 135# Turn position $1 which uses 0 to represent the most-recent stack entry 136# into which may have additional internal debugger frames pushed on. 137function _Dbg_frame_adjusted_pos 138{ 139 if (($# != 1)) ; then 140 echo -n '-1' 141 return 1 142 fi 143 typeset -i pos 144 ((pos=${#FUNCNAME[@]} - _Dbg_stack_size + $1)) 145 echo -n $pos 146 return 0 147} 148 149# Creates a parameter string for return in non-local variable 150# _Dbg_parm_str. This is obtained from BASH_ARGC and BASH_ARGV. On 151# entry, _Dbg_next_argc, and _Dbg_next_argv should be set. These 152# variables and _Dbg_parm_str are updated on exit. _Dbg_next_argc is 153# and integer index into BASH_ARGC and _Dbg_next_argv is and index 154# into BASH_ARGV. On return 155_Dbg_frame_fn_param_str() { 156 (($# == 0)) || return 1 157 _Dbg_is_int "$_Dbg_next_argc" || return 2 158 _Dbg_is_int "$_Dbg_next_argv" || return 3 159 160 # add 1 to argument count to compensate for this call (of zero 161 # parameters) and at the same time we update _Dbg_next_argc for the 162 # next call. 163 # 164 ((_Dbg_next_argc++)) 165 typeset -i arg_count=BASH_ARGC[$_Dbg_next_argc] 166 if ((arg_count == 0)) ; then 167 _Dbg_parm_str='' 168 else 169 typeset -i i 170 _Dbg_parm_str="\"${BASH_ARGV[$_Dbg_next_argv+arg_count-1]}\"" 171 for (( i=1; i <= arg_count-1; i++ )) ; do 172 _Dbg_parm_str+=", \"${BASH_ARGV[$_Dbg_next_argv+arg_count-i-1]}\"" 173 done 174 ((_Dbg_next_argv+=arg_count)) 175 fi 176 return 0 177} 178 179_Dbg_frame_set_fn_param() { 180 (($# == 1)) || return 1 181 typeset -i skip_count=$1 182 # Set to ignore this call in computation 183 _Dbg_next_argc=1 184 _Dbg_next_argv=1 185 186 typeset -i i 187 for (( i=1; i <= skip_count; i++ )) ; do 188 typeset -i arg_count=${BASH_ARGC[$i]} 189 ((_Dbg_next_argv+=arg_count)) 190 done 191 # After this function returns argv will be one greater. So adjust 192 # for that now. 193 ((_Dbg_next_argc=skip_count)) 194 ((_Dbg_next_argv--)) 195 196 ## Debug: 197 ## typeset -p BASH_ARGC 198 ## typeset -p BASH_ARGV 199 ## typeset -p FUNCNAME 200 ## typeset -p _Dbg_next_argc 201 ## typeset -p _Dbg_next_argv 202} 203 204# Print "##" or "->" depending on whether or not $1 (POS) is a number 205# between 0 and _Dbg_stack_size-1. For POS, 0 is the top-most 206# (newest) entry. For _Dbg_stack_pos, 0 is the bottom-most entry. 207# 0 is returnd on success, nonzero on failure. 208function _Dbg_frame_prefix { 209 typeset prefix='??' 210 typeset -i rc=0 211 if (($# == 1)) ; then 212 typeset -i pos=$1 213 if ((pos < 0)) ; then 214 rc=2 215 elif ((pos >= _Dbg_stack_size)) ; then 216 rc=3 217 elif (( pos == _Dbg_stack_pos )) ; then 218 prefix='->' 219 else 220 prefix='##' 221 fi 222 else 223 rc=1 224 fi 225 echo -n $prefix 226 return $rc 227} 228 229# Print one line in a call stack 230function _Dbg_frame_print { 231 typeset prefix=$1 232 typeset -i pos=$2 233 typeset fn=$3 234 typeset filename="$4" 235 typeset -i line=$5 236 typeset args="$6" 237 typeset callstr=$fn 238 [[ -n $args ]] && callstr="$callstr($args)" 239 _Dbg_msg "$prefix$pos in file \`$filename' at line $line" 240} 241