From 320691f312287575140bde777a11035166fc53a0 Mon Sep 17 00:00:00 2001 From: Einhard Leichtfuß Date: Sun, 22 Dec 2024 04:08:55 +0100 Subject: Initial commit The basic.bash script is based on the one used in `github.com:lawandorga/laworga-mail-server.git`, and other versions used by me (which the `lawandorga-mail-server.git` one was based upon). The notes are generally new, but many of them just a consolidation and refinement of existing knowledge (of mine). --- docs/error-handling/subshell/repeated-printing.md | 84 +++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 docs/error-handling/subshell/repeated-printing.md (limited to 'docs/error-handling/subshell') diff --git a/docs/error-handling/subshell/repeated-printing.md b/docs/error-handling/subshell/repeated-printing.md new file mode 100644 index 0000000..fa94a1b --- /dev/null +++ b/docs/error-handling/subshell/repeated-printing.md @@ -0,0 +1,84 @@ +# Problem: Repeated printing of (parts of) the stack trace + +## Gist + +* With subshells, several stack traces may be printed. +* All but the first stack trace can be ignored. + + +## Problematic behaviour + +* For each subshell in the current stack of subshells (including the root + shell), we get a stack trace. + * The `ERR` trap is caught for each subshell. + * Reason: The trap on `ERR` returns non-zero, and so does the subshell. + * If it returned zero, the error would be ignored on the upper level, + and program execution continue, which is undesired. +* More precisely, each subshell gives a stack trace on the stack from itself + up to the root shell. + * Reason: Subshells inherit the knowledge of its ancestors, but do not + know of its descendant subshells. + * Thus, the closer to the root of the stack, the more often we get the + (same) information printed; while the information on where the error + originally occurred is printed only once. + + +## Desired behaviour + +* The whole stack trace is printed once, and nothing more. + * This would be achieved if only the subshell where the error originally + occurred were to print a stack trace. + + +## Considerations on fixes / improvements + +### Only print the stack trace for the root shell. + +* This is easy, just check for `[[ $BASH_SUBSHELL -eq 0 ]]`. +* This would mean that on error within a subshell, we do not get + information below where the first subshell was invoked. +* This may be deemed acceptable if subshells are rarely used and/or only + with short local code within. + * Short local code would be, e.g., `$(head -n 1 FILE)`. + * Short local code would not be, e.g., `$(local_nontrivial_function)`. + + +### Reserve a special exit code. + +* Let `$SPECIAL_EXIT_CODE` be some exit code distinct from `0` and `1`. +* Change `ERR` trap to `trap 'basic::on_error 0' ERR`, and define `on_error()` + as follows: + ``` + function basic::on_error() + { + [[ $? -eq $SPECIAL_EXIT_CODE ]] && exit 1 + local -ri offset="$1" + basic::print_stacktrace $((offset + 1)) + exit $SPECIAL_EXIT_CODE + } + ``` +* The idea is that `on_error()` only prints the stack trace in the lowest + subshell, which has the full stack trace. +* Naturally, this does not work properly if the original actual error had + `$SPECIAL_EXIT_CODE` as exit code. +* That is, we'd need an exit code that cannot occur anywhere else. + * This should be impossible in the general case. + + +### Use a temporary file + +* Use a temporary file to indicate whether `print_stacktrace()` was already + called in a subshell. +* This feels somewhat evil. + + +### Inspect `$BASH_COMMAND` + +* The `$BASH_COMMAND` variable contains the command executed that caused the + trap. +* We'd have to identify whether the command spawned a subshell (and the error + came from there). +* Note that `(` is a valid command name---but `$BASH_COMMAND` maintains any + necessary quoting. +* It might suffice to check for `(.*` and `${varname_regex}=\$(.*`, given the + [other self-imposed restrictions](../../error-handling.md#Subshell). -- cgit v1.2.3