Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug breakpoint is hit when it shouldn't be #19443

Open
lawrence-laz opened this issue Mar 26, 2024 · 6 comments · May be fixed by #20543
Open

Debug breakpoint is hit when it shouldn't be #19443

lawrence-laz opened this issue Mar 26, 2024 · 6 comments · May be fixed by #20543
Labels
backend-llvm The LLVM backend outputs an LLVM IR Module. bug Observed behavior contradicts documented or intended behavior debug-info Debug information of binary generated by Zig is not as expected.
Milestone

Comments

@lawrence-laz
Copy link

lawrence-laz commented Mar 26, 2024

Zig Version

0.12.0-dev.2809+f3bd17772
0.12.0-dev.3439+31a7f22b8

Steps to Reproduce and Observed Behavior

Create a simple repro by running:

mkdir repro && cd repro
zig init
cat <<EOT > src/main.zig
const std = @import("std");

pub fn main() !void {
    var i: usize = 0;
    while (i < 100) : (i += 1) {
        if (i == 90) {
            std.debug.print("It's 90!", .{});
        } else if (i == 50) {
            std.debug.print("It's 50!", .{});
        }
    }
}
EOT
zig build
cd zig-out/bin/
lldb repro

Then in lldb console:

(lldb) b main.zig:9
Breakpoint 1: where = repro`main.main + 188 at main.zig:9:28, address = 0x0000000100000824
(lldb) run
Process 4864 launched: '~/zig/repro/zig-out/bin/repro' (arm64)
Process 4864 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100000824 repro`main.main at main.zig:9:28
   6            if (i == 90) {
   7                std.debug.print("It's 90!", .{});
   8            } else if (i == 50) {
-> 9                std.debug.print("It's 50!", .{});
   10           }
   11       }
   12   }
Target 0: (repro) stopped.
(lldb) var i
(unsigned long) i = 0
(lldb) continue
Process 4864 resuming
Process 4864 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100000824 repro`main.main at main.zig:9:28
   6            if (i == 90) {
   7                std.debug.print("It's 90!", .{});
   8            } else if (i == 50) {
-> 9                std.debug.print("It's 50!", .{});
   10           }
   11       }
   12   }
Target 0: (repro) stopped.
(lldb) var i
(unsigned long) i = 1

Notice how the breakpoint gets hit every cycle, even though the i == 50 should happen only once. Reading the i variable it prints 0, 1, etc.

Expected Behavior

Breakpoint should hit only once when i == 50.

@lawrence-laz lawrence-laz added the bug Observed behavior contradicts documented or intended behavior label Mar 26, 2024
@mlugg
Copy link
Member

mlugg commented Mar 26, 2024

I think this is an manifestation of the slightly weird issue that when a block is skipped, debuggers will indicate us first jumping to the block's last line, and then we appear to enter the following code.

@lawrence-laz
Copy link
Author

lawrence-laz commented Mar 26, 2024

You might be onto something.

If the code is changed to:

const std = @import("std");

pub fn main() !void {
    var i: usize = 0;
    while (i < 100) : (i += 1) {
        if (i == 90) {
            std.debug.print("It's 90!", .{});
        } else if (i == 50) {
            const foo: usize = i + 10;
            var bar: usize = foo + 20;
            std.debug.print("It's inner {d}!", .{bar});
            bar += 1;
            std.debug.print("It's inner plus one {d}!", .{bar});
            std.debug.print("It's 50!", .{});
        }
    }
}

And breakpoint is in the middle of the block, then it behaves as expected:

(lldb) b main.zig:11
Breakpoint 2: where = repro`main.main + 320 at main.zig:11:28, address = 0x00000001000008a8
(lldb) run
Process 5813 launched: '~/zig/repro/zig-out/bin/repro' (arm64)
Process 5813 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
    frame #0: 0x00000001000008a8 repro`main.main at main.zig:11:28
   8            } else if (i == 50) {
   9                const foo: usize = i + 10;
   10               var bar: usize = foo + 20;
-> 11               std.debug.print("It's inner {d}!", .{bar});
   12               bar += 1;
   13               std.debug.print("It's inner plus one {d}!", .{bar});
   14               std.debug.print("It's 50!", .{});
Target 0: (repro) stopped.
(lldb) var i
(unsigned long) i = 50

Would this mean zig cannot do anything and it's the issue of a debugger?
Maybe zig could output some noop debug info at the end of the block, so it wouldn't overlap with the last line in source code?

@nektro
Copy link
Contributor

nektro commented Mar 26, 2024

does this reproduce on 0.12.0-dev.3439+31a7f22b8 ?

@lawrence-laz
Copy link
Author

lawrence-laz commented Mar 26, 2024

Tried reproducing same with c++:

mkdir repro-cpp
cd repro-cpp
cat <<EOF > main.cpp
#include <iostream>

int main()
{
    int i = 0;
    while (i < 100)
    {
        if (i == 90) {
            std::cout << "It's 90!\n";
        }
        else if (i == 50) {
            std::cout << "It's 50!\n";
        }
        i += 1;
    }
}
EOF
g++ -g main.cpp
lldb a.out

and then in debugger

(lldb) b main.cpp:12
Breakpoint 1: where = a.out`main + 124 at main.cpp:12:23, address = 0x0000000100003128
(lldb) run
Process 6746 launched: '~/zig/repro/repro-cpp/a.out' (arm64)
Process 6746 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003128 a.out`main at main.cpp:12:23
   9                std::cout << "It's 90!\n";
   10           }
   11           else if (i == 50) {
-> 12               std::cout << "It's 50!\n";
   13           }
   14           i += 1;
   15       }
