git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[RFC PATCH 1/1] bdl-lib.sh: add bash debug logger

From
Wink Saville <wink@saville.com>
Date
Mar 28, 2018, 00:15 UTC
Message-ID
<126d0c61bf79c137bb2e220d57c94282719035f4.1522190580.git.wink@saville.com>
In-Reply-To
<cover.1522190580.git.wink@saville.com>
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
Previous: Wink SavilleNext: Johannes Schindelin
Message 2 of 8 in “bdl-lib.sh: add bash debug logger”
  1. 0/1 bdl-lib.sh: add bash debug loggerWink Saville, Mar 28, 2018
  2. 1/1 bdl-lib.sh: add bash debug loggerWink Saville, Mar 28, 2018
  3. Johannes SchindelinMar 30, 2018
  4. Wink SavilleMar 30, 2018
  5. Johannes SchindelinApr 5, 2018
  6. Wink SavilleApr 5, 2018
  7. Johannes SchindelinApr 5, 2018
  8. Wink SavilleApr 5, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.