Skip to content
FutureBlitz

    Install

    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])
    Output
    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)
    Output
    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;
    • DebugAssert is 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)
    Output
    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:

    1. Click left of line 11’s number in Take to set a breakpoint.
    2. Press F5. The program stops at line 11 the first time Take runs.
    3. The Variables view shows the parameter slot, and Self with its fields, among them counts, an array shown as its elements.
    4. Press F5 to go on, and it stops again for each call, until slot is 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.

    Report an issue with this page or with FutureBlitz on GitHub.