scripts: add option to start an interactive debug shell

Add an option that, when a command fails:
  - starts an interactive shell with the failed command's environment
  - attempts re-execution of the failed command, continues, or aborts
    at user's whim.

Before starting the debug-shell, the backtrace is printed.
When exiting for an abort, the standard error message is printed.

Based on an idea and a patch from: Johannes Stezenbach <js@sig21.net>
    http://sourceware.org/ml/crossgcc/2012-09/msg00144.html

Signed-off-by: Johannes Stezenbach <js@sig21.net>
[yann.morin.1998@free.fr: integrate in the fault handler]
Signed-off-by: "Yann E. MORIN" <yann.morin.1998@free.fr>
Acked-by: Johannes Stezenbach <js@sig21.net>
Patchwork-Id: 191571
Patchwork-Id: 191668
This commit is contained in:
Yann E. MORIN" 2012-10-06 23:48:07 +02:00
parent df3be9eef3
commit dd98145bc1
3 changed files with 120 additions and 3 deletions

View File

@ -87,4 +87,23 @@ config NO_OVERIDE_LC_MESSAGES
Say N, please. Say N, please.
config DEBUG_INTERACTIVE
bool
prompt "Interactive shell on failed commands"
help
If you say 'y' here, then an interactive shell will be spawned for
each failed command.
This shell will have the same environment that the failed command
was run with, and the working directory will be set to the directory
the failed command was run in.
After you fix the issue, you can exit the interactive shell with any
of these exit codes:
1 the issue was fixed, continue the build with the next command
2 the issue was fixed, re-run the failed command
3 abort the build
Note: '2' is only possible for commands run via CT_DoExecLog, though.
endif endif

View File

@ -25,6 +25,14 @@
. .config.2 . .config.2
# Yes! We can do full logging from now on! # Yes! We can do full logging from now on!
# If we want an interactive debug-shell, we must ensure these FDs
# are indeed connected to a terminal (and not redirected in any way).
if [ "${CT_DEBUG_INTERACTIVE}" = "y" -a ! \( -t 0 -a -t 6 -a -t 2 \) ]; then
CT_DoLog ERROR "Can't spawn interactive debug-shell,"
CT_DoLog ERROR "because stdout/stderr has been redirected."
exit 1
fi
# Override the locale early, in case we ever translate crosstool-NG messages # Override the locale early, in case we ever translate crosstool-NG messages
if [ -z "${CT_NO_OVERIDE_LC_MESSAGES}" ]; then if [ -z "${CT_NO_OVERIDE_LC_MESSAGES}" ]; then
export LC_ALL=C export LC_ALL=C

View File

@ -5,6 +5,8 @@
# Prepare the fault handler # Prepare the fault handler
CT_OnError() { CT_OnError() {
local ret=$? local ret=$?
local result
local old_trap
local intro local intro
local file line func local file line func
local step step_depth local step step_depth
@ -35,6 +37,62 @@ CT_OnError() {
CT_DoLog ERROR ">> ${intro}: ${func}[${file}${line}]" CT_DoLog ERROR ">> ${intro}: ${func}[${file}${line}]"
intro=" called from" intro=" called from"
done done
# If the user asked for interactive debugging, dump him/her to a shell
if [ "${CT_DEBUG_INTERACTIVE}" = "y" ]; then
# We do not want this sub-shell exit status to be caught, because
# it is absolutely legit that it exits with non-zero.
# Save the trap handler to restore it after our debug-shell
old_trap="$(trap -p ERR)"
trap -- ERR
(
exec >&6
printf "\r \n\nCurrent command"
if [ -n "${cur_cmd}" ]; then
printf ":\n %s\n" "${cur_cmd}"
else
printf " (unknown), "
fi
printf "exited with error code: %d\n" ${ret}
printf "Please fix it up and finish by exiting the shell with one of these values:\n"
printf " 1 fixed, continue with next build command\n"
if [ -n "${cur_cmd}" ]; then
printf " 2 repeat this build command\n"
fi
printf " 3 abort build\n\n"
while true; do
${bash} --rcfile <(printf "PS1='ct-ng:\w> '\nPROMPT_COMMAND=''\n") -i
result=$?
case $result in
1) printf "\nContinuing past the failed command.\n\n"
break
;;
2) if [ -n "${cur_cmd}" ]; then
printf "\nRe-trying last command.\n\n"
break
fi
;;&
3) break;;
*) printf "\nPlease exit with one of these values:\n"
printf " 1 fixed, continue with next build command\n"
if [ -n "${cur_cmd}" ]; then
printf " 2 repeat this build command\n"
fi
printf " 3 abort build\n"
;;
esac
done
exit $result
)
result=$?
# Restore the trap handler
eval "${old_trap}"
case "${result}" in
1) rm -f "${CT_WORK_DIR}/backtrace"; return;;
2) rm -f "${CT_WORK_DIR}/backtrace"; touch "${CT_BUILD_DIR}/repeat"; return;;
# 3 is an abort, continue...
esac
fi
fi fi
# And finally, in top-level shell, print some hints # And finally, in top-level shell, print some hints
@ -157,10 +215,11 @@ CT_DoLog() {
# Usage: CT_DoExecLog <level> [VAR=val...] <command> [parameters...] # Usage: CT_DoExecLog <level> [VAR=val...] <command> [parameters...]
CT_DoExecLog() { CT_DoExecLog() {
local level="$1" local level="$1"
local cur_cmd
shift shift
( (
for i in "$@"; do for i in "$@"; do
tmp_log+="'${i}' " cur_cmd+="'${i}' "
done done
while true; do while true; do
case "${1}" in case "${1}" in
@ -168,8 +227,39 @@ CT_DoExecLog() {
*) break;; *) break;;
esac esac
done done
CT_DoLog DEBUG "==> Executing: ${tmp_log}" # This while-loop goes hand-in-hand with the ERR trap handler:
"${@}" 2>&1 |CT_DoLog "${level}" # - if the command terminates successfully, then we hit the break
# statement, and we exit the loop
# - if the command terminates in error, then the ERR handler kicks
# in, then:
# - if the user did *not* ask for interactive debugging, the ERR
# handler exits, and we hit the end of the sub-shell
# - if the user did ask for interactive debugging, the ERR handler
# spawns a shell. Upon termination of this shell, the ERR handler
# examines the exit status of the shell:
# - if 1, the ERR handler returns; then we hit the else statement,
# then the break, and we exit the 'while' loop, to continue the
# build;
# - if 2, the ERR handler touches the repeat file, and returns;
# then we hit the if statement, and we loop for one more
# iteration;
# - if 3, the ERR handler exits with the command's exit status,
# and we're dead;
# - for any other exit status of the shell, the ERR handler
# prints an informational message, and respawns the shell
#
# This allows a user to get an interactive shell that has the same
# environment (PATH and so on) that the failed command was ran with.
while true; do
rm -f "${CT_BUILD_DIR}/repeat"
CT_DoLog DEBUG "==> Executing: ${cur_cmd}"
"${@}" 2>&1 |CT_DoLog "${level}"
if [ -f "${CT_BUILD_DIR}/repeat" ]; then
continue
else
break
fi
done
) )
# Catch failure of the sub-shell # Catch failure of the sub-shell
[ $? -eq 0 ] [ $? -eq 0 ]