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, 7:53 p.m.
Message ID <82c19a72f25a7b399f6b.1350330836@treguer.bzh.lan>
Download mbox | patch
Permalink /patch/191657/
State Superseded
Headers show

Comments

Yann E. MORIN - Oct. 15, 2012, 7:53 p.m.
# HG changeset patch
# User "Yann E. MORIN" <yann.morin.1998@free.fr>
# Date 1349560087 -7200
# Node ID 82c19a72f25a7b399f6be4e2c8c1cecc45d8c171
# 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, continue, or aborts
    at user's whim.

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: "Yann E. MORIN" <yann.morin.1998@free.fr>
Cc: Johannes Stezenbach <js@sig21.net>


--
For unsubscribe information see http://sourceware.org/lists.html#faq
Yann E. MORIN - Oct. 15, 2012, 8:04 p.m.
Johannes, All,

[Replying on my own patch. Yikes! ;-) ]

On Monday 15 October 2012 Yann E. MORIN wrote:
> # HG changeset patch
> # User "Yann E. MORIN" <yann.morin.1998@free.fr>
> # Date 1349560087 -7200
> # Node ID 82c19a72f25a7b399f6be4e2c8c1cecc45d8c171
> # Parent  2a616dab6531ad82876c3252cd2e1cb873375c3f
> scripts: add option to start an interactive debug shell
[--SNIP--]
> diff --git a/scripts/functions b/scripts/functions
> --- a/scripts/functions
> +++ b/scripts/functions
> @@ -5,10 +5,45 @@
>  # Prepare the fault handler
>  CT_OnError() {
>      local ret=$?
> +    local old_trap result
>      local intro
>      local file line func
>      local step step_depth
>  
> +    # If the user asked for interactive debugging, dump him/her to a shell
> +    if [ "${CT_DEBUG_INTERACTIVE}" = "y" ]; then

The debug-shell should not be spawned for commands that were not run via
CT_DoExecLog, otherwise it is impossible to properly resume, although it
would be possible to just 'continue' or 'abort', not 'repeat'.

So, what do you think for non-CT_DoExecLog commands:
  - don't run debug-shell at all, or
  - run debug-shell, but only allow 'continue' or 'abort' ?

Note: use [ -n "${cur_cmd}" ] to check if we're in CT_DoExecLog.

> +        # We do not want this sub-shell exit status to be caught
> +        old_trap="$(trap -p ERR)"

No need to save the old trap, it's not used.
Or, what was I thinking in the first place to do that?... O_o

> @@ -157,10 +192,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

The hunk above could well be a separate cset.

Regards,
Yann E. MORIN.

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,22 @@ 
       
       Say N, please.
 
+config DEBUG_INTERACTIVE
+    bool
+    prompt "Interactive shell on failed commands"
+    depends on EXPERIMENTAL
+    help
+      If you say 'y' here, then an interactive shell will be spawned for
+      each failed command run via CT_DoExecLog.
+      
+      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
+
 endif
diff --git a/scripts/functions b/scripts/functions
--- a/scripts/functions
+++ b/scripts/functions
@@ -5,10 +5,45 @@ 
 # Prepare the fault handler
 CT_OnError() {
     local ret=$?
+    local old_trap result
     local intro
     local file line func
     local step step_depth
 
+    # 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
+        old_trap="$(trap -p ERR)"
+        trap ERR
+        (
+            exec >&6
+            printf "Current command\n  %s\n" "${cur_cmd}"
+            printf "exited with error code: %d\n" ${ret}
+            printf "Please fix it up and finish by exiting the shell.\n"
+            while true; do
+                bash --rcfile <(echo "PS1='ct-ng:\w> '") -i
+                result=$?
+                case $result in
+                    1)  break;;
+                    2)  break;;
+                    3)  break;;
+                    *)  echo "please exit with one of these values:"
+                        echo "1  fixed, continue with next build command"
+                        echo "2  repeat this build command"
+                        echo "3  abort build"
+                        ;;
+                esac
+            done
+            exit $result
+        )
+        result=$?
+        case "${result}" in
+            1)  return;;
+            2)  touch "${CT_BUILD_DIR}/repeat"; return;;
+            # 3 is an abort, continue...
+        esac
+    fi
+
     # To avoid printing the backtace for each sub-shell
     # up to the top-level, just remember we've dumped it
     if [ ! -f "${CT_BUILD_DIR}/backtrace" ]; then
@@ -157,10 +192,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 +204,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 ]