Debugging tests that crash

How do I debug a test that segfaults?

❯ zig build test --summary all
test
└─ run test failure
error: while executing test 'sleigh.test.defined(id)', the following command terminated with signal 11 (expected exited with code 0):
/Users/joelr/Work/Zig/bebop-zig/.zig-cache/o/43d0e2ef192792eae7de1691cf7d93dc/test --seed=0x4a364834 --cache-dir=/Users/joelr/Work/Zig/bebop-zig/.zig-cache --listen=-
Build Summary: 3/5 steps succeeded; 1 failed; 19/19 tests passed

To repeat what dimdin said in more concrete terms: run the command that failed directly.

/Users/joelr/Work/Zig/bebop-zig/.zig-cache/o/43d0e2ef192792eae7de1691cf7d93dc/test --seed=0x4a364834 --cache-dir=/Users/joelr/Work/Zig/bebop-zig/.zig-cache --listen=-

And if you want to debug it with LLDB, prefix the command with lldb --

Sorry, I neglected to mention, also remove the --listen flag unless you’re trying to debug the interface between those processes.

Turns out my parser was too recursive. It’s curious that it crashed with bad access and did not run out of memory, though.

(lldb) r
Process 76795 launched: '/Users/joelr/Work/Zig/bebop-zig/.zig-cache/o/43d0e2ef192792eae7de1691cf7d93dc/test' (arm64)
Process 76795 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x16edfff70)
    frame #0: 0x00000001000d3e74 test`mecha.map__struct_26223.parse(allocator=<unavailable>, str=(ptr = <read memory from 0x16edfff80 failed (0 of 8 bytes read)>, len = <read memory from 0x16edfff88 failed (0 of 8 bytes read)>)) at mecha.zig:614
   611 	    const Res = Result(ReturnType(@TypeOf(conv)));
   612 	    typecheckParser(@TypeOf(parser));
   613 	    return .{ .parse = struct {
-> 614 	        fn parse(allocator: mem.Allocator, str: []const u8) Error!Res {
   615 	            const r = try parser.parse(allocator, str);
   616 	            return Res{ .value = conv(r.value), .rest = r.rest };
   617 	        }
Target 0: (test) stopped.
(lldb) bt
^C* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x16edfff70)
  * frame #0: 0x00000001000d3e74 test`mecha.map__struct_26223.parse(allocator=<unavailable>, str=(ptr = <read memory from 0x16edfff80 failed (0 of 8 bytes read)>, len = <read memory from 0x16edfff88 failed (0 of 8 bytes read)>)) at mecha.zig:614
    frame #1: 0x00000001000d409c test`mecha.map__struct_26293.parse(allocator=<unavailable>, str=(ptr = " defined (foo)", len = 14)) at mecha.zig:615:39
    frame #2: 0x00000001000d30d8 test`mecha.oneOf__struct_27067.parse(allocator=<unavailable>, str=(ptr = " defined (foo)", len = 14)) at mecha.zig:418:28
    frame #3: 0x00000001000d3784 test`mecha.ref__struct_26046.parse(allocator=mem.Allocator @ 0x0000000100116410, str=(ptr = " defined (foo)", len = 14)) at mecha.zig:937:32
    frame #4: 0x00000001000d387c test`mecha.convert__struct_26109.parse(allocator=<unavailable>, str=(ptr = " defined (foo)", len = 14)) at mecha.zig:499:39
    frame #5: 0x00000001000d3c14 test`mecha.combine__struct_26168.parse(allocator=mem.Allocator @ 0x0000000100116410, str=(ptr = " defined (foo)", len = 14)) at mecha.zig:366:43
    frame #6: 0x00000001000d3ea0 test`mecha.map__struct_26223.parse(allocator=<unavailable>, str=(ptr = " defined (foo)", len = 14)) at mecha.zig:615:39
    frame #7: 0x00000001000d409c test`mecha.map__struct_26293.parse(allocator=<unavailable>, str=(ptr = " defined (foo)", len = 14)) at mecha.zig:615:39
...
    frame #7817: 0x00000001000d3c14 test`mecha.combine__struct_26168.parse(allocator=mem.Allocator @ 0x0000000100116410, str=(ptr = " defined (foo)", len = 14)) at mecha.zig:366:43
    frame #7818: 0x00000001000d3ea0 test`mecha.map__struct_26223.parse(allocator=<unavailable>, str=(ptr = " defined (foo)", len = 14)) at mecha.zig:615:39

... Interrupted.