1# bashdb.fns - Bourne-Again Shell Debugger functions 2 3_BUFSIZ=100 4 5# Here after each statement in script being debugged. 6# Handle single-step and breakpoints. 7_steptrap() { 8 let _curline=$1-1 # no. of line that just ran 9 let "$_curline < 1" && let _curline=1 10 11 let "$_curline > $_firstline+$_BUFSIZ" && _readin $_curline 12 13 let " $_trace" && 14 _msg "$PS4, line $_curline: ${_lines[$(($_curline-$_firstline+1))]}" 15 16 17 # if in step mode, decrement counter 18 let " $_steps >= 0" && let _steps="$_steps - 1" 19 20 # first check if line num or string brkpt. reached 21 if _at_linenumbp || _at_stringbp; then 22 _msg "Reached breakpoint at line $_curline" 23 _cmdloop # enter debugger 24 25 # if not, check whether break condition exists and is true 26 elif [ -n "$_brcond" ] && eval $_brcond; then 27 _msg "Break condition $_brcond true at line $_curline" 28 _cmdloop # enter debugger 29 30 # next, check if step mode and no. of steps is up 31 elif let "$_steps == 0"; then 32 _msg "Stopped at line $_curline" 33 _cmdloop # enter debugger 34 fi 35} 36 37 38# Debugger command loop. 39# Here at start of debugger session, when brkpt. reached, or after single-step. 40_cmdloop() { 41 local cmd args 42 43# added support for default command (last one entered) 44 45 while read -e -p "bashdb> [$lastcmd $lastargs] " cmd args; do 46 if [ -z "$cmd" ]; then 47 cmd=$lastcmd 48 args=$lastargs 49 fi 50 51 lastcmd="$cmd" 52 lastargs=$args 53 54# made commands to be debugger commands by default, no need for '*' prefix 55 56 case $cmd in 57 bp ) _setbp $args ;; #set brkpt at line num or string 58 59 bc ) _setbc $args ;; # set break condition 60 61 cb ) _clearbp ;; # clear all brkpts. 62 63 g ) return ;; # start/resume execution 64 65 s ) let _steps=${args:-1} 66 return ;; # single-step N times(default 1) 67 68 x ) _xtrace ;; # toggle execution trace 69 70 pr ) _print $args ;; # print lines in file 71 72 \? | h | help ) _menu ;; # print command menu 73 74 hi ) history ;; # show command history 75 76 q ) _cleanup; exit ;; # quit 77 78 \! ) eval $args ;; # run shell command 79 80 * ) _msg "Invalid command: $cmd" ; _menu ;; 81 esac 82 done 83} 84 85 86# see if next line no. is a brkpt. 87_at_linenumbp() { 88 if [ -z "${_linebp}" ]; then 89 return 1 90 fi 91 echo "${_curline}" | grep -E "(${_linebp%\|})" >/dev/null 2>&1 92 return $? 93} 94 95 96# search string brkpts to see if next line in script matches. 97_at_stringbp() { 98 local l; 99 100 if [ -z "$_stringbp" ]; then 101 return 1; 102 fi 103 l=${_lines[$_curline-$_firstline+1]} 104 echo "${l}" | grep -E "\\*(${_stringbp%\|})\\*" >/dev/null 2>&1 105 return $? 106} 107 108 109# print message to stderr 110_msg() { 111 echo -e "$@" >&2 112} 113 114 115# set brkpt(s) at given line numbers and/or strings 116# by appending lines to brkpt file 117_setbp() { 118 declare -i n 119 case "$1" in 120 "") _listbp ;; 121 [0-9]*) #number, set brkpt at that line 122 n=$1 123 _linebp="${_linebp}$n|" 124 _msg "Breakpoint at line " $1 125 ;; 126 *) #string, set brkpt at next line w/string 127 _stringbp="${_stringbp}$@|" 128 _msg "Breakpoint at next line containing $@." 129 ;; 130 esac 131} 132 133 134# list brkpts and break condition. 135_listbp() { 136 _msg "Breakpoints at lines:" 137 _msg "${_linebp//\|/ }" 138 _msg "Breakpoints at strings:" 139 _msg "${_stringbp//\|/ }" 140 _msg "Break on condition:" 141 _msg "$_brcond" 142} 143 144 145# set or clear break condition 146_setbc() { 147 if [ -n "$@" ] ; then 148 _brcond=$args 149 _msg "Break when true: $_brcond" 150 else 151 _brcond= 152 _msg "Break condition cleared" 153 fi 154} 155 156 157# clear all brkpts 158_clearbp() { 159 _linebp= 160 _stringbp= 161 _msg "All breakpoints cleared" 162} 163 164 165# toggle execution trace feature 166_xtrace() { 167 let _trace="! $_trace" 168 169 _msg "Execution trace \c" 170 let " $_trace" && _msg "on." || _msg "off." 171} 172 173 174# print command menu 175_menu() { 176 177# made commands to be debugger commands by default, no need for '*' prefix 178 179 _msg 'bashdb commands: 180 bp N set breakpoint at line N 181 bp string set breakpoint at next line containing "string" 182 bp list breakpoints and break condition 183 bc string set break condition to "string" 184 bc clear break condition 185 cb clear all breakpoints 186 g start/resume execution 187 s [N] execute N statements (default 1) 188 x toggle execution trace on/off (default on) 189 pr [start|.] [cnt] print "cnt" lines from line no. "start" 190 ?, h, help print this menu 191 hi show command history 192 q quit 193 194 ! cmd [args] execute command "cmd" with "args" 195 196 default: last command (in "[ ]" at the prompt) 197 198 Readline command line editing (emacs/vi mode) is available' 199} 200 201 202# erase temp files before exiting 203_cleanup() { 204 rm $_dbgfile 2>/dev/null 205} 206 207 208# read $_BUFSIZ lines from $_guineapig into _lines array, starting from line $1 209# save number of first line read in _firstline 210_readin() { 211 declare -i _i=1 212 let _firstline=$1 213 214 SEDCMD="$_firstline,$(($_firstline+$_BUFSIZ))p" 215 216 sed -n "$SEDCMD" $_guineapig > /tmp/_script.$$ 217 while read -r _lines[$_i]; do 218 _i=_i+1 219 done < /tmp/_script.$$ 220 rm -f /tmp/_script.$$ 2>/dev/null 221} 222 223_print() { 224 typeset _start _cnt 225 226 if [ -z "$1" ] || [ "$1" = . ]; then 227 _start=$_curline 228 else 229 _start=$1 230 fi 231 232 _cnt=${2:-9} 233 234 SEDCMD="$_start,$(($_start+$_cnt))p" 235 236 pr -tn $_guineapig | sed -n "$SEDCMD" 237} 238