7 Error messages

A typical Gforth error message looks like this:

in file included from *args*:1:1: 
in file included from xxx-err1.fs:1:9: 
xxx-err2.fs:3:1: error: Invalid memory address
>>>bar<<<
Backtrace:
xxx-err2.fs:1:15:              0 $7FA610EA7F68 @ 
                               1 $14 
xxx-err2.fs:2:7:               2 $7FA610EA7FB0 foo 

The line identifying the error is

xxx-err2.fs:3:1: error: Invalid memory address

It says that in file xxx-err2.fs, line 3, character 1, the error “Invalid memory address” happened; more precisely, the exception -9 was thrown there, and eventually was caught by Gforth’s system exception handler (either because the program did not catch it or caught it and rethrew it).

The next line shows the text-interpreted line where the error happened, abd the word where the error happened is surrounded by >>> and <<<.

Before that you find the nesting of source file includes that led to the inclusion of xxx-err2.fs, with the outermost at the top and the innermost at the bottom. So in this case xxx-err2.fs was included from xxx-err1.fs, line 1, character 9, and that was included from an OS-level command-line argument to Gforth, indicated by *args*.

At the end of the error message you find the backtrace, actually a return stack dump. In our example, the middle line of the return stack dump just contains the number 20 ($14), and is not a return address. The number 1 before the $14 is the index of the return stack entry.

The bottom line of the backtrace shows a return address, pushed by the call to foo; the backtrace also shows foo for this entry, at the end, and the source location of the call xxx-err2.fs:2:7 at the beginning of the line.

The top line of the backtrace shows a pseudo-return address, as if the primitive @ pushed a return address. This return address was pushed by the signal handler in order to produce this line in the backtrace, so you know where the exception came from.

So in our example we see that bar called foo, and foo pushed 20 on the return stack and then called @ (and @ threw an Invalid memory address exception).

You can see the source code of the backtrace location with nt (next backtrace entry, initially the one with index 0, i.e., the top of the return stack), bt (previous backtrace entry, initially the one with the biggest index, i.e., the bottom of the return stack), or index tt (the indexed backtrace entry; see Locating backtrace entry source).

Note that the backtrace is not perfect: We don’t know which return stack entries are return addresses (so we may get false positives).

The return stack dump represents the return stack at the time when a specific throw was executed. In programs that make use of catch, it is not necessarily clear which throw should be used for the return stack dump (e.g., consider one throw that indicates an error, which is caught, and during recovery another error happens; which throw should be used for the stack dump?). Gforth presents the return stack dump for the first throw after the last executed (not returned-to) catch or nothrow; this works well in the usual case. To get the right backtrace, you usually want to insert nothrow or ['] false catch 2drop after a catch if the error is not rethrown.

The gforth engine is able to do a return stack dump for throws generated from primitives (e.g., invalid memory address, stack empty etc.); gforth-fast is only able to do a return stack dump from a directly called throw (including abort"). Given an exception caused by a primitive in gforth-fast, you will typically see no return stack dump at all; however, if the exception is caught by catch (e.g., for restoring some state), and then thrown again, the return stack dump will be for the first such throw.

gforth-fast also does not attempt to differentiate between division by zero and division overflow, because that costs time in every division.