Target 0: (a.out) stopped.
(lldb) var i
(int) i = 50

seems to work fine, so it's probably not a debugger issue?

@lawrence-laz
Copy link
Author

lawrence-laz commented Mar 26, 2024

does this reproduce on 0.12.0-dev.3439+31a7f22b8 ?

@nektro tested on 0.12.0-dev.3439+31a7f22b8, same issue

@mlugg
Copy link
Member

mlugg commented Mar 27, 2024

Would this mean Zig cannot do anything and it's the issue of a debugger?

No, this is still a Zig bug. I might try and take a look later today.

@andrewrk andrewrk added this to the 0.14.0 milestone Mar 28, 2024
@Vexu Vexu added the debug-info Debug information of binary generated by Zig is not as expected. label Jun 10, 2024
tau-dev added a commit to tau-dev/zig that referenced this issue Jul 8, 2024
Reset the line number to its previous state after genBodyDebugScope, so
that instructions between its end and the next dbg_stmt do not wander
inside the block. This core fix is very simple, but surfaces a secondary
issue: the LLVM codegecn generates blocks in a different order than
Clang would for the same CFG in C, which ultimately results in GDB
skipping the loop header altogether. So we need to reorder some basic
blocks too.
tau-dev added a commit to tau-dev/zig that referenced this issue Jul 8, 2024
Reset the line number to its previous state after genBodyDebugScope, so
that instructions between its end and the next dbg_stmt do not wander
inside the block. This core fix is very simple, but surfaces a secondary
issue: the LLVM codegen generates blocks in a different order than Clang
would for the same CFG in C, which ultimately results in GDB skipping
the loop header altogether. So we need to reorder some basic blocks too.
tau-dev added a commit to tau-dev/zig that referenced this issue Jul 8, 2024
Reset the line number to its previous state after genBodyDebugScope, so
that instructions between its end and the next dbg_stmt do not wander
inside the block. This core fix is very simple, but surfaces a secondary
issue: the LLVM codegen generates blocks in a different order than Clang
would for the same CFG in C, which ultimately results in GDB skipping
the loop header altogether. So we need to reorder some basic blocks too.
tau-dev added a commit to tau-dev/zig that referenced this issue Jul 8, 2024
Reset the line number to its previous state after genBodyDebugScope, so
that instructions between its end and the next dbg_stmt do not wander
inside the block. This core fix is very simple, but surfaces a secondary
issue: the LLVM codegen generates blocks in a different order than Clang
would for the same CFG in C, which ultimately results in GDB skipping
the loop header altogether. So we need to reorder some basic blocks too.
tau-dev added a commit to tau-dev/zig that referenced this issue Jul 21, 2024
Reset the line number to its previous state after genBodyDebugScope, so
that instructions between its end and the next dbg_stmt do not wander
inside the block. This core fix is very simple, but surfaces a secondary
issue: the LLVM codegen generates blocks in a different order than Clang
would for the same CFG in C, which ultimately results in GDB skipping
the loop header altogether. So we need to reorder some basic blocks too.
tau-dev added a commit to tau-dev/zig that referenced this issue Jul 21, 2024
Reset the line number to its previous state after genBodyDebugScope, so
that instructions between its end and the next dbg_stmt do not wander
inside the block. This core fix is very simple, but surfaces a secondary
issue: the LLVM codegen generates blocks in a different order than Clang
would for the same CFG in C, which ultimately results in GDB skipping
the loop header altogether. So we need to reorder some basic blocks too.
@andrewrk andrewrk added the backend-llvm The LLVM backend outputs an LLVM IR Module. label Aug 8, 2024
@andrewrk andrewrk modified the milestones: 0.15.0, 0.14.0 Aug 8, 2024
tau-dev added a commit to tau-dev/zig that referenced this issue Sep 4, 2024
Reset the line number to its previous state after genBodyDebugScope, so
that instructions between its end and the next dbg_stmt do not wander
inside the block. This core fix is very simple, but surfaces a secondary
issue: the LLVM codegen generates blocks in a different order than Clang
would for the same CFG in C, which ultimately results in GDB skipping
the loop header altogether. So we need to reorder some basic blocks too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend-llvm The LLVM backend outputs an LLVM IR Module. bug Observed behavior contradicts documented or intended behavior debug-info Debug information of binary generated by Zig is not as expected.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants