Commit c127c06fd7

Jacob Young <jacobly0@users.noreply.github.com>
2024-09-12 18:02:21
Dwarf: implement and test lexical blocks
1 parent d354daf
Changed files (6)
src/arch/x86_64/CodeGen.zig
@@ -2180,6 +2180,12 @@ fn checkInvariantsAfterAirInst(self: *Self, inst: Air.Inst.Index, old_air_bookke
     }
 }
 
+fn genBodyBlock(self: *Self, body: []const Air.Inst.Index) InnerError!void {
+    try self.asmPseudo(.pseudo_dbg_enter_block_none);
+    try self.genBody(body);
+    try self.asmPseudo(.pseudo_dbg_leave_block_none);
+}
+
 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
     const pt = self.pt;
     const zcu = pt.zcu;
@@ -13184,7 +13190,7 @@ fn genTry(
     const state = try self.saveState();
 
     for (liveness_cond_br.else_deaths) |death| try self.processDeath(death);
-    try self.genBody(body);
+    try self.genBodyBlock(body);
     try self.restoreState(state, &.{}, .{
         .emit_instructions = false,
         .update_tracking = true,
@@ -13293,7 +13299,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
     const reloc = try self.genCondBrMir(cond_ty, cond);
 
     for (liveness_cond_br.then_deaths) |death| try self.processDeath(death);
-    try self.genBody(then_body);
+    try self.genBodyBlock(then_body);
     try self.restoreState(state, &.{}, .{
         .emit_instructions = false,
         .update_tracking = true,
@@ -13304,7 +13310,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
     self.performReloc(reloc);
 
     for (liveness_cond_br.else_deaths) |death| try self.processDeath(death);
-    try self.genBody(else_body);
+    try self.genBodyBlock(else_body);
     try self.restoreState(state, &.{}, .{
         .emit_instructions = false,
         .update_tracking = true,
@@ -13665,14 +13671,16 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
     });
     defer assert(self.loops.remove(inst));
 
-    try self.genBody(body);
+    try self.genBodyBlock(body);
     self.finishAirBookkeeping();
 }
 
 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
     const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
     const extra = self.air.extraData(Air.Block, ty_pl.payload);
+    try self.asmPseudo(.pseudo_dbg_enter_block_none);
     try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
+    try self.asmPseudo(.pseudo_dbg_leave_block_none);
 }
 
 fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void {
@@ -13684,7 +13692,6 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !
     try self.blocks.putNoClobber(self.gpa, inst, .{ .state = self.initRetroactiveState() });
     const liveness = self.liveness.getBlock(inst);
 
-    // TODO emit debug info lexical block
     try self.genBody(body);
 
     var block_data = self.blocks.fetchRemove(inst).?;
@@ -13796,7 +13803,7 @@ fn lowerSwitchBr(self: *Self, inst: Air.Inst.Index, switch_br: Air.UnwrappedSwit
 
         // Relocate all success cases to the body we're about to generate.
         for (relocs) |reloc| self.performReloc(reloc);
-        try self.genBody(case.body);
+        try self.genBodyBlock(case.body);
         try self.restoreState(state, &.{}, .{
             .emit_instructions = false,
             .update_tracking = true,
@@ -13814,7 +13821,7 @@ fn lowerSwitchBr(self: *Self, inst: Air.Inst.Index, switch_br: Air.UnwrappedSwit
         const else_deaths = liveness.deaths.len - 1;
         for (liveness.deaths[else_deaths]) |operand| try self.processDeath(operand);
 
-        try self.genBody(else_body);
+        try self.genBodyBlock(else_body);
         try self.restoreState(state, &.{}, .{
             .emit_instructions = false,
             .update_tracking = true,
src/arch/x86_64/Emit.zig
@@ -287,6 +287,30 @@ pub fn emitMir(emit: *Emit) Error!void {
                             .none => {},
                         }
                     },
+                    .pseudo_dbg_enter_block_none => {
+                        switch (emit.debug_output) {
+                            .dwarf => |dw| {
+                                log.debug("mirDbgEnterBlock (line={d}, col={d})", .{
+                                    emit.prev_di_line, emit.prev_di_column,
+                                });
+                                try dw.enterBlock(emit.code.items.len);
+                            },
+                            .plan9 => {},
+                            .none => {},
+                        }
+                    },
+                    .pseudo_dbg_leave_block_none => {
+                        switch (emit.debug_output) {
+                            .dwarf => |dw| {
+                                log.debug("mirDbgLeaveBlock (line={d}, col={d})", .{
+                                    emit.prev_di_line, emit.prev_di_column,
+                                });
+                                try dw.leaveBlock(emit.code.items.len);
+                            },
+                            .plan9 => {},
+                            .none => {},
+                        }
+                    },
                     .pseudo_dbg_enter_inline_func => {
                         switch (emit.debug_output) {
                             .dwarf => |dw| {
src/arch/x86_64/Lower.zig
@@ -312,6 +312,8 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
             .pseudo_dbg_prologue_end_none,
             .pseudo_dbg_line_line_column,
             .pseudo_dbg_epilogue_begin_none,
+            .pseudo_dbg_enter_block_none,
+            .pseudo_dbg_leave_block_none,
             .pseudo_dbg_enter_inline_func,
             .pseudo_dbg_leave_inline_func,
             .pseudo_dbg_local_a,
src/arch/x86_64/Mir.zig
@@ -935,6 +935,10 @@ pub const Inst = struct {
         pseudo_dbg_line_line_column,
         /// Start of epilogue
         pseudo_dbg_epilogue_begin_none,
+        /// Start of lexical block
+        pseudo_dbg_enter_block_none,
+        /// End of lexical block
+        pseudo_dbg_leave_block_none,
         /// Start of inline function
         pseudo_dbg_enter_inline_func,
         /// End of inline function
src/link/Dwarf.zig
@@ -1374,10 +1374,11 @@ pub const WipNav = struct {
     any_children: bool,
     func: InternPool.Index,
     func_sym_index: u32,
-    func_high_reloc: u32,
-    inlined_funcs: std.ArrayListUnmanaged(struct {
+    func_high_pc: u32,
+    blocks: std.ArrayListUnmanaged(struct {
         abbrev_code: u32,
-        high_reloc: u32,
+        low_pc_off: u64,
+        high_pc: u32,
     }),
     cfi: struct {
         loc: u32,
@@ -1391,7 +1392,7 @@ pub const WipNav = struct {
 
     pub fn deinit(wip_nav: *WipNav) void {
         const gpa = wip_nav.dwarf.gpa;
-        if (wip_nav.func != .none) wip_nav.inlined_funcs.deinit(gpa);
+        if (wip_nav.func != .none) wip_nav.blocks.deinit(gpa);
         wip_nav.debug_frame.deinit(gpa);
         wip_nav.debug_info.deinit(gpa);
         wip_nav.debug_line.deinit(gpa);
@@ -1486,49 +1487,72 @@ pub const WipNav = struct {
         try dlw.writeByte(DW.LNS.set_epilogue_begin);
     }
 
-    pub fn enterInlineFunc(wip_nav: *WipNav, func: InternPool.Index, code_off: u64, line: u32, column: u32) UpdateError!void {
+    pub fn enterBlock(wip_nav: *WipNav, code_off: u64) UpdateError!void {
+        const dwarf = wip_nav.dwarf;
+        const diw = wip_nav.debug_info.writer(dwarf.gpa);
+        const block = try wip_nav.blocks.addOne(dwarf.gpa);
+
+        block.abbrev_code = @intCast(wip_nav.debug_info.items.len);
+        try wip_nav.abbrevCode(.block);
+        block.low_pc_off = code_off;
+        try wip_nav.infoAddrSym(wip_nav.func_sym_index, code_off);
+        block.high_pc = @intCast(wip_nav.debug_info.items.len);
+        try diw.writeInt(u32, 0, dwarf.endian);
+        wip_nav.any_children = false;
+    }
+
+    pub fn leaveBlock(wip_nav: *WipNav, code_off: u64) UpdateError!void {
+        const block_bytes = comptime uleb128Bytes(@intFromEnum(AbbrevCode.block));
+        const block = wip_nav.blocks.pop();
+        if (wip_nav.any_children)
+            try uleb128(wip_nav.debug_info.writer(wip_nav.dwarf.gpa), @intFromEnum(AbbrevCode.null))
+        else
+            std.leb.writeUnsignedFixed(
+                block_bytes,
+                wip_nav.debug_info.items[block.abbrev_code..][0..block_bytes],
+                try wip_nav.dwarf.refAbbrevCode(.empty_block),
+            );
+        std.mem.writeInt(u32, wip_nav.debug_info.items[block.high_pc..][0..4], @intCast(code_off - block.low_pc_off), wip_nav.dwarf.endian);
+        wip_nav.any_children = true;
+    }
+
+    pub fn enterInlineFunc(
+        wip_nav: *WipNav,
+        func: InternPool.Index,
+        code_off: u64,
+        line: u32,
+        column: u32,
+    ) UpdateError!void {
         const dwarf = wip_nav.dwarf;
         const zcu = wip_nav.pt.zcu;
         const diw = wip_nav.debug_info.writer(dwarf.gpa);
-        const inlined_func = try wip_nav.inlined_funcs.addOne(dwarf.gpa);
+        const block = try wip_nav.blocks.addOne(dwarf.gpa);
 
-        inlined_func.abbrev_code = @intCast(wip_nav.debug_info.items.len);
+        block.abbrev_code = @intCast(wip_nav.debug_info.items.len);
         try wip_nav.abbrevCode(.inlined_func);
         try wip_nav.refNav(zcu.funcInfo(func).owner_nav);
         try uleb128(diw, zcu.navSrcLine(zcu.funcInfo(wip_nav.func).owner_nav) + line + 1);
         try uleb128(diw, column + 1);
-        const external_relocs = &dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs;
-        try external_relocs.ensureUnusedCapacity(dwarf.gpa, 2);
-        external_relocs.appendAssumeCapacity(.{
-            .source_off = @intCast(wip_nav.debug_info.items.len),
-            .target_sym = wip_nav.func_sym_index,
-            .target_off = code_off,
-        });
-        try diw.writeByteNTimes(0, @intFromEnum(dwarf.address_size));
-        inlined_func.high_reloc = @intCast(external_relocs.items.len);
-        external_relocs.appendAssumeCapacity(.{
-            .source_off = @intCast(wip_nav.debug_info.items.len),
-            .target_sym = wip_nav.func_sym_index,
-            .target_off = undefined,
-        });
-        try diw.writeByteNTimes(0, @intFromEnum(dwarf.address_size));
+        block.low_pc_off = code_off;
+        try wip_nav.infoAddrSym(wip_nav.func_sym_index, code_off);
+        block.high_pc = @intCast(wip_nav.debug_info.items.len);
+        try diw.writeInt(u32, 0, dwarf.endian);
         try wip_nav.setInlineFunc(func);
         wip_nav.any_children = false;
     }
 
     pub fn leaveInlineFunc(wip_nav: *WipNav, func: InternPool.Index, code_off: u64) UpdateError!void {
         const inlined_func_bytes = comptime uleb128Bytes(@intFromEnum(AbbrevCode.inlined_func));
-        const inlined_func = wip_nav.inlined_funcs.pop();
-        const external_relocs = &wip_nav.dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs;
-        external_relocs.items[inlined_func.high_reloc].target_off = code_off;
+        const block = wip_nav.blocks.pop();
         if (wip_nav.any_children)
             try uleb128(wip_nav.debug_info.writer(wip_nav.dwarf.gpa), @intFromEnum(AbbrevCode.null))
         else
             std.leb.writeUnsignedFixed(
                 inlined_func_bytes,
-                wip_nav.debug_info.items[inlined_func.abbrev_code..][0..inlined_func_bytes],
+                wip_nav.debug_info.items[block.abbrev_code..][0..inlined_func_bytes],
                 try wip_nav.dwarf.refAbbrevCode(.empty_inlined_func),
             );
+        std.mem.writeInt(u32, wip_nav.debug_info.items[block.high_pc..][0..4], @intCast(code_off - block.low_pc_off), wip_nav.dwarf.endian);
         try wip_nav.setInlineFunc(func);
         wip_nav.any_children = true;
     }
@@ -1664,17 +1688,18 @@ pub const WipNav = struct {
                 return ctx.wip_nav.dwarf.endian;
             }
             fn addrSym(ctx: @This(), sym_index: u32) UpdateError!void {
-                try ctx.wip_nav.infoAddrSym(sym_index);
+                try ctx.wip_nav.infoAddrSym(sym_index, 0);
             }
         } = .{ .wip_nav = wip_nav };
         try uleb128(adapter.writer(), counter.stream.bytes_written);
         try loc.write(adapter);
     }
 
-    fn infoAddrSym(wip_nav: *WipNav, sym_index: u32) UpdateError!void {
+    fn infoAddrSym(wip_nav: *WipNav, sym_index: u32, sym_off: u64) UpdateError!void {
         try wip_nav.infoExternalReloc(.{
             .source_off = @intCast(wip_nav.debug_info.items.len),
             .target_sym = sym_index,
+            .target_off = sym_off,
         });
         try wip_nav.debug_info.appendNTimes(wip_nav.dwarf.gpa, 0, @intFromEnum(wip_nav.dwarf.address_size));
     }
@@ -1695,17 +1720,18 @@ pub const WipNav = struct {
                 return ctx.wip_nav.dwarf.endian;
             }
             fn addrSym(ctx: @This(), sym_index: u32) UpdateError!void {
-                try ctx.wip_nav.frameAddrSym(sym_index);
+                try ctx.wip_nav.frameAddrSym(sym_index, 0);
             }
         } = .{ .wip_nav = wip_nav };
         try uleb128(adapter.writer(), counter.stream.bytes_written);
         try loc.write(adapter);
     }
 
-    fn frameAddrSym(wip_nav: *WipNav, sym_index: u32) UpdateError!void {
+    fn frameAddrSym(wip_nav: *WipNav, sym_index: u32, sym_off: u64) UpdateError!void {
         try wip_nav.frameExternalReloc(.{
             .source_off = @intCast(wip_nav.debug_frame.items.len),
             .target_sym = sym_index,
+            .target_off = sym_off,
         });
         try wip_nav.debug_frame.appendNTimes(wip_nav.dwarf.gpa, 0, @intFromEnum(wip_nav.dwarf.address_size));
     }
@@ -2150,8 +2176,8 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
         .any_children = false,
         .func = .none,
         .func_sym_index = undefined,
-        .func_high_reloc = undefined,
-        .inlined_funcs = undefined,
+        .func_high_pc = undefined,
+        .blocks = undefined,
         .cfi = undefined,
         .debug_frame = .{},
         .debug_info = .{},
@@ -2294,7 +2320,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
             const func_type = ip.indexToKey(func.ty).func_type;
             wip_nav.func = nav_val.toIntern();
             wip_nav.func_sym_index = sym_index;
-            wip_nav.inlined_funcs = .{};
+            wip_nav.blocks = .{};
             if (dwarf.debug_frame.header.format != .none) wip_nav.cfi = .{
                 .loc = 0,
                 .cfa = dwarf.debug_frame.header.initial_instructions[0].def_cfa,
@@ -2319,7 +2345,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
                                 .source_off = @intCast(wip_nav.debug_frame.items.len),
                             });
                             try dfw.writeByteNTimes(0, dwarf.sectionOffsetBytes());
-                            try wip_nav.frameAddrSym(sym_index);
+                            try wip_nav.frameAddrSym(sym_index, 0);
                             try dfw.writeByteNTimes(undefined, @intFromEnum(dwarf.address_size));
                         },
                         .eh_frame => {
@@ -2346,20 +2372,9 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
             try wip_nav.strp(nav.name.toSlice(ip));
             try wip_nav.strp(nav.fqn.toSlice(ip));
             try wip_nav.refType(Type.fromInterned(func_type.return_type));
-            const external_relocs = &dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs;
-            try external_relocs.ensureUnusedCapacity(dwarf.gpa, 2);
-            external_relocs.appendAssumeCapacity(.{
-                .source_off = @intCast(wip_nav.debug_info.items.len),
-                .target_sym = sym_index,
-            });
-            try diw.writeByteNTimes(0, @intFromEnum(dwarf.address_size));
-            wip_nav.func_high_reloc = @intCast(external_relocs.items.len);
-            external_relocs.appendAssumeCapacity(.{
-                .source_off = @intCast(wip_nav.debug_info.items.len),
-                .target_sym = sym_index,
-                .target_off = undefined,
-            });
-            try diw.writeByteNTimes(0, @intFromEnum(dwarf.address_size));
+            try wip_nav.infoAddrSym(sym_index, 0);
+            wip_nav.func_high_pc = @intCast(wip_nav.debug_info.items.len);
+            try diw.writeInt(u32, 0, dwarf.endian);
             try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
                 target_info.defaultFunctionAlignment(file.mod.resolved_target.result).toByteUnits().?);
             try diw.writeByte(@intFromBool(false));
@@ -2466,8 +2481,7 @@ pub fn finishWipNav(
             },
         }
         {
-            const external_relocs = &dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs;
-            external_relocs.items[wip_nav.func_high_reloc].target_off = sym.size;
+            std.mem.writeInt(u32, wip_nav.debug_info.items[wip_nav.func_high_pc..][0..4], @intCast(sym.size), dwarf.endian);
             if (wip_nav.any_children) {
                 const diw = wip_nav.debug_info.writer(dwarf.gpa);
                 try uleb128(diw, @intFromEnum(AbbrevCode.null));
@@ -2562,8 +2576,8 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
         .any_children = false,
         .func = .none,
         .func_sym_index = undefined,
-        .func_high_reloc = undefined,
-        .inlined_funcs = undefined,
+        .func_high_pc = undefined,
+        .blocks = undefined,
         .cfi = undefined,
         .debug_frame = .{},
         .debug_info = .{},
@@ -3036,8 +3050,8 @@ fn updateType(
         .any_children = false,
         .func = .none,
         .func_sym_index = undefined,
-        .func_high_reloc = undefined,
-        .inlined_funcs = undefined,
+        .func_high_pc = undefined,
+        .blocks = undefined,
         .cfi = undefined,
         .debug_frame = .{},
         .debug_info = .{},
@@ -3480,8 +3494,8 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP
             .any_children = false,
             .func = .none,
             .func_sym_index = undefined,
-            .func_high_reloc = undefined,
-            .inlined_funcs = undefined,
+            .func_high_pc = undefined,
+            .blocks = undefined,
             .cfi = undefined,
             .debug_frame = .{},
             .debug_info = .{},
@@ -3553,8 +3567,8 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP
             .any_children = false,
             .func = .none,
             .func_sym_index = undefined,
-            .func_high_reloc = undefined,
-            .inlined_funcs = undefined,
+            .func_high_pc = undefined,
+            .blocks = undefined,
             .cfi = undefined,
             .debug_frame = .{},
             .debug_info = .{},
@@ -3756,8 +3770,8 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
             .any_children = false,
             .func = .none,
             .func_sym_index = undefined,
-            .func_high_reloc = undefined,
-            .inlined_funcs = undefined,
+            .func_high_pc = undefined,
+            .blocks = undefined,
             .cfi = undefined,
             .debug_frame = .{},
             .debug_info = .{},
@@ -4212,6 +4226,8 @@ const AbbrevCode = enum {
     empty_packed_struct_type,
     union_type,
     empty_union_type,
+    empty_block,
+    block,
     empty_inlined_func,
     inlined_func,
     local_arg,
@@ -4319,7 +4335,7 @@ const AbbrevCode = enum {
                 .{ .linkage_name, .strp },
                 .{ .type, .ref_addr },
                 .{ .low_pc, .addr },
-                .{ .high_pc, .addr },
+                .{ .high_pc, .data4 },
                 .{ .alignment, .udata },
                 .{ .external, .flag },
                 .{ .noreturn, .flag },
@@ -4331,7 +4347,7 @@ const AbbrevCode = enum {
                 .{ .linkage_name, .strp },
                 .{ .type, .ref_addr },
                 .{ .low_pc, .addr },
-                .{ .high_pc, .addr },
+                .{ .high_pc, .data4 },
                 .{ .alignment, .udata },
                 .{ .external, .flag },
                 .{ .noreturn, .flag },
@@ -4672,6 +4688,21 @@ const AbbrevCode = enum {
                 .{ .alignment, .udata },
             },
         },
+        .empty_block = .{
+            .tag = .lexical_block,
+            .attrs = &.{
+                .{ .low_pc, .addr },
+                .{ .high_pc, .data4 },
+            },
+        },
+        .block = .{
+            .tag = .lexical_block,
+            .children = true,
+            .attrs = &.{
+                .{ .low_pc, .addr },
+                .{ .high_pc, .data4 },
+            },
+        },
         .empty_inlined_func = .{
             .tag = .inlined_subroutine,
             .attrs = &.{
@@ -4679,7 +4710,7 @@ const AbbrevCode = enum {
                 .{ .call_line, .udata },
                 .{ .call_column, .udata },
                 .{ .low_pc, .addr },
-                .{ .high_pc, .addr },
+                .{ .high_pc, .data4 },
             },
         },
         .inlined_func = .{
@@ -4690,7 +4721,7 @@ const AbbrevCode = enum {
                 .{ .call_line, .udata },
                 .{ .call_column, .udata },
                 .{ .low_pc, .addr },
-                .{ .high_pc, .addr },
+                .{ .high_pc, .data4 },
             },
         },
         .local_arg = .{
test/src/Debugger.zig
@@ -721,6 +721,93 @@ pub fn addTestsForTarget(db: *Debugger, target: Target) void {
             \\1 breakpoints deleted; 0 breakpoint locations disabled.
         },
     );
+    db.addLldbTest(
+        "if_blocks",
+        target,
+        &.{
+            .{
+                .path = "if_blocks.zig",
+                .source =
+                \\pub fn main() void {
+                \\    for (0..2) |i| {
+                \\        if (i == 0) {
+                \\            var x: u32 = 123;
+                \\            _ = &x;
+                \\        } else {
+                \\            var x: f32 = 4.5;
+                \\            _ = &x;
+                \\        }
+                \\    }
+                \\}
+                \\
+                ,
+            },
+        },
+        \\breakpoint set --file if_blocks.zig --source-pattern-regexp '_ = &x;'
+        \\process launch
+        \\frame variable
+        \\process continue
+        \\frame variable
+        \\breakpoint delete --force 1
+    ,
+        &.{
+            \\(lldb) frame variable
+            \\(usize) i = 0
+            \\(u32) x = 123
+            \\(lldb) process continue
+            ,
+            \\(lldb) frame variable
+            \\(usize) i = 1
+            \\(f32) x = 4.5
+            \\(lldb) breakpoint delete --force 1
+            \\1 breakpoints deleted; 0 breakpoint locations disabled.
+        },
+    );
+    db.addLldbTest(
+        "switch_blocks",
+        target,
+        &.{
+            .{
+                .path = "switch_blocks.zig",
+                .source =
+                \\pub fn main() void {
+                \\    for (0..2) |i| {
+                \\        switch (i) {
+                \\            0 => {
+                \\                var x: u32 = 123;
+                \\                _ = &x;
+                \\            },
+                \\            else => {
+                \\                var x: f32 = 4.5;
+                \\                _ = &x;
+                \\            },
+                \\        }
+                \\    }
+                \\}
+                \\
+                ,
+            },
+        },
+        \\breakpoint set --file switch_blocks.zig --source-pattern-regexp '_ = &x;'
+        \\process launch
+        \\frame variable
+        \\process continue
+        \\frame variable
+        \\breakpoint delete --force 1
+    ,
+        &.{
+            \\(lldb) frame variable
+            \\(usize) i = 0
+            \\(u32) x = 123
+            \\(lldb) process continue
+            ,
+            \\(lldb) frame variable
+            \\(usize) i = 1
+            \\(f32) x = 4.5
+            \\(lldb) breakpoint delete --force 1
+            \\1 breakpoints deleted; 0 breakpoint locations disabled.
+        },
+    );
     db.addLldbTest(
         "inline_call",
         target,