Zig Debugging with LLDB

Short example of degugging with LLDB.
As an example, I will use a small program that I wrote for Rosetta Code:

const std = @import("std");
const stdout = @import("std").io.getStdOut().writer();

pub fn rot(txt: []u8, key: u8) void {
    for (txt, 0..txt.len) |c, i| {
        if (std.ascii.isLower(c)) {
            txt[i] = (c - 'a' + key) % 26 + 'a';
        } else if (std.ascii.isUpper(c)) {
            txt[i] = (c - 'A' + key) % 26 + 'A';
        }
    }
}

pub fn main() !void {
    const key = 3;
    var txt = "The five boxing wizards jump quickly".*;

    try stdout.print("Original:  {s}\n", .{txt});
    rot(&txt, key);
    try stdout.print("Encrypted: {s}\n", .{txt});
    rot(&txt, 26 - key);
    try stdout.print("Decrypted: {s}\n", .{txt});
}

Now let’s compile:

zig build-exe caesar.zig

Start debugging:

lldb caesar 
(lldb) target create "caesar"
Current executable set to '/home/chris/workspace/zig/rosetta/caesar' (x86_64).
(lldb)  β–ˆ

Now we set a breakpoint at main function:

(lldb) b main
Breakpoint 1: where = caesar`caesar.main + 18 at caesar.zig:16:5, address = 0x0000000001033c22
(lldb)  β–ˆ

And then run the program:

(lldb) r
Process 24175 launched: '/home/chris/workspace/zig/chris/usb/caesar' (x86_64)
Process 24175 stopped
* thread #1, name = 'caesar', stop reason = breakpoint 1.1
    frame #0: 0x0000000001033c22 caesar`caesar.main at caesar.zig:16:5
   13  	
   14  	pub fn main() !void {
   15  	   const key = 3;
-> 16  	   var txt = "The five boxing wizards jump quickly".*;
   17  	
   18  	   try stdout.print("Original:  {s}\n", .{txt});
   19  	   rot(&txt, key);
(lldb) β–ˆ

We can step forward with next:

(lldb) n
Process 24175 stopped
* thread #1, name = 'caesar', stop reason = step over
    frame #0: 0x0000000001033c43 caesar`caesar.main at caesar.zig:18:21
   15  	   const key = 3;
   16  	   var txt = "The five boxing wizards jump quickly".*;
   17  	
-> 18  	   try stdout.print("Original:  {s}\n", .{txt});
   19  	   rot(&txt, key);
   20  	   try stdout.print("Encrypted: {s}\n", .{txt});
   21  	   rot(&txt, 26 - key);
(lldb)  β–ˆ

And so on. A very nice feature is switching to GUI mode:

(lldb) gui
| LLDB (F1) | Target (F2) | Process (F3) | Thread (F4) | View (F5) | Help (F6) |                                                                                                                                                                                                                                               
β”Œβ”€β”€<Sources>β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”Œβ”€β”€<Threads>───────────────────────────────────────────────────┐
β”‚ caesar`caesar.main                                                                                                                                                                                                                                          β”‚β”‚ ◆─process 24175                                              β”‚
β”‚  1 β”‚ const std = @import("std");                                                                                                                                                                                                                            β”‚β”‚ └─◆─thread #1: tid = 0x5e6f, stop reason = step over         β”‚
β”‚  2 β”‚ const stdout = @import("std").io.getStdOut().writer();                                                                                                                                                                                                 β”‚β”‚   β”œβ”€#0: caesar.main + 51                                     β”‚
β”‚  3 β”‚                                                                                                                                                                                                                                                        β”‚β”‚   β”œβ”€#1: start.posixCallMainAndExit [inlined] start.callMain +β”‚
β”‚  4 β”‚ pub fn rot(txt: []u8, key: u8) void {                                                                                                                                                                                                                  β”‚β”‚   β”œβ”€#2: start.posixCallMainAndExit + 93                      β”‚
β”‚  5 β”‚     for (txt, 0..txt.len) |c, i| {                                                                                                                                                                                                                     β”‚β”‚   β”œβ”€#3: start.posixCallMainAndExit + 1133                    β”‚
β”‚  6 β”‚         if (std.ascii.isLower(c)) {                                                                                                                                                                                                                    β”‚β”‚   └─#4: start._start + 18                                    β”‚
β”‚  7 β”‚             txt[i] = (c - 'a' + key) % 26 + 'a';                                                                                                                                                                                                       β”‚β”‚                                                              β”‚
β”‚  8 β”‚         } else if (std.ascii.isUpper(c)) {                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚  9 β”‚             txt[i] = (c - 'A' + key) % 26 + 'A';                                                                                                                                                                                                       β”‚β”‚                                                              β”‚
β”‚ 10 β”‚         }                                                                                                                                                                                                                                              β”‚β”‚                                                              β”‚
β”‚ 11 β”‚     }                                                                                                                                                                                                                                                  β”‚β”‚                                                              β”‚
β”‚ 12 β”‚ }                                                                                                                                                                                                                                                      β”‚β”‚                                                              β”‚
β”‚ 13 β”‚                                                                                                                                                                                                                                                        β”‚β”‚                                                              β”‚
β”‚ 14 β”‚ pub fn main() !void {                                                                                                                                                                                                                                  β”‚β”‚                                                              β”‚
β”‚ 15 β”‚     const key = 3;                                                                                                                                                                                                                                     β”‚β”‚                                                              β”‚
β”‚ 16 β”‚     var txt = "The five boxing wizards jump quickly".*;                                                                                                                                                                                                β”‚β”‚                                                              β”‚
β”‚ 17 β”‚                                                                                                                                                                                                                                                        β”‚β”‚                                                              β”‚
β”‚ 18 β”‚β—†    try stdout.print("Original:  {s}\n", .{txt});                                                                                                                                                                               <<< Thread 1: step overβ”‚β”‚                                                              β”‚
β”‚ 19 β”‚     rot(&txt, key);                                                                                                                                                                                                                                    β”‚β”‚                                                              β”‚
β”‚ 20 β”‚     try stdout.print("Encrypted: {s}\n", .{txt});                                                                                                                                                                                                      β”‚β”‚                                                              β”‚
β”‚ 21 β”‚     rot(&txt, 26 - key);                                                                                                                                                                                                                               β”‚β”‚                                                              β”‚
β”‚ 22 β”‚     try stdout.print("Decrypted: {s}\n", .{txt});                                                                                                                                                                                                      β”‚β”‚                                                              β”‚
β”‚ 23 β”‚ }                                                                                                                                                                                                                                                      β”‚β”‚                                                              β”‚
β”‚ 24 β”‚                                                                                                                                                                                                                                                        β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚                                                              β”‚
β”Œβ”€β”€<Variables>────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐│                                                              β”‚
β”‚ ◆─(unsigned char[36]) txt "The five boxing wizards jump quickly"                                                                                                                                                                                            β”‚β”‚                                                              β”‚
β”‚ ◆─(elf.Elf64_Dyn) _DYNAMIC                                                                                                                                                                                                                                  β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β”‚                                                                                                                                                                                                                                                             β”‚β”‚                                                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Process: 24175    stopped               Thread: 24175       Frame:   0  PC = 0x0000000001033c43                                                                                                                                                                                                                                

You can set breakpoints there by navigating to a line and pressing β€˜b’. To exit the GUI, press Esc.
The other commands can be found in the help.

21 Likes

That was cool! Let me tell you that I enjoyed your ziglings exercices (and sense of humor you put in it) :grinning:

1 Like

Update:
A great video-tutorial on how to debug with lldb!

1 Like

This is a bit advanced but Jacob Young has a fork of LLDB that adds quite sophisticated Zig support including standard library integration, which is meant to pair with the self-hosted x86 backend (-fno-llvm -fno-lld).

This is a way better debugging experience if you are using that backend.

13 Likes

I’d like to add an extension to this thread: using lldb after a test.

Sometimes you have a failing test and you would like to use lldb to set breakpoints and observe the behavior of a smaller part of your function without having to run the entire program. This is possible to do with tests in Zig!

# filename: cumulative.zig
const std = @import("std");

fn cumulative_sum(slice: []u16) void {
    const l = slice.len - 1;
    for (slice[0..][0..l], slice[1..][0..l]) |*left, *right| {
        right.* = left.*; // the bug is here, I should use a +=
    }
}

test cumulative_sum {
    var slice = [_]u16{ 10, 20, 30, 40 };
    cumulative_sum(&slice);
    try std.testing.expectEqualSlices(u16, &[_]u16{ 10, 30, 60, 100 }, &slice);
}

now you can test

zig test cumulative.zig

You should see an error and at the very bottom you’ll find the path to the test object file:

0 passed; 0 skipped; 1 failed.
error: the following test command failed with exit code 1:
/path-to-zig-cache/.zig-cache/o/ead8187be37f01c571e970ede773ed12/test

Depending on if you are working in a project after using zig init or if you’re just working on a file somewhere else, the path may be to your project .zig-cache or to a global zig cache.

You can now debug that object!

lldb /path-to-zig-cache/.zig-cache/o/ead8187be37f01c571e970ede773ed12/test

You can now set breakpoints as mentioned above and then run the process.

(lldb) breakpoint set -n cumulative.cumulative_sum
(lldb) r

If you want to see the contents of a slice, you can pass slice.len into the parray command. For example, in the cumulative_sum() function above, once we have set a breakpoint

(lldb) parray `slice.len` slice.ptr

Explicit @breakpoint()

If you want to explicitly add in a breakpoint in the code, just use @breakpoint() and then run the test. The test should immediately crash and then you can load the object with lldb and just enter r to launch the process. You should stop at every @breakpoint() that you set.

const std = @import("std");

fn cumulative_sum(slice: []u16) void {
    const l = slice.len - 1;
    // now lldb will stop at this breakpoint without having to set it in the lldb console
    @breakpoint();
    for (slice[0..][0..l], slice[1..][0..l]) |*left, *right| {
        @breakpoint();
        right.* = left.*;
    }
}

test cumulative_sum {
    var slice = [_]u16{ 10, 20, 30, 40 };
    cumulative_sum(&slice);
    try std.testing.expectEqualSlices(u16, &[_]u16{ 10, 30, 60, 100 }, &slice);
}
7 Likes

Is there a way to use lldb when running your tests with zig build test? The tutorial and resources I could find just talk about running lldb with executables.

Yes, there are ways to debug tests.

zig build test actually produces an executable and then runs the executable.

Calling addTest, without calling addRunArtifact the test executable is produced but it is not run.

You can run lldb using the addSystemCommand:

    const unit_tests = b.addTest({
        // TODO add here the test options
    });

    const lldb = b.addSystemCommand(&.{
        "lldb",
        // add lldb flags before --
        "--",
    });
    // appends the unit_tests executable path to the lldb command line
    lldb.addArtifactArg(unit_tests);
    // lldb.addArg can add arguments after the executable path

    const lldb_step = b.step("debug", "run the tests under lldb");
    lldb_step.dependOn(&lldb.step);
6 Likes

That’s very cool, thanks a lot!

Please don’t quote the entire previous entry. That makes it unreadable. Just press the heart if you like it.

Handy tip when trying to debug tests: if the test binary crashes it will print the command that you could paste into a debugger. And one way to make it crash is to use @breakpoint() somewhere. Then you kill 2 birds with 1 stone because the debugger will stop at your breakpoint.

3 Likes