# -*- shell-script -*-
# gdb-like "backtrace" debugger command
#
# Copyright (C) 2002-2006, 2008, 2010-2011, 2018-2019
# Rocky Bernstein <rocky@gnu.org>
#
# 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.
# Print a stack backtrace.
# $1 is an additional offset correction - this routine is called from two
# different places and one routine has one more additional call on top.
# $2 is the maximum number of entries to include.
# $3 is which entry we start from; the "up", "down" and the "frame"
# commands may shift this.
# This code assumes the version of bash where FUNCNAME is an array,
# not a variable.
_Dbg_help_add backtrace \
'**backtrace** [*opts*] [*count*]
Print backtrace of all stack frames, or inner-most *count* frames.
With a negative argument, print outer-most -*count* frames.
An arrow indicates the "current frame". The current frame determines
the context used for many debugger commands such as expression
evaluation or source-line listing.
*opts* are:
-s | --source - show source code line
-h | --help - give this help
Examples:
---------
backtrace # Print a full stack trace
backtrace 2 # Print only the top two entries
backtrace -1 # Print a stack trace except the initial (least recent) call.
backtrace -s # show source lines in listing
backtrace --source # same as above
See also:
---------
**frame** and **list**
' 1 _Dbg_complete_backtrace
# Command completion for a frame command
_Dbg_complete_backtrace() {
typeset -i start=0
typeset -i end; ((end=_Dbg_stack_size-1))
_Dbg_complete_num_range $start $end
}
# FIXME: $1 is a hidden parameter not shown in help. $2 is COUNT.
# $3 is FRAME-INDEX.
function _Dbg_do_backtrace {
_Dbg_not_running && return 3
typeset -i show_source=0
OPTLIND=''
while getopts_long sh opt \
source no_argument \
help no_argument \
'' $@
do
case "$opt" in
s | source )
show_source=1
shift
;;
h | help)
_Dbg_do_help backtrace
return
;;
esac
done
typeset -i count=${1:-$_Dbg_stack_size}
$(_Dbg_is_int $count) || {
_Dbg_errmsg "Bad integer COUNT parameter: $count"
return 1
}
typeset -i frame_start=${2:-0}
if (( count < 0 )) ; then
(( frame_start = _Dbg_stack_size + count ))
(( count = _Dbg_stack_size ))
fi
$(_Dbg_is_int $frame_start) || {
_Dbg_errmsg "Bad integer parameter: $ignore_count"
return 1
}
# i is the logical frame value - 0 most recent frame.
typeset -i i=frame_start
typeset -li adjusted_pos
## DEBUG
## typeset -p pos
## typeset -p BASH_LINENO
## typeset -p BASH_SOURCE
## typeset -p FUNCNAME
typeset filename
typeset -i adjusted_pos
# Position 0 is special in that get the line number not from the
# stack but ultimately from LINENO which was saved in the hook call.
if (( frame_start == 0 )) ; then
((count--)) ;
adjusted_pos=$(_Dbg_frame_adjusted_pos 0)
filename=$(_Dbg_file_canonic "${BASH_SOURCE[$adjusted_pos]}")
_Dbg_frame_print $(_Dbg_frame_prefix 0) '0' '' "$filename" "$_Dbg_frame_last_lineno" ''
fi
typeset -i skip_fns
((skip_fns=${#FUNCNAME[@]} - _Dbg_stack_size + 1))
_Dbg_frame_set_fn_param $skip_fns
# Loop which dumps out stack trace.
## DEBUG
## typeset -p BASH_ARGC
## typeset -p BASH_ARGV
## typeset -p FUNCNAME
## typeset -p _Dbg_next_argc
## typeset -p _Dbg_next_argv
## echo "Adjusted pos $(_Dbg_frame_adjusted_pos 0)"
for (( i=frame_start+1 ;
i <= _Dbg_stack_size && count > 0 ;
i++ )) ; do
typeset -i arg_count=${BASH_ARGC[$_Dbg_next_argc]}
adjusted_pos=$(_Dbg_frame_adjusted_pos $i)
_Dbg_msg_nocr $(_Dbg_frame_prefix $i)$i ${FUNCNAME[$adjusted_pos-1]}
typeset parms=''
# Print out parameter list.
if (( 0 != ${#BASH_ARGC[@]} )) ; then
_Dbg_frame_fn_param_str
if [[ ${FUNCNAME[$adjusted_pos-1]} == "source" ]] ; then
_Dbg_parm_str=\"$(_Dbg_file_canonic "${BASH_ARGV[$_Dbg_next_argv-1]}")\"
fi
fi
typeset -l lineno
if (( adjusted_pos == ${#BASH_SOURCE[@]} )) ; then
lineno=0
((adjusted_pos--))
else
lineno=${BASH_LINENO[$adjusted_pos-1]}
fi
filename=$(_Dbg_file_canonic "${BASH_SOURCE[$adjusted_pos]}")
_Dbg_msg "($_Dbg_parm_str) called from file \`$filename'" "at line $lineno"
if (( show_source )) ; then
_Dbg_get_source_line $lineno "${BASH_SOURCE[$adjusted_pos]}"
_Dbg_printf "%s" "$_Dbg_source_line"
fi
((count--))
done
return 0
}
_Dbg_alias_add bt backtrace
_Dbg_alias_add T backtrace
_Dbg_alias_add where backtrace