2 # bashdb - Bash shell debugger
4 # Adapted from an idea in O'Reilly's `Learning the Korn Shell'
5 # Copyright (C) 1993-1994 O'Reilly and Associates, Inc.
6 # Copyright (C) 1998, 1999, 2001 Gary V. Vaughan <gvv@techie.com>>
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 # As a special exception to the GNU General Public License, if you
23 # distribute this file as part of a program that contains a
24 # configuration script generated by Autoconf, you may include it under
25 # the same distribution terms that you use for the rest of that program.
29 # This program requires bash 2.x.
30 # If bash 2.x is installed as "bash2", you can invoke bashdb like this:
32 # DEBUG_SHELL=/bin/bash2 /bin/bash2 bashdb script.sh
37 # cond [break] [condition]
38 # tbreak [regexp|+lines]
40 # Variable watchpoints
41 # Instrument `source' and `.' files in $_potbelliedpig
42 # be cleverer about lines we allow breakpoints to be set on
43 # break [function_name]
45 echo 'Bash Debugger version 1.2.4'
47 export _dbname
=${0##*/}
49 if test $# -lt 1; then
50 echo "$_dbname: Usage: $_dbname filename" >&2
57 echo "$_dbname: Cannot read file '$_guineapig'." >&2
63 __debug
=${TMPDIR-/tmp}/bashdb.$$
64 sed -e '/^# bashdb - Bash shell debugger/,/^# -- DO NOT DELETE THIS LINE -- /d' "$0" > $__debug
65 cat $_guineapig >> $__debug
66 exec ${DEBUG_SHELL-bash} $__debug $_guineapig "$@"
70 # -- DO NOT DELETE THIS LINE -- The program depends on it
73 # $1 name of the original guinea pig script
81 shopt -s extglob
# turn on extglob so we can parse the debugger funcs
89 if (( ++__steptrap_calls
> 1 && $_curline == 1 )); then
93 if [ -n "$_disps" ]; then
94 while (( $i < ${#_disps[@]} ))
96 if [ -n "${_disps[$i]}" ]; then
97 _msg
"${_disps[$i]}: \c"
98 eval _msg
${_disps[$i]}
104 if (( $_trace )); then
108 if (( $_steps >= 0 )); then
109 let _steps
="$_steps - 1"
112 if _at_linenumbp
; then
113 _msg
"Reached breakpoint at line $_curline"
116 elif [ -n "$_brcond" ] && eval $_brcond; then
117 _msg
"Break condition $_brcond true at line $_curline"
120 elif (( $_steps == 0 )); then
121 # Assuming a real script will have the "#! /bin/sh" at line 1,
122 # assume that when $_curline == 1 we are inside backticks.
123 if (( ! $_trace )); then
124 _msg
"Stopped at line $_curline"
142 if [[ $1 == *(\
+)[1-9]*([0-9]) ]]; then
145 # normalize argument, then double it (+2 -> +2 + 2 = 4)
146 _x
=${1##*([!1-9])} # cut off non-numeric prefix
147 _x
=${x%%*([!0-9])} # cut off non-numeric suffix
155 # find the next valid line
157 while _invalidbreakp
$f
165 _msg
"Line $1 is not a valid breakpoint"
168 if [ -n "${_lines[$f]}" ]; then
170 _msg
"Breakpoint set at line $f"
172 _msg
"Breakpoints can only be set on executable lines"
175 _msg
"Please specify a numeric line number"
185 if [ -n "$_linebp" ]; then
187 for i
in ${_linebp[*]}; do
191 _msg
"No breakpoints have been set"
200 read -e -p "Delete all breakpoints? "
204 _msg
"All breakpoints have been cleared"
212 if [[ $1 == [1-9]*([0-9]) ]]; then
214 _msg
"Breakpoint cleared at line $1"
216 _msg
"Please specify a numeric line number"
224 if (( $# > 0 )); then
226 _msg
"Break when true: $_brcond"
229 _msg
"Break condition cleared"
238 _disps
[${#_disps[@]}]="$1"
239 if (( ${#_disps[@]} < 10 ))
241 _msg
" ${#_disps[@]}: $1"
243 _msg
"${#_disps[@]}: $1"
252 if [ -n "$_disps" ]; then
253 while (( $i < ${#_disps[@]} ))
256 if (( ${#_disps[@]} < 10 ))
258 _msg
" $j: ${_disps[$i]}"
260 _msg
"$j: ${_disps[$i]}"
265 _msg
"No displays have been set"
271 if (( $# < 1 )) ; then
272 read -e -p "Delete all display expressions? "
276 _msg
"All breakpoints have been cleared"
284 if [[ $1 == [1-9]*([0-9]) ]]; then
286 _msg
"Display $i has been cleared"
289 _msg
"Please specify a numeric display number"
295 # usage _ftrace -u funcname [funcname...]
298 local _opt
=-t _tmsg
="enabled" _func
299 if [[ $1 == -u ]]; then
305 declare -f $_opt $_func
306 _msg
"Tracing $_tmsg for function $_func"
314 while read -e -p "bashdb> " cmd args
; do
315 test -n "$cmd" && history -s "$cmd $args" # save on history list
316 test -n "$cmd" ||
{ set $_lastcmd; cmd
=$1; shift; args
=$
*; }
322 _lastcmd
="break $args"
325 _msg
"ambiguous command: '$cmd', condition, continue?"
327 cond|condi|condit|conditi|conditio|condition
)
329 _lastcmd
="condition $args"
331 c|cont|conti|contin|continu|
continue)
336 _msg
"ambiguous command: '$cmd', delete, display?"
338 de|del|dele|delet|delete
)
340 _lastcmd
="delete $args"
342 di|dis|disp|displ|displa|display
)
344 _lastcmd
="display $args"
346 f|ft|ftr|ftra|ftrace
)
348 _lastcmd
="ftrace $args"
356 # _lastcmd is set in the _displayscript function
360 _lastcmd
="print $args"
365 s|st|ste|step|n|ne|nex|next
)
366 let _steps
=${args:-1}
367 _lastcmd
="next $args"
373 u|un|und|undi|undis|undisp|undispl|undispla|undisplay
)
375 _lastcmd
="undisplay $args"
379 _lastcmd
="$cmd $args"
382 _msg
"Invalid command: '$cmd'"
389 function _at_linenumbp
391 [[ -n ${_linebp[$_curline]} ]]
394 function _invalidbreakp
396 local line
=${_lines[$1]}
398 # XXX - should use shell patterns
400 ||
expr "$line" : '[ \t]*#.*' > /dev
/null \
401 ||
expr "$line" : '[ \t]*;;[ \t]*$' > /dev
/null \
402 ||
expr "$line" : '[ \t]*[^)]*)[ \t]*$' > /dev
/null \
403 ||
expr "$line" : '[ \t]*;;[ \t]*#.**$' > /dev
/null \
404 ||
expr "$line" : '[ \t]*[^)]*)[ \t]*;;[ \t]*$' > /dev
/null \
405 ||
expr "$line" : '[ \t]*[^)]*)[ \t]*;;*[ \t]*#.*$' > /dev
/null
419 _msg
"Nothing to print"
423 function _displayscript
425 local i j start end bp cl
427 if (( $# == 1 )); then # list 5 lines on either side of $1
428 if [ $1 = "%" ]; then
430 let end
=${#_lines[@]}
435 elif (( $# > 1 )); then # list between start and end
436 if [ $1 = "^" ]; then
442 if [ $2 = "\$" ]; then
443 let end
=${#_lines[@]}
447 else # list 5 lines on either side of current line
448 let start
=$_curline-5
452 # normalize start and end
453 if (( $start < 1 )); then
456 if (( $end > ${#_lines[@]} )); then
460 cl
=$
(( $end - $start ))
461 if (( $cl > ${LINES-24} )); then
468 ( while (( $i <= $end )); do
473 # calculate the next block of lines
474 start
=$
(( $end + 1 ))
475 end
=$
(( $start + 11 ))
476 if (( $end > ${#_lines[@]} ))
481 _lastcmd
="list $start $end"
486 let _trace
="! $_trace"
487 if (( $_trace )); then
488 _msg
"Execution trace on"
490 _msg
"Execution trace off"
501 local i
=0 bp
=' ' line
=$1 cl
=' '
503 if [[ -n ${_linebp[$line]} ]]; then
507 if (( $_curline == $line )); then
511 if (( $line < 100 )); then
512 _msg
"${_guineapig/*\//}:$line $bp $cl${_lines[$line]}"
513 elif (( $line < 10 )); then
514 _msg
"${_guineapig/*\//}:$line $bp $cl${_lines[$line]}"
515 elif (( $line > 0 )); then
516 _msg
"${_guineapig/*\//}:$line $bp $cl${_lines[$line]}"
522 rm -f $__debug $_potbelliedpig 2> /dev
/null
527 _msg
'bashdb commands:
528 break N set breakpoint at line N
529 break list breakpoints & break condition
530 condition foo set break condition to foo
531 condition clear break condition
532 delete N clear breakpoint at line N
533 delete clear all breakpoints
534 display EXP evaluate and display EXP for each debug step
535 display show a list of display expressions
536 undisplay N remove display expression N
537 list N M display all lines of script between N and M
538 list N display 5 lines of script either side of line N
539 list display 5 lines if script either side of current line
540 continue continue execution upto next breakpoint
541 next [N] execute [N] statements (default 1)
542 print expr prints the value of an expression
543 trace toggle execution trace on/off
544 ftrace [-u] func make the debugger step into function FUNC
545 (-u turns off tracing FUNC)
547 ! string passes string to a shell
553 HISTFILE
=~
/.bashdb_history
557 # strings to save and restore the setting of `extglob' in debugger functions
559 _seteglob
='local __eopt=-u ; shopt -q extglob && __eopt=-s ; shopt -s extglob'
560 _resteglob
='shopt $__eopt extglob'
566 # Be careful about quoted newlines
567 _potbelliedpig
=${TMPDIR-/tmp}/${_guineapig/*\//}.$$
568 sed 's,\\$,\\\\,' $_guineapig > $_potbelliedpig
570 _msg
"Reading source from file: $_guineapig"
574 done < $_potbelliedpig
577 # Assuming a real script will have the "#! /bin/sh" at line 1,
578 # don't stop at line 1 on the first run
581 trap '_steptrap $LINENO' DEBUG