diff --git a/doc/ref/debug.xml b/doc/ref/debug.xml index d9b963bf1e..04568d09d3 100644 --- a/doc/ref/debug.xml +++ b/doc/ref/debug.xml @@ -19,7 +19,8 @@ Probably the most important debugging tool in ⪆ is the break loop an statement into your code or by hitting Control-C. In the break loop one can inspect variables, stack traces and issue commands as usual in an interactive ⪆ session. See also the -, and +, , and + functions.

Sections  diff --git a/doc/ref/mloop.xml b/doc/ref/mloop.xml index 7e8832abb8..be0bee0747 100644 --- a/doc/ref/mloop.xml +++ b/doc/ref/mloop.xml @@ -739,14 +739,16 @@ is an example in the ⪆ code where the idea is actually used.

- + Backtrace Stack trace shows the last nr commands on the execution stack during whose execution the error occurred. If not given, nr defaults to 5. (Assume, for the following example, that after the last example -has been set back to its default value.) +has been set back to its default value.). acts the +same as while also showing the arguments and local +variables of each function.

StabChain(SymmetricGroup(100)); # After this we typed ^C diff --git a/lib/error.g b/lib/error.g index e63991ad90..4e6905273f 100644 --- a/lib/error.g +++ b/lib/error.g @@ -59,8 +59,49 @@ end); ErrorLVars := fail; -BIND_GLOBAL("WHERE", function( context, depth, outercontext) - local bottom, lastcontext, f; +# In this method the line hint changes in every PrintTo are balanced, because at the moment +# PrintTo(ERROR_OUTPUT,...) resets the indentation level every time it is called. +# If/when this is fixed, the indendation in this function could be simplified +BIND_GLOBAL("PRETTY_PRINT_VARS", function(context) + local vars, i, argcount, val; + vars := ContentsLVars(context); + if not IsRecord(vars) then + return; + fi; + + argcount := ABS_RAT(NumberArgumentsFunction(vars.func)); + PrintTo(ERROR_OUTPUT, "\>\>\n\<\"); + else + for i in [1..argcount] do + if IsBound(vars.values[i]) then + val := vars.values[i]; + else + val := ""; + fi; + PrintTo(ERROR_OUTPUT, "\>\>\>\>\n", vars.names[i], " :=\>\> ", val, "\<\<\<\<\<\<"); + od; + fi; + + PrintTo(ERROR_OUTPUT, "\>\>\n\<\"); + else + for i in [argcount+1..Length(vars.names)] do + if IsBound(vars.values[i]) then + val := vars.values[i]; + else + val := ""; + fi; + PrintTo(ERROR_OUTPUT, "\>\>\>\>\n", vars.names[i], " :=\>\> ", val, "\<\<\<\<\<\<"); + od; + fi; + PrintTo(ERROR_OUTPUT,"\n"); +end); + +BIND_GLOBAL("WHERE", function(context, depth, outercontext, showlocals) + local bottom, lastcontext, f, args; if depth <= 0 then return; fi; @@ -68,6 +109,10 @@ BIND_GLOBAL("WHERE", function( context, depth, outercontext) lastcontext := outercontext; while depth > 0 and context <> bottom do PRINT_CURRENT_STATEMENT(ERROR_OUTPUT, context); + if showlocals then + PRETTY_PRINT_VARS(context); + fi; + PrintTo(ERROR_OUTPUT, " called from\n"); lastcontext := context; context := ParentLVars(context); @@ -83,22 +128,38 @@ BIND_GLOBAL("WHERE", function( context, depth, outercontext) end); -BIND_GLOBAL("Where", function(arg) +BIND_GLOBAL("WHERE_INTERNAL", function(depth, showlocals) + if ErrorLVars = fail or ErrorLVars = GetBottomLVars() then + PrintTo(ERROR_OUTPUT, "not in any function "); + else + WHERE(ParentLVars(ErrorLVars), depth, ErrorLVars, showlocals); + fi; + PrintTo(ERROR_OUTPUT, "at ", INPUT_FILENAME(), ":", INPUT_LINENUMBER(), "\n"); +end); + +BIND_GLOBAL("WhereWithVars", function(arg) local depth; if LEN_LIST(arg) = 0 then depth := 5; else depth := arg[1]; fi; - - if ErrorLVars = fail or ErrorLVars = GetBottomLVars() then - PrintTo(ERROR_OUTPUT, "not in any function "); + + WHERE_INTERNAL(depth, true); +end); + +BIND_GLOBAL("Where", function(arg) + local depth; + if LEN_LIST(arg) = 0 then + depth := 5; else - WHERE(ParentLVars(ErrorLVars),depth, ErrorLVars); + depth := arg[1]; fi; - PrintTo(ERROR_OUTPUT, "at ",INPUT_FILENAME(),":",INPUT_LINENUMBER(),"\n"); + + WHERE_INTERNAL(depth, false); end); + OnBreak := Where; #OnBreak := function() diff --git a/tst/testspecial/backtrace.g b/tst/testspecial/backtrace.g index 2e895dcc7d..82ced1627d 100644 --- a/tst/testspecial/backtrace.g +++ b/tst/testspecial/backtrace.g @@ -5,72 +5,101 @@ f := function() end; f(); Where(); +WhereWithVars(); quit; f:=function() if true = 1/0 then return 1; fi; return 2; end;; f(); Where(); +WhereWithVars(); quit; f:=function() local x; if x then return 1; fi; return 2; end;; f(); Where(); +WhereWithVars(); quit; f:=function() if 1 then return 1; fi; return 2; end;; f(); Where(); +WhereWithVars(); quit; f:=function() if 1 < 0 then return 1; elif 1 then return 2; fi; return 3; end;; f(); Where(); +WhereWithVars(); quit; f:=function() while 1 do return 1; od; return 2; end;; f(); Where(); +WhereWithVars(); quit; f:=function() local i; for i in 1 do return 1; od; return 2; end;; f(); Where(); +WhereWithVars(); quit; f:=function() local i; for i in true do return 1; od; return 2; end;; f(); Where(); +WhereWithVars(); quit; +f:=function(x) local i,j; for i in true do return 1; od; return 2; end;; +f([1,2,3]); +Where(); +WhereWithVars(); +quit; + +f:=function(x) local i,j; Unbind(x); for i in true do return 1; od; return 2; end;; +f([1,2,3]); +Where(); +WhereWithVars(); +quit; + +f:=function(x) local i,j; Unbind(x); j := 4; for i in true do return 1; od; return 2; end;; +f([1,2,3]); +Where(); +WhereWithVars(); +quit; f:=function() local x; repeat x:=1; until 1; return 2; end;; f(); Where(); +WhereWithVars(); quit; f:=function() local x; Assert(0, 1); return 2; end;; f(); Where(); +WhereWithVars(); quit; f:=function() local x; Assert(0, 1, "hello"); return 2; end;; f(); Where(); +WhereWithVars(); quit; # Verify issue #2656 is fixed l := [[1]];; f := {} -> l[2,1];; f(); Where(); +WhereWithVars(); quit; # verify issue #1373 is fixed diff --git a/tst/testspecial/backtrace.g.out b/tst/testspecial/backtrace.g.out index de22536241..e550ce86ac 100644 --- a/tst/testspecial/backtrace.g.out +++ b/tst/testspecial/backtrace.g.out @@ -15,6 +15,14 @@ brk> Where(); l[[ 1 .. 3 ]] := 1; at *stdin*:5 called from ( ) called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +l[[ 1 .. 3 ]] := 1; at *stdin*:5 + arguments: + local variables: + l := [ 0, 0, 0, 0, 0, 0 ] + called from +( ) + called from read-eval loop at *errin*:2 brk> quit; gap> gap> @@ -28,6 +36,9 @@ type 'quit;' to quit to outer loop brk> Where(); ( ) called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +( ) + called from read-eval loop at *errin*:2 brk> quit; gap> gap> @@ -43,6 +54,9 @@ type 'quit;' to quit to outer loop brk> Where(); ( ) called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +( ) + called from read-eval loop at *errin*:2 brk> quit; gap> gap> @@ -58,6 +72,9 @@ type 'quit;' to quit to outer loop brk> Where(); ( ) called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +( ) + called from read-eval loop at *errin*:2 brk> quit; gap> gap> @@ -75,6 +92,9 @@ type 'quit;' to quit to outer loop brk> Where(); ( ) called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +( ) + called from read-eval loop at *errin*:2 brk> quit; gap> gap> @@ -90,6 +110,9 @@ type 'quit;' to quit to outer loop brk> Where(); ( ) called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +( ) + called from read-eval loop at *errin*:2 brk> quit; gap> gap> @@ -109,6 +132,16 @@ for i in 1 do od; at *stdin*:24 called from ( ) called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +for i in 1 do + return 1; +od; at *stdin*:24 + arguments: + local variables: + i := + called from +( ) + called from read-eval loop at *errin*:2 brk> quit; gap> gap> @@ -128,47 +161,155 @@ for i in true do od; at *stdin*:27 called from ( ) called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +for i in true do + return 1; +od; at *stdin*:27 + arguments: + local variables: + i := + called from +( ) + called from read-eval loop at *errin*:2 brk> quit; gap> +gap> f:=function(x) local i,j; for i in true do return 1; od; return 2; end;; +gap> f([1,2,3]); +Error, no method found! For debugging hints type ?Recovery from NoMethodFound +Error, no 1st choice method found for `Iterator' on 1 arguments at GAPROOT/lib/methsel2.g:249 called from +for i in true do + return 1; +od; at *stdin*:29 called from +( ) + called from read-eval loop at *stdin*:30 +type 'quit;' to quit to outer loop +brk> Where(); +for i in true do + return 1; +od; at *stdin*:29 called from +( ) + called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +for i in true do + return 1; +od; at *stdin*:29 + arguments: + x := [ 1, 2, 3 ] + local variables: + i := + j := + called from +( ) + called from read-eval loop at *errin*:2 +brk> quit; +gap> +gap> f:=function(x) local i,j; Unbind(x); for i in true do return 1; od; return 2; end;; +gap> f([1,2,3]); +Error, no method found! For debugging hints type ?Recovery from NoMethodFound +Error, no 1st choice method found for `Iterator' on 1 arguments at GAPROOT/lib/methsel2.g:249 called from +for i in true do + return 1; +od; at *stdin*:31 called from +( ) + called from read-eval loop at *stdin*:32 +type 'quit;' to quit to outer loop +brk> Where(); +for i in true do + return 1; +od; at *stdin*:31 called from +( ) + called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +for i in true do + return 1; +od; at *stdin*:31 + arguments: + x := + local variables: + i := + j := + called from +( ) + called from read-eval loop at *errin*:2 +brk> quit; +gap> +gap> f:=function(x) local i,j; Unbind(x); j := 4; for i in true do return 1; od; return 2; end;; +gap> f([1,2,3]); +Error, no method found! For debugging hints type ?Recovery from NoMethodFound +Error, no 1st choice method found for `Iterator' on 1 arguments at GAPROOT/lib/methsel2.g:249 called from +for i in true do + return 1; +od; at *stdin*:33 called from +( ) + called from read-eval loop at *stdin*:34 +type 'quit;' to quit to outer loop +brk> Where(); +for i in true do + return 1; +od; at *stdin*:33 called from +( ) + called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +for i in true do + return 1; +od; at *stdin*:33 + arguments: + x := + local variables: + i := + j := 4 + called from +( ) + called from read-eval loop at *errin*:2 +brk> quit; gap> gap> f:=function() local x; repeat x:=1; until 1; return 2; end;; gap> f(); Error, must be 'true' or 'false' (not the integer 1) in repeat x := 1; -until 1; at *stdin*:30 called from +until 1; at *stdin*:35 called from ( ) - called from read-eval loop at *stdin*:31 + called from read-eval loop at *stdin*:36 type 'quit;' to quit to outer loop brk> Where(); ( ) called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +( ) + called from read-eval loop at *errin*:2 brk> quit; gap> gap> gap> f:=function() local x; Assert(0, 1); return 2; end;; gap> f(); Error, Assert: must be 'true' or 'false' (not the integer 1) in - Assert( 0, 1 ); at *stdin*:33 called from + Assert( 0, 1 ); at *stdin*:38 called from ( ) - called from read-eval loop at *stdin*:34 + called from read-eval loop at *stdin*:39 type 'quit;' to quit to outer loop brk> Where(); ( ) called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +( ) + called from read-eval loop at *errin*:2 brk> quit; gap> gap> gap> f:=function() local x; Assert(0, 1, "hello"); return 2; end;; gap> f(); Error, Assert: must be 'true' or 'false' (not the integer 1) in - Assert( 0, 1, "hello" ); at *stdin*:36 called from + Assert( 0, 1, "hello" ); at *stdin*:41 called from ( ) - called from read-eval loop at *stdin*:37 + called from read-eval loop at *stdin*:42 type 'quit;' to quit to outer loop brk> Where(); ( ) called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +( ) + called from read-eval loop at *errin*:2 brk> quit; gap> gap> # Verify issue #2656 is fixed @@ -177,15 +318,29 @@ gap> f(); Error, List Element: [2] must have an assigned value in return m[i][j]; at GAPROOT/lib/matrix.gi:26 called from ELM_LIST( m, row, col ) at GAPROOT/lib/matobj.gi:27 called from -return l[2, 1]; at *stdin*:39 called from +return l[2, 1]; at *stdin*:44 called from ( ) - called from read-eval loop at *stdin*:40 + called from read-eval loop at *stdin*:45 type 'quit;' to quit to outer loop brk> Where(); ELM_LIST( m, row, col ) at GAPROOT/lib/matobj.gi:27 called from -return l[2, 1]; at *stdin*:39 called from +return l[2, 1]; at *stdin*:44 called from ( ) called from read-eval loop at *errin*:1 +brk> WhereWithVars(); +ELM_LIST( m, row, col ) at GAPROOT/lib/matobj.gi:27 + arguments: + m := [ [ 1 ] ] + row := 2 + col := 1 + local variables: + called from +return l[2, 1]; at *stdin*:44 + arguments: + local variables: + called from +( ) + called from read-eval loop at *errin*:2 brk> quit; gap> gap> # verify issue #1373 is fixed @@ -194,6 +349,6 @@ Error, FLAGS_FILTER: must be an operation (not a function) in <> from GAPROOT/lib/oper1.g:378 in function INSTALL_METHOD called from <> from GAPROOT/lib/oper1.g:338 in function InstallMethod called from ( ) - called from read-eval loop at *stdin*:42 + called from read-eval loop at *stdin*:47 type 'quit;' to quit to outer loop brk> QUIT;