scripts: add option to start an interactive debug shell
author"Yann E. MORIN" <yann.morin.1998@free.fr>
Sat Oct 06 23:48:07 2012 +0200 (2012-10-06)
changeset 30833a7b2eee9dcd
parent 3082 39ec9e913d79
child 3084 95df9d32afd2
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
config/global/ct-behave.in
scripts/crosstool-NG.sh.in
scripts/functions
     1.1 --- a/config/global/ct-behave.in	Sun Oct 14 23:46:15 2012 +0000
     1.2 +++ b/config/global/ct-behave.in	Sat Oct 06 23:48:07 2012 +0200
     1.3 @@ -87,4 +87,23 @@
     1.4        
     1.5        Say N, please.
     1.6  
     1.7 +config DEBUG_INTERACTIVE
     1.8 +    bool
     1.9 +    prompt "Interactive shell on failed commands"
    1.10 +    help
    1.11 +      If you say 'y' here, then an interactive shell will be spawned for
    1.12 +      each failed command.
    1.13 +      
    1.14 +      This shell will have the same environment that the failed command
    1.15 +      was run with, and the working directory will be set to the directory
    1.16 +      the failed command was run in.
    1.17 +      
    1.18 +      After you fix the issue, you can exit the interactive shell with any
    1.19 +      of these exit codes:
    1.20 +        1  the issue was fixed, continue the build with the next command
    1.21 +        2  the issue was fixed, re-run the failed command
    1.22 +        3  abort the build
    1.23 +      
    1.24 +      Note: '2' is only possible for commands run via CT_DoExecLog, though.
    1.25 +
    1.26  endif
     2.1 --- a/scripts/crosstool-NG.sh.in	Sun Oct 14 23:46:15 2012 +0000
     2.2 +++ b/scripts/crosstool-NG.sh.in	Sat Oct 06 23:48:07 2012 +0200
     2.3 @@ -25,6 +25,14 @@
     2.4  . .config.2
     2.5  # Yes! We can do full logging from now on!
     2.6  
     2.7 +# If we want an interactive debug-shell, we must ensure these FDs
     2.8 +# are indeed connected to a terminal (and not redirected in any way).
     2.9 +if [ "${CT_DEBUG_INTERACTIVE}" = "y" -a ! \( -t 0 -a -t 6 -a -t 2 \) ]; then
    2.10 +    CT_DoLog ERROR "Can't spawn interactive debug-shell,"
    2.11 +    CT_DoLog ERROR "because stdout/stderr has been redirected."
    2.12 +    exit 1
    2.13 +fi
    2.14 +
    2.15  # Override the locale early, in case we ever translate crosstool-NG messages
    2.16  if [ -z "${CT_NO_OVERIDE_LC_MESSAGES}" ]; then
    2.17      export LC_ALL=C
     3.1 --- a/scripts/functions	Sun Oct 14 23:46:15 2012 +0000
     3.2 +++ b/scripts/functions	Sat Oct 06 23:48:07 2012 +0200
     3.3 @@ -5,6 +5,8 @@
     3.4  # Prepare the fault handler
     3.5  CT_OnError() {
     3.6      local ret=$?
     3.7 +    local result
     3.8 +    local old_trap
     3.9      local intro
    3.10      local file line func
    3.11      local step step_depth
    3.12 @@ -35,6 +37,62 @@
    3.13              CT_DoLog ERROR ">>  ${intro}: ${func}[${file}${line}]"
    3.14              intro="      called from"
    3.15          done
    3.16 +
    3.17 +        # If the user asked for interactive debugging, dump him/her to a shell
    3.18 +        if [ "${CT_DEBUG_INTERACTIVE}" = "y" ]; then
    3.19 +            # We do not want this sub-shell exit status to be caught, because
    3.20 +            # it is absolutely legit that it exits with non-zero.
    3.21 +            # Save the trap handler to restore it after our debug-shell
    3.22 +            old_trap="$(trap -p ERR)"
    3.23 +            trap -- ERR
    3.24 +            (
    3.25 +                exec >&6
    3.26 +                printf "\r         \n\nCurrent command"
    3.27 +                if [ -n "${cur_cmd}" ]; then
    3.28 +                    printf ":\n  %s\n" "${cur_cmd}"
    3.29 +                else
    3.30 +                    printf " (unknown), "
    3.31 +                fi
    3.32 +                printf "exited with error code: %d\n" ${ret}
    3.33 +                printf "Please fix it up and finish by exiting the shell with one of these values:\n"
    3.34 +                printf "    1  fixed, continue with next build command\n"
    3.35 +                if [ -n "${cur_cmd}" ]; then
    3.36 +                    printf "    2  repeat this build command\n"
    3.37 +                fi
    3.38 +                printf "    3  abort build\n\n"
    3.39 +                while true; do
    3.40 +                    ${bash} --rcfile <(printf "PS1='ct-ng:\w> '\nPROMPT_COMMAND=''\n") -i
    3.41 +                    result=$?
    3.42 +                    case $result in
    3.43 +                        1)  printf "\nContinuing past the failed command.\n\n"
    3.44 +                            break
    3.45 +                            ;;
    3.46 +                        2)  if [ -n "${cur_cmd}" ]; then
    3.47 +                                printf "\nRe-trying last command.\n\n"
    3.48 +                                break
    3.49 +                            fi
    3.50 +                            ;;&
    3.51 +                        3)  break;;
    3.52 +                        *)  printf "\nPlease exit with one of these values:\n"
    3.53 +                            printf "    1  fixed, continue with next build command\n"
    3.54 +                            if [ -n "${cur_cmd}" ]; then
    3.55 +                                printf "    2  repeat this build command\n"
    3.56 +                            fi
    3.57 +                            printf "    3  abort build\n"
    3.58 +                            ;;
    3.59 +                    esac
    3.60 +                done
    3.61 +                exit $result
    3.62 +            )
    3.63 +            result=$?
    3.64 +            # Restore the trap handler
    3.65 +            eval "${old_trap}"
    3.66 +            case "${result}" in
    3.67 +                1)  rm -f "${CT_WORK_DIR}/backtrace"; return;;
    3.68 +                2)  rm -f "${CT_WORK_DIR}/backtrace"; touch "${CT_BUILD_DIR}/repeat"; return;;
    3.69 +                # 3 is an abort, continue...
    3.70 +            esac
    3.71 +        fi
    3.72      fi
    3.73  
    3.74      # And finally, in top-level shell, print some hints
    3.75 @@ -157,10 +215,11 @@
    3.76  # Usage: CT_DoExecLog <level> [VAR=val...] <command> [parameters...]
    3.77  CT_DoExecLog() {
    3.78      local level="$1"
    3.79 +    local cur_cmd
    3.80      shift
    3.81      (
    3.82      for i in "$@"; do
    3.83 -        tmp_log+="'${i}' "
    3.84 +        cur_cmd+="'${i}' "
    3.85      done
    3.86      while true; do
    3.87          case "${1}" in
    3.88 @@ -168,8 +227,39 @@
    3.89              *)      break;;
    3.90          esac
    3.91      done
    3.92 -    CT_DoLog DEBUG "==> Executing: ${tmp_log}"
    3.93 -    "${@}" 2>&1 |CT_DoLog "${level}"
    3.94 +    # This while-loop goes hand-in-hand with the ERR trap handler:
    3.95 +    # - if the command terminates successfully, then we hit the break
    3.96 +    #   statement, and we exit the loop
    3.97 +    # - if the command terminates in error, then the ERR handler kicks
    3.98 +    #   in, then:
    3.99 +    #     - if the user did *not* ask for interactive debugging, the ERR
   3.100 +    #       handler exits, and we hit the end of the sub-shell
   3.101 +    #     - if the user did ask for interactive debugging, the ERR handler
   3.102 +    #       spawns a shell. Upon termination of this shell, the ERR handler
   3.103 +    #       examines the exit status of the shell:
   3.104 +    #         - if 1, the ERR handler returns; then we hit the else statement,
   3.105 +    #           then the break, and we exit the 'while' loop, to continue the
   3.106 +    #           build;
   3.107 +    #         - if 2, the ERR handler touches the repeat file, and returns;
   3.108 +    #           then we hit the if statement, and we loop for one more
   3.109 +    #           iteration;
   3.110 +    #         - if 3, the ERR handler exits with the command's exit status,
   3.111 +    #           and we're dead;
   3.112 +    #         - for any other exit status of the shell, the ERR handler
   3.113 +    #           prints an informational message, and respawns the shell
   3.114 +    #
   3.115 +    # This allows a user to get an interactive shell that has the same
   3.116 +    # environment (PATH and so on) that the failed command was ran with.
   3.117 +    while true; do
   3.118 +        rm -f "${CT_BUILD_DIR}/repeat"
   3.119 +        CT_DoLog DEBUG "==> Executing: ${cur_cmd}"
   3.120 +        "${@}" 2>&1 |CT_DoLog "${level}"
   3.121 +        if [ -f "${CT_BUILD_DIR}/repeat" ]; then
   3.122 +            continue
   3.123 +        else
   3.124 +            break
   3.125 +        fi
   3.126 +    done
   3.127      )
   3.128      # Catch failure of the sub-shell
   3.129      [ $? -eq 0 ]