functions: Improve TRACE_FUNC and DEBUG_STACK: provide full call stack traces in output

Signed-off-by: Thierry Laurion <insurgo@riseup.net>
This commit is contained in:
Thierry Laurion 2025-03-16 13:37:37 -04:00
parent 6279500e5b
commit 280cb1f706
No known key found for this signature in database
GPG Key ID: 9A53E1BB3FF00461

View File

@ -512,25 +512,53 @@ DO_WITH_DEBUG() {
return "$exit_status"
}
# Trace the current script and function.
# TRACE_FUNC outputs the function call stack in a readable format.
# It helps debug the execution path leading to the current function.
#
# The format of the output is:
# main(/path/to/script:line) -> function1(/path/to/file:line) -> function2(/path/to/file:line)
#
# Usage:
# Call TRACE_FUNC within any function to print the call hierarchy.
TRACE_FUNC() {
# Index [1] for BASH_SOURCE and FUNCNAME give us the caller location.
# FUNCNAME is 'main' if called from a script outside any function.
# BASH_LINENO is offset by 1, it provides the line that the
# corresponding FUNCNAME was _called from_, so BASH_LINENO[0] is the
# location of the caller.
TRACE "${BASH_SOURCE[1]}(${BASH_LINENO[0]}): ${FUNCNAME[1]}"
local i stack_trace=""
# Traverse the call stack from the earliest caller to the direct caller of TRACE_FUNC
for ((i=${#FUNCNAME[@]}-1; i>1; i--)); do
stack_trace+="${FUNCNAME[i]}(${BASH_SOURCE[i]}:${BASH_LINENO[i-1]}) -> "
done
# Append the direct caller (without extra " -> " at the end)
stack_trace+="${FUNCNAME[1]}(${BASH_SOURCE[1]}:${BASH_LINENO[0]})"
# Print the final trace output
TRACE "${stack_trace}"
}
# Show the entire current call stack in debug output - useful if a catastrophic
# error or something very unexpected occurs, like totally invalid parameters.
# DEBUG_STACK prints the entire call stack for debugging purposes.
# This function provides more detailed output than TRACE_FUNC, which is useful
# for diagnosing errors, tracking function calls, or understanding unexpected behavior.
#
# The output format:
# call stack: (N frames)
# - 0 - /path/to/file(line): function_name
# - 1 - /path/to/file(line): function_name
#
# Usage:
# Call DEBUG_STACK anywhere to display the full stack trace.
DEBUG_STACK() {
local FRAMES
FRAMES="${#FUNCNAME[@]}"
DEBUG "call stack: ($((FRAMES - 1)) frames)"
# Don't print DEBUG_STACK itself, start from 1
for i in $(seq 1 "$((FRAMES - 1))"); do
DEBUG "- $((i - 1)) - ${BASH_SOURCE[$i]}(${BASH_LINENO[$((i - 1))]}): ${FUNCNAME[$i]}"
local SKIP_FIRST=0
# If TRACE_FUNC called DEBUG_STACK, remove it from the stack to avoid redundancy.
[[ "${FUNCNAME[1]}" == "TRACE_FUNC" ]] && SKIP_FIRST=1
# Get the total number of stack frames
local FRAMES="${#FUNCNAME[@]}"
DEBUG "call stack: ($((FRAMES - 1 - SKIP_FIRST)) frames)"
# Iterate through the stack and print each function call with file and line number
for i in $(seq $((1 + SKIP_FIRST)) "$((FRAMES - 1))"); do
DEBUG "- $((i - 1 - SKIP_FIRST)) - ${BASH_SOURCE[$i]}(${BASH_LINENO[$((i - 1))]}): ${FUNCNAME[$i]}"
done
}