Add bdl-lib to assist the programmer in debugging scripts and tests. --- bdl-exmpl.sh | 46 ++++++++++++ bdl-lib.sh | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++ t/t0014-bdl-lib.sh | 115 ++++++++++++++++++++++++++++ t/test-lib.sh | 4 + 4 files changed, 380 insertions(+) create mode 100755 bdl-exmpl.sh create mode 100644 bdl-lib.sh create mode 100755 t/t0014-bdl-lib.sh diff --git a/bdl-exmpl.sh b/bdl-exmpl.sh new file mode 100755 index 000000000..a47d82bca --- /dev/null +++ b/bdl-exmpl.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Examples using bdl-lib.sh + +# Source bdl-lib.sh +. bdl-lib.sh + +# These both output to the default bdl_stdout=1 +bdl +bdl "hi" +bdl 1 "hi" + +# Output to a file as parameter +echo -n >bdl_out.txt +bdl bdl_out.txt "hi to bdl_out.txt" +cat bdl_out.txt + +# Output to a file via bdl_dst +echo -n >bdl_out.txt +bdl_dst=bdl_out.txt +bdl "hi to bdl_out.txt" +cat bdl_out.txt +bdl_dst= + +# Output to FD 5 connected to 1 via bdl_stdout +bdl_stdout=5 +exec 5>&1 +bdl +bdl "hi" +bdl 5 "hi" +exec 1>&5 +bdl_stdout=1 + +# No printing to stdout +echo -n >bdl_out.txt +bdl_stdout=1 +bdl_dst= +bdl 0 "not printed" +bdl_stdout=0 +bdl +bdl bdl_out.txt "printed to bdl_out.txt" +bdl "not printed" +bdl_stdout=1 +cat bdl_out.txt + +# This prints a "0" since there is only one parameter +bdl 0 diff --git a/bdl-lib.sh b/bdl-lib.sh new file mode 100644 index 000000000..cecf726bb --- /dev/null +++ b/bdl-lib.sh @@ -0,0 +1,215 @@ +# ################################################################## +# Bash Debug logger scriplet, source into your script to use. +# +# Write debug info with file name and line number. +# +# If the number of parameters == 0 then just the file +# name and line number are printed to the destination. +# +# If the number of parameters == 1 then the file +# name and line number are printed followed by a space +# and then the parameter. +# +# If number of parameters > 1 then the first parameter +# is the destination and the other parameters are written +# to the destination. +# +# The destination can be the first parameter to bdl +# or bdl_stdout or bdl_dst. If the destination is empty +# then the data is written to bdl_stdout unless bdl_stdout +# is empty or 0 then no data is written. Also, if the +# destination is 0 no data is written. +# +# Examples: +#$ cat -n bdl-exmpl.sh +# 1 #!/usr/bin/env bash +# 2 # Examples using bdl-lib.sh +# 3 +# 4 # Source bdl-lib.sh +# 5 . bdl-lib.sh +# 6 +# 7 # These both output to the default bdl_stdout=1 +# 8 bdl +# 9 bdl "hi" +# 10 bdl 1 "hi" +# 11 +# 12 # Output to a file as parameter +# 13 echo -n >bdl_out.txt +# 14 bdl bdl_out.txt "hi to bdl_out.txt" +# 15 cat bdl_out.txt +# 16 +# 17 # Output to a file via bdl_dst +# 18 echo -n >bdl_out.txt +# 19 bdl_dst=bdl_out.txt +# 20 bdl "hi to bdl_out.txt" +# 21 cat bdl_out.txt +# 22 bdl_dst= +# 23 +# 24 # Output to FD 5 connected to 1 via bdl_stdout +# 25 bdl_stdout=5 +# 26 exec 5>&1 +# 27 bdl +# 28 bdl "hi" +# 29 bdl 5 "hi" +# 30 exec 1>&5 +# 31 bdl_stdout=1 +# 32 +# 33 # No printing to stdout +# 34 echo -n >bdl_out.txt +# 35 bdl_stdout=1 +# 36 bdl_dst= +# 37 bdl 0 "not printed" +# 38 bdl_stdout=0 +# 39 bdl +# 40 bdl bdl_out.txt "printed to bdl_out.txt" +# 41 bdl "not printed" +# 42 bdl_stdout=1 +# 43 cat bdl_out.txt +# 44 +# 45 # This prints a "0" since there is only one parameter +# 46 bdl 0 +# +# +# This is the output of bdl-exmpl.sh +# +# $ /bin/bash ./bdl-exmpl.sh +# bdl-exmpl.sh:8: +# bdl-exmpl.sh:9: hi +# bdl-exmpl.sh:10: hi +# bdl-exmpl.sh:14: hi to bdl_out.txt +# bdl-exmpl.sh:20: hi to bdl_out.txt +# bdl-exmpl.sh:27: +# bdl-exmpl.sh:28: hi +# bdl-exmpl.sh:29: hi +# bdl-exmpl.sh:40: printed to bdl_out.txt +# bdl-exmpl.sh:46: 0 +# ################################################################## + +BDL_LOADED=t + +# Prompt waitng for a return, q will exit +bdl_pause () { + read -p "Line ${BASH_LINENO}: $@" bdl_pause_v_ + [[ "$bdl_pause_v_" == "q" ]] && exit 1 +} + +# Initialize bdl variables if user didn't +[[ "$bdl_dst" == "" ]] && bdl_dst= +[[ "$bdl_stdout" == "" ]] && bdl_stdout=1 +[[ "$bdl_call_depth" == "" ]] && bdl_call_depth=0 +[[ "$bdl_call_stack_view" == "" ]] && bdl_call_stack_view=f + +# Initialize priviate bdl variables +_bdl_call_lineno_offset_array=() +_bdl_call_lineno_offset_array_idx=0 +_bdl_call_save=() +_bdl_call_save_idx=0 + +# Push bdl state and initialize call meta data. +# +# $1 is value for bdl_call_depth +# $2 Optional text of a script with where bdl calls +# will be found and used to compute lineno info. +bdl_push () { + # Push the bdl data + _bdl_call_save[$_bdl_call_save_idx]="bdl_dst=$bdl_dst; \ +bdl_stdout=$bdl_stdout; \ +bdl_call_depth=$bdl_call_depth; \ +bdl_call_stack_view=$bdl_call_stack_view; \ +_bdl_call_lineno_offset_array=(${_bdl_call_lineno_offset_array[*]}); \ +_bdl_call_lineno_offset_array_idx=$_bdl_call_lineno_offset_array_idx" + _bdl_call_save_idx=$((_bdl_call_save_idx+1)) + + # Set meta data to fudge line numbers when bdl is used in tests. + bdl_call_depth=$1 + shift + _bdl_call_lineno_offset_array_idx=0 + _bdl_call_lineno_offset_array=() + + if test "$1" != "" + then + # Read the script and find lines that begin with "bdl " + # and compute their offsets and saving them in an array + # that bdl will use to compute compute the lineno. + IFS=$'\n' read -d '' -r -a test_run_script_array <<< "$@" + for i in "${!test_run_script_array[@]}"; do + ln=${test_run_script_array[$i]} + tln="$(sed -e 's/^[[:space:]]*//' <<<$ln)" + if [[ "$tln" =~ ^bdl\ ]] + then + _bdl_call_lineno_offset_array+=$((i+1)) + fi + done + fi +} + +# Pop a previously save state. +bdl_pop () { + _bdl_call_save_idx=$((_bdl_call_save_idx-1)) + eval "${_bdl_call_save[$_bdl_call_save_idx]}" +} + +# Write debug info with no source or line number +bdl_nsl () { + if (( $# > 1 )); then + bdl_nsl_v_=$1 + shift + else + bdl_nsl_v_=$bdl_dst + fi + [[ "$bdl_nsl_v_" == "" ]] && bdl_nsl_v_=$bdl_stdout + if [[ "$bdl_nsl_v_" != "" && "$@" != "" ]]; then + if [[ $bdl_nsl_v_ =~ ^[0-9] ]]; then + # There's probably a better way, but this "works": + case $bdl_nsl_v_ in + 1) echo "$@" 1>&1 ;; + 2) echo "$@" 1>&2 ;; + 3) echo "$@" 1>&3 ;; + 4) echo "$@" 1>&4 ;; + 5) echo "$@" 1>&5 ;; + 6) echo "$@" 1>&6 ;; + 7) echo "$@" 1>&7 ;; + 8) echo "$@" 1>&8 ;; + 9) echo "$@" 1>&9 ;; + *) : ;; # 0 and all other characters are nop's + esac + else + echo "$@" >> $bdl_nsl_v_ + fi + fi + return 0 +} + +# Write debug info with file name and line number. +bdl () { + #View the call stack + if test "$bdl_call_stack_view" != "f" + then + for i in "${!BASH_SOURCE[@]}"; do + (( $i == 0 )) && ln=${LINENO} || ln=${BASH_LINENO[${i}-1]} + bdl_nsl "[$i] ${BASH_SOURCE[$i]##*/}:${FUNCNAME[$i]}:${ln}" + done + fi + + # The ${@:+ } only adds a space if $@ isn't empty. + # This is done because We allow the call to bdl to + # have no parameters and bdl then just prints the + # file name and line number which can be useful + # to know a line was processed but there is no need + # to print any other data. + bdl_ln=${BASH_LINENO[${bdl_call_depth}]} + if (( ${_bdl_call_lineno_offset_array_idx} < ${#_bdl_call_lineno_offset_array[@]} )) + then + bdl_offset=${_bdl_call_lineno_offset_array[$_bdl_call_lineno_offset_array_idx]} + bdl_ln=$((bdl_ln+bdl_offset)) + _bdl_call_lineno_offset_array_idx=$((_bdl_call_lineno_offset_array_idx+1)) + fi + if (( $# <= 1 )); then + bdl_nsl $bdl_dst "${BASH_SOURCE[${bdl_call_depth}+1]##*/}:${bdl_ln}:${@:+ }$@" + else + v_=$1 + shift + bdl_nsl $v_ "${BASH_SOURCE[${bdl_call_depth}+1]##*/}:${bdl_ln}:${@:+ }$@" + fi + return 0 +} diff --git a/t/t0014-bdl-lib.sh b/t/t0014-bdl-lib.sh new file mode 100755 index 000000000..a0546189a --- /dev/null +++ b/t/t0014-bdl-lib.sh @@ -0,0 +1,115 @@ +#!/bin/sh + +test_description='test bash debug logger' + +# Only execute if shell is bash +if test "$BASH_VERSION" != "" +then + +. ../bdl-lib.sh +bdl_dst=output + +fi + +. ./test-lib.sh + +test_expect_success 'do nothing' ' + printf "" >output && + printf "" >expected && + test_cmp expected output +' + +# Only execute if shell is bash +if test "$BASH_VERSION" != "" +then + +test_expect_success 'bdl_nsl only prints nothing' ' + printf "" >output && + bdl_nsl && + printf "" >expected && + test_cmp expected output +' + +test_expect_success 'bdl_nsl with string prints string only' ' + printf "" >output && + bdl_nsl "test 1" && + printf "test 1\n" >expected && + test_cmp expected output +' + +test_lineno=$LINENO +test_expect_success 'bdl only print source and linenumber' ' + printf "" >output && + bdl && + printf "t0014-bdl-lib.sh:$((test_lineno+3)):\n" >expected && + test_cmp expected output +' + +test_lineno=$LINENO +test_expect_success 'bdl with string prints source and linenumber and string' ' + printf "" >output && + bdl "test 1" && + printf "t0014-bdl-lib.sh:$((test_lineno+3)): test 1\n" >expected && + test_cmp expected output +' + +test_expect_success 'bdl 0 "nothing printed"' ' + printf "" >output && + printf "" >expected && + bdl 0 "nothing printed" && + test_cmp expected output +' + +# Save current bdl_dst and restore when test completes +#bdl_dst_save=$bdl_dst +#bdl_stdout_save=$bdl_stdout +bdl_push +test_expect_success 'bdl bdl_dst empty bdl_stdout=0 nothing printed' ' + bdl_dst= && + bdl_stdout=0 && + printf "" >output && + printf "" >expected && + bdl "nothing printed" && + test_cmp expected output +' +#bdl_dst=$bdl_dst_save +#bdl_stdout=$bdl_stdout_save +bdl_pop + +# Testing subroutine calls from a test verify bdl_push/pop works +# for direct calls and nested calls +subsub_lineno=$LINENO +subsub () { + bdl_push 0 + bdl "subsub line" + bdl_pop + return "0" +} + +sub_lineno=$LINENO +sub () { + bdl_push 0 + bdl "sub line" + subsub + bdl_pop + return "0" +} + +test_expect_success 'test calls subsub' ' + printf "" >output && + subsub && + printf "t0014-bdl-lib.sh:$((subsub_lineno+3)): subsub line\n" >expected && + test_cmp expected output +' + +test_expect_success 'test sub calls subsub' ' + printf "" >output && + sub && + printf "t0014-bdl-lib.sh:$((sub_lineno+3)): sub line +t0014-bdl-lib.sh:$((subsub_lineno+3)): subsub line\n" >expected && + test_cmp expected output +' + +fi + +test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index 7740d511d..85142e462 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -680,11 +680,15 @@ test_run_ () { trace=$trace_tmp fi + test "$BDL_LOADED" = "t" && bdl_push 4 "$@" + setup_malloc_check test_eval_ "$1" eval_ret=$? teardown_malloc_check + test "$BDL_LOADED" = "t" && bdl_pop + if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure" && test "$test_cleanup" != ":" then -- 2.16.3