Debugging with lldb on Windows?

Has anyone managed to get lldb debugging to work with symbols on Windows 10?

I’m trying to debug with lldb using a fairly standard build.zig based build of my game project, but somehow lldb is unable to find symbols, even if the .pdb file for my executable exists and Visual Studio 2022 is able to debug this just fine.

When trying to get a backtrace or set a breakpoint, I get:

$ lldb --version
lldb version 19.1.6

$ set LLDB_USE_NATIVE_PDB_READER=1

$ lldb zig-out\bin\zig2d.exe
(lldb) b main
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.

I think I’m using lldb more or less in the intended way and feels like this is a platform problem.

Has anyone managed to get this to work?

Debugging in Visual Studio 2022 is not too bad, it’s just that I’d prefer a command line tool as it takes forever for VS2022 to start. :slight_smile:

As an aside: getting lldb to even start on Windows is a bit of a struggle as it seems to depend on Python 3.10 being installed. I happen to have multiple Python versions installed, just not 3.10, and installing Python 3.10 just for lldb can mess some other things up then. But this can be fixed by dropping a mini Python install under the LLVM bin directory.

MSYS2’s lldb works just fine with Zig binaries

CLANG64 ~/zig-test
$ lldb ./zig-out/bin/zig-test.exe
(lldb) target create "./zig-out/bin/zig-test.exe"
(lldb) Current executable set to 'C:\msys64\home\user\zig-test\zig-out\bin\zig-test.exe' (x86_64).
(lldb) b main
Breakpoint 1: where = zig-test.exe`main + 26 at main.zig:5, address = 0x000000000040163a
(lldb) run
(lldb) Process 9996 launched: 'C:\msys64\home\user\zig-test\zig-out\bin\zig-test.exe' (x86_64)
Process 9996 stopped
* thread #1, stop reason = breakpoint 1.1
    frame #0: 0x000000000034163a zig-test.exe`main(stdout_file=(context = fs.File @ 0x0000007a00ffda
98)) at main.zig:5
   2
   3    pub fn main() !void {
   4        // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`)
-> 5        std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
   6
   7        // stdout is for the actual output of your application, for example if you
   8        // are implementing gzip, then only the compressed bytes should be sent to
(lldb)

If you have worked with Cygwin, you should feel right at home. MSYS2 can be tricky because there are several environments available, but you should use the CLANG64 one.

I have used Cygwin but nowadays avoid it. I’ll try the MSYS2 version. The original post experiments were done with an official LLVM release from their website.

Yeah I get it, just pointed that lldb MSYS2 ships works out of box so it is possible to get it working. I use MSYS2 while developing on windows because their packaging is just good. The python issue you mention is not a problem when using msys as python would be installed as a dependency and you don’t need to fiddle with the lldb installation directory or pollute the system path and possibly get weird DLL clashes.

1 Like

Hmm, that didn’t seem to improve the situation. :frowning:

Which lldb version do you have? I have

$ /mingw64/bin/lldb.exe --version
lldb version 19.1.4

Here’s what happens with msys2 lldb:

# (paths are edited to change \ -> /)
$ cd ~/dev/zig2d
$ /mingw64/bin/lldb.exe zig-out/bin/zig2d.exe
(lldb) target create "zig-out/bin/zig2d.exe"
Current executable set to 'C:/users/janne/dev/zig2d/zig-out/bin/zig2d.exe' (x86_64).
(lldb) b main
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
(lldb) r
(lldb) Process 2280 launched: 'C:/users/janne/dev/zig2d/zig-out/bin/zig2d.exe' (x86_64)
Process 2280 exited with status = 0 (0x00000000)

Yeah, this was a nice surprise! I agree that this packaging is much better than what LLVM official releases offer. I wonder why they have to depend on Python by default?

OK, looks like LLDB works on a smaller project. I created a new exe with “zig init” and this works:

$ /mingw64/bin/lldb.exe zig-out/bin/test.exe
(lldb) target create "zig-out/bin/test.exe"
(rrent executable set to 'C:/users/janne/dev/test/zig-out/bin/test.exe' (x86_64).
(lldb) (lldb) b main
Breakpoint 1: where = test.exe`main + 26 at main.zig:7, address = 0x0000000140001a0a
(lldb) r
(lldb) Process 6704 launched: 'C:/users/janne/dev/test/zig-out/bin/test.exe' (x86_64)
Process 6704 stopped
* thread #1, stop reason = breakpoint 1.1
    frame #0: 0x00007ff732c21a0a test.exe`main(stdout_file=(context = fs.File @ 0x000000afd0bfdc58)) at main.zig:7
   4
   5    pub fn main() !void {
   6        // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`)
-> 7        std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
   8
   9        // stdout is for the actual output of your application, for example if you
   10       // are implementing gzip, then only the compressed bytes should be sent to
(lldb) c
Process 6704 resuming
Process 6704 exited with status = 0 (0x00000000)
(lldb)

That’s how my game project’s build.zig was originally setup but it’s a more complicated now as it has a bunch of dependencies built into it.

If you have worked with Cygwin, you should feel right at home. MSYS2 can be tricky because there are several environments available, but you should use the CLANG64 one

I had missed this part. I now tried the following too but alas it also doesn’t seem to load symbols.

$ pacman -S mingw-w64-clang-x86_64-lldb
$ /clang64/bin/lldb.exe zig-out/bin/zig2d.exe
(lldb) target create "zig-out/bin/zig2d.exe"
(rrent executable set to 'C:/users/janne/dev/zig2d/zig-out/bin/zig2d.exe' (x86_64).
(lldb) b main
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
(lldb) r
(lldb) Process 27580 launched: 'C:/users/janne/dev/zig2d/zig-out/bin/zig2d.exe' (x86_64)
Process 27580 exited with status = 0 (0x00000000)

Mine is 19.1.6 but I according to your last message that doesn’t matter
I guess official LLVM also works on smaller example? Can you verify that .pdb file with the same basename exists in zig-out/bin directory?

Yes, this exists:

$ ls -la zig-out/bin
total 15884
drwxr-xr-x 1 janne None       0 Jan  6 13:39 .
drwxr-xr-x 1 janne None       0 Jan  4 01:17 ..
drwxr-xr-x 1 janne None       0 Jan  4 01:24 .vs
-rwxr-xr-x 1 janne None 7479808 Jan  6 13:34 zig2d.exe
-rw-r--r-- 1 janne None 8781824 Jan  6 13:34 zig2d.pdb

And I think the pdb file is working as I was able to debug the zig2d executable on Visual Studio 2022.

I tried to debug my small zig init test.exe with the official LLVM lldb and that didn’t work either. But this easier case works with the MSYS2 packaged LLDB. I’d be ok using just the MSYS2 package if I can get it to work.

Can you try to load pdb explicitly? (lldb) target symbols add zig-out/bin/zig2d.pdb. If this does not work, I think an example to reproduce is needed for further investigation

Hey, this works!! Thank you so much for the help! I wonder why it’s unable to pick up the pdb file without explicitly setting it? Maybe it gets confused by \ and / in the filenames?

(Note: \ has been converted to / in the below snippet as the Ziggit forum software misinterprets the backslashes)

$ lldb zig-out/bin/zig2d.exe
(lldb) target create "zig-out/bin/zig2d.exe"
(rrent executable set to 'C:/users/janne/dev/zig2d/zig-out/bin/zig2d.exe' (x86_64).
(lldb) target symbols add zig-out/bin/zig2d.pdb
symbol file 'C:/users/janne/dev/zig2d/zig-out/bin/zig2d.pdb' has been added to 'C:/users/janne/dev/zig2d/zig-out/bin/zig2d.exe'
(lldb) b main
Breakpoint 1: where = zig2d.exe`main + 71 at start.zig:620, address = 0x00000001400077a7
(lldb) r
(lldb) Process 19464 launched: 'C:/users/janne/dev/zig2d/zig-out/bin/zig2d.exe' (x86_64)
Process 19464 stopped
* thread #1, stop reason = breakpoint 1.1
    frame #0: 0x00007ff7fb3677a7 zig2d.exe`main(c_argc=1, c_argv=0x000001315ae23250, c_envp=0x000001315ae20100) at start.zig:620
   617  }
   618
   619  fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) callconv(.c) c_int {
-> 620      var env_count: usize = 0;
   621      while (c_envp[env_count] != null) : (env_count += 1) {}
   622      const envp = @as([*][*:0]u8, @ptrCast(c_envp))[0..env_count];
1 Like

I have no clues :grin: But it’s unlikely due to path as it works fine with different target, I also tried renaming exe to zig2d.exe in case if the name is the problem, but it succeed. I would like to investigate this when I have some free time, is there any particular step/dependency that breaks debug?

Summary of a working solution for LLDB debugging on Windows 10 for anyone who might stumble upon this same problem.

Problem: Debugging a more complex project with multiple native dependencies (sokol-gfx, Ziglua, etc) with LLDB on Windows 10 did not work as LLDB was unable to load symbols.

This didn’t work with LLVM’s official LLDB binary nor with MSYS2 packaged LLDB in the CLANG64 environment.

Solution:

  1. MSYS2: Using either LLVM official LLDB or MSYS2 LLDB, load the binary as usual with lldb zig-out/bin/zig2d.exe (after zig build).
  2. LLVM official:
  • set LLDB_USE_NATIVE_PDB_READER=1 (see below for details)
  • Tell LLDB where the symbols are: target symbols add zig-out/bin/zig2d.pdb

Another problem with LLDB on Windows: LLVM official binaries depend on Python 3.10. If you have any other Python version installed, LLDB simply won’t start and issues no error or warning. This is due to missing Python DLLs. The solution is to either install Python 3.10 into your system or extract a mini Python 3.10 installation into the LLVM bin directory as in explained here. In my case I had to do the latter, as I use Python 3.13 for my Python development.

The MSYS2 package for LLDB doesn’t have this problem as it’s packaged correctly with Python.

Without LLDB_USE_NATIVE_PDB_READER, I get the below error when using official LLVM lldb build:

target symbols add zig-out\bin\zig2d.pdb
error: symbol file 'C:/Users/janne/dev/zig2d/zig-out/bin/zig2d.pdb' does not match any existing module
3 Likes