Testing and tools, 4 of 5
Debugging
Read a panic's stack trace and an uncaught error's, tell debug builds from release builds, and step through code in a debugger.
What you’ll build
A program with a bug, an index past the end of an array, three calls deep. You’ll read the stack trace its panic prints, see an uncaught error’s trace, compare a debug and a release build, and then stop at the bug in a debugger.
A panic’s stack trace
A panic is a bug the program can’t go on from, like an index out of range. It prints what went wrong and where, then a line for each call it was in, the innermost first, and the program exits with code 101.
Import std.core
Class Shelf
Field counts: Int[]
New(counts: Int[])
Self.counts = counts
End New
Method Take(slot: Int): Int
counts[slot] -= 1
Return counts[slot]
End Method
End Class
Function TakeEach(shelf: Shelf, slots: Int[])
For slot In slots
Print("slot {slot}: {shelf.Take(slot)} left")
End For
End Function
TakeEach(Shelf([3, 0, 5]), [0, 2, 3])slot 0: 2 left
slot 2: 4 left
panic: index 3 is out of range for 3 elements at main.fb:11:9
at Shelf.Take (main.fb:11)
at TakeEach (main.fb:18)
at top-level code (main.fb:22)Read the trace from the top: line 11 of Shelf.Take indexed slot 3 of a 3-element array, which
TakeEach called on line 18, which the top-level code called on line 22. The names are
FutureBlitz’s: a Method is Class.Method, and a generic function’s line names its type
arguments.
Every build prints stack traces, debug and release alike, with nothing to install or ship beside the program.
An uncaught error’s trace
An error that nothing catches ends the program too, with exit code 1. Its trace starts where the error was first thrown.
Import std.core
Class OutOfStock Extends Error
End Class
Function Take(count: Int): Int Throws OutOfStock
If count == 0 Then Throw OutOfStock("nothing left")
Return count - 1
End Function
Function Order(): Int Throws OutOfStock
Return Try Take(0)
End Function
Local left = Try Order()
Print(left)uncaught OutOfStock: nothing left at main.fb:7:24
at Take (main.fb:7)
at Order (main.fb:12)
at top-level code (main.fb:15)Debug and release builds
fb run and fb build make a debug build unless they’re given --release. A debug build checks
more, so bugs show where they happen:
- integer overflow panics, where a release build wraps around;
DebugAssertis checked, where a release build leaves it out;- the report of objects a program leaked also names each cycle of references that kept them;
- reading through a Null Ptr panics.
Assert, array indexes, and integer division by zero are checked in every build. This line
overflows an Int:
Import std.core
Local big: Int = 2147483647
big += 1
Print(big)panic: integer overflow in '+' at main.fb:4:1
at top-level code (main.fb:4)Built with fb build --release main.fb, the same program prints -2147483648. Debug builds are for
finding bugs; release builds, which are optimized, are for shipping.
A debugger
A debug build has full debug information, so a debugger can stop at a line, step through the
code, and show each variable’s value. The VS Code extension sets this up through CodeLLDB: open
the file and press F5. It builds the program in debug and runs it under the debugger. A
launch.json configuration of type futureblitz says what to debug, and with what arguments:
{
"type": "futureblitz",
"request": "launch",
"name": "Debug the shelf",
"program": "${workspaceFolder}/main.fb",
"args": []
}Then:
- Click left of line 11’s number in
Taketo set a breakpoint. - Press F5. The program stops at line 11 the first time
Takeruns. - The Variables view shows the parameter
slot, and Self with its fields, among themcounts, an array shown as its elements. - Press F5 to go on, and it stops again for each call, until
slotis 3.
fb ships a script for lldb that shows Strings, arrays, Maps, nullables, and objects as their values rather than their bytes, which the extension loads. Any debugger that reads DWARF, or on Windows a PDB, works with FutureBlitz programs.
What you learned
- A panic prints where it happened and a stack trace, and exits with code 101.
- An uncaught error prints a trace from where it was thrown, and exits with code 1.
- Debug builds panic on overflow, check DebugAsserts, and name leaked cycles; release builds don’t.
- A debug build has full debug information, and F5 in VS Code runs it in a debugger.
The rules are in its page in Tools.
Report an issue with this page or with FutureBlitz on GitHub.