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.

39 Likes

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

2 Likes

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

4 Likes

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.

15 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);
}
9 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);
11 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.

11 Likes

It might be necessary to add .use_llvm = true to const exe = b.addExecutable to see symbols when using a newer Zig version.

Yes, only when building for x86_64 Linux atm, but that will change in the future.

There is also the LLB fork that works with the custom backends, I think it has a better experience, but you need to build from source.

Normal LLB should support zig in the future, atm zig pretends to be CPP when using LLVM. In addition, the custom backends are using experimental debug information that isn’t supported yet either, but that will also change.

1 Like

10 posts were split to a new topic: How to debug Zig with LLDB on Windows

A post was merged into an existing topic: How to debug Zig with LLDB on Windows

I am not able to set breakpoint when used zig build like below on Ubuntu 22.04:

$ mkdir /tmp/hello
$ cd /tmp/hello
$ zig version
0.16.0-dev.2193+fc517bd01
$ zig init
$ cd /tmp/aaa
$ zig build --build-file ../hello/build.zig --global-cache-dir /tmp/.cache --cache-dir .cache -p . -j4
$ file bin/hello
bin/hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped
$ lldb -v
lldb version 21.1.8 (https://github.com/llvm/llvm-project revision 2078da43e25a4623cab2d0d60decddf709aaea28)
$ lldb bin/hello
(lldb) target create "bin/hello"
Current executable set to '/tmp/aaa/bin/hello' (x86_64).
(lldb) b main
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
(lldb) q
$ bin/hello 
All your codebase are belong to us.
info: arg: bin/hello
Run `zig build test` to run the tests.

Maybe my out-of-tree build usage is incorrect?

Update: maybe the lldb I am using is weird as it doesn’t work even for in-tree build:

`$ cd /tmp/aaa`
`$ zig init`
`$ zig build β€”build-file ./build.zig –global-cache-dir /tmp/.cache –cache-dir .cache -p . -j4`
`$ file bin/aaa`
`bin/aaa: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped`
`$ lldb bin/aaa`
`lldb) target create β€œbin/aaa”`
`Current executable set to β€˜/tmp/aaa/bin/aaa’ (x86_64).`
`(lldb) b main`
`Breakpoint 1: no locations (pending).`
`WARNING:  Unable to resolve breakpoint to any actual locations.`
`(lldb) q`

Fortunately the stock gdb (Ubuntu 12.1-0ubuntu1~22.04.2) 12.1 seems working so I can use it for now.