Patchwork [1,of,1] scripts: add option to start an interactive debug shell

login
register
mail settings
Submitter Yann E. MORIN
Date Oct. 15, 2012, 9:45 p.m.
Message ID <de0568d968da614fe8f7.1350337513@treguer.bzh.lan>
Download mbox | patch
Permalink /patch/191668/
State Accepted
Commit 3a7b2eee9dcd
Headers show

Comments

Yann E. MORIN - Oct. 15, 2012, 9:45 p.m.
# HG changeset patch
# User "Yann E. MORIN" <yann.morin.1998@free.fr>
# Date 1349560087 -7200
# Node ID de0568d968da614fe8f7287dbd4d65e51b6096e2
# Parent  2a616dab6531ad82876c3252cd2e1cb873375c3f
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>
Patchwork-Id: 191571
Patchwork-Id: 191657


--
For unsubscribe information see http://sourceware.org/lists.html#faq

Patch

diff --git a/config/global/ct-behave.in b/config/global/ct-behave.in
--- a/config/global/ct-behave.in
+++ b/config/global/ct-behave.in
@@ -87,4 +87,23 @@ 
       
       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
diff --git a/scripts/functions b/scripts/functions
--- a/scripts/functions
+++ b/scripts/functions
@@ -5,6 +5,8 @@ 
 # Prepare the fault handler
 CT_OnError() {
     local ret=$?
+    local result
+    local old_trap
     local intro
     local file line func
     local step step_depth
@@ -35,6 +37,62 @@ 
             CT_DoLog ERROR ">>  ${intro}: ${func}[${file}${line}]"
             intro="      called from"
         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_BUILD_DIR}/backtrace"; return;;
+                2)  rm -f "${CT_BUILD_DIR}/backtrace"; touch "${CT_BUILD_DIR}/repeat"; return;;
+                # 3 is an abort, continue...
+            esac
+        fi
     fi
 
     # And finally, in top-level shell, print some hints
@@ -157,10 +215,11 @@ 
 # Usage: CT_DoExecLog <level> [VAR=val...] <command> [parameters...]
 CT_DoExecLog() {
     local level="$1"
+    local cur_cmd
     shift
     (
     for i in "$@"; do
-        tmp_log+="'${i}' "
+        cur_cmd+="'${i}' "
     done
     while true; do
         case "${1}" in
@@ -168,8 +227,39 @@ 
             *)      break;;
         esac
     done
-    CT_DoLog DEBUG "==> Executing: ${tmp_log}"
-    "${@}" 2>&1 |CT_DoLog "${level}"
+    # This while-loop goes hand-in-hand with the ERR trap handler:
+    # - 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
     [ $? -eq 0 ]