Issue 180924
Summary LLDB cannot step off of an inferior's embedded trap instruction if there is in LLDB breakpoint at the same location
Labels lldb
Assignees
Reporter DavidSpickett
    https://github.com/llvm/llvm-project/issues/56268 was fixed, but in testing that I found some strange interactions when a breakpoint is placed by LLDB on top of a breakpoint instruction in the inferior program.

# Reproducer Program

```
int main() {
 __builtin_debugtrap();
  return 0;
}
```
```
$ ./bin/clang /tmp/test.c -o /tmp/test.o -g
```
```
$ ./bin/llvm-objdump -d /tmp/test.o
<...>
0000000000000714 <main>:
     714: d10043ff      sub sp, sp, #0x10
     718: 2a1f03e0      mov     w0, wzr
     71c: b9000fff str     wzr, [sp, #0xc]
     720: d43e0000      brk     #0xf000
     724: 910043ff      add     sp, sp, #0x10
     728: d65f03c0      ret
```
Note that the `brk` is some way into the `main` function, but with the help of debug info, we know that it's the first instruction after the prologue so that's where `b main` ends up.
```
<...>
.Ltmp1:
        .loc    0 2 3 prologue_end              // /tmp/test.c:2:3
        brk #0xf000
```

# Scenario 1: No LLDB breakpoint

We hit the inferior `brk` and can continue. This is expected behaviour.
```
$ ./bin/lldb /tmp/test.o
(lldb) target create "/tmp/test.o"
Current executable set to '/tmp/test.o' (aarch64).
(lldb) run
Process 116373 launched: '/tmp/test.o' (aarch64)
Process 116373 stopped
* thread #1, name = 'test.o', stop reason = signal SIGTRAP
    frame #0: 0x0000aaaaaaaa0724 test.o`main at test.c:3:3
   1   	int main() {
   2   	  __builtin_debugtrap();
-> 3 	  return 0;
   4   	}
(lldb) c
Process 116373 resuming
Process 116373 exited with status = 0 (0x00000000)
```
# Scenario 2: break on `main`, run, continue from LLDB breakpoint
```
$ ./bin/lldb /tmp/test.o
(lldb) target create "/tmp/test.o"
Current executable set to '/tmp/test.o' (aarch64).
(lldb) b main
Breakpoint 1: where = test.o`main + 12 at test.c:2:3, address = 0x0000000000000720
(lldb) run
Process 116408 launched: '/tmp/test.o' (aarch64)
Process 116408 stopped
* thread #1, name = 'test.o', stop reason = breakpoint 1.1
    frame #0: 0x0000aaaaaaaa0720 test.o`main at test.c:2:3
   1   	int main() {
-> 2   	 __builtin_debugtrap();
   3   	  return 0;
   4   	}
```
The initial stop is due to the LLDB placed breakpoint. This is expected.
```
(lldb) c
Process 116408 resuming
Process 116408 stopped
* thread #1, name = 'test.o', stop reason = breakpoint 1.1
    frame #0: 0x0000aaaaaaaa0720 test.o`main at test.c:2:3
   1   	int main() {
-> 2   	 __builtin_debugtrap();
   3   	  return 0;
   4   	}
```
Continuing sends us into an endless loop of some kind. We don't detect the inferior's `brk` and step past it. The logs show the breakpoint being hit over and over again. This may be part of the single step logic. Which is usually: disable breakpoint, single step until PC changes, re-enable breakpoint.
```
(lldb) log enable lldb break
(lldb) c
<...>
intern-state     Hit breakpoint location: 1.1: 
  module = /tmp/test.o
  compile unit = test.c
  function = main
  location = /tmp/test.c:2:3
  address = 0x0000aaaaaaaa0720
 resolved = true
  hardware = false
  hit count = 37479    <<<<<<<<<<< hit count is constantly increasing
, stopping.

intern-state GDBRemoteCommunicationClient::SendGDBStoppointTypePacket() add at addr = 0xaaaaaaaa0720
Process 116408 stopped
* thread #1, name = 'test.o', stop reason = breakpoint 1.1
    frame #0: 0x0000aaaaaaaa0720 test.o`main at test.c:2:3
   1   	int main() {
-> 2   	  __builtin_debugtrap();
   3 	  return 0;
   4   	}
```
Maybe there's a niche argument that this is correct, but in 99% of cases, we'd want to continue past it. So what if we remove the LLDB breakpoint?
```
(lldb) breakpoint delete 1
1 breakpoints deleted; 0 breakpoint locations disabled.
(lldb) c
Process 116408 resuming
Process 116408 stopped
* thread #1, name = 'test.o', stop reason = trace
    frame #0: 0x0000aaaaaaaa0720 test.o`main at test.c:2:3
   1 	int main() {
-> 2   	  __builtin_debugtrap();
   3   	  return 0;
   4 	}
```
I would expect us to hit the inferior's `brk` and know to step over it, but for some reason once we've hit an LLDB breakpoint and continued, that doesn't work even if the breakpoint is removed.

# Scenario 3: Hit the LLDB breakpoint once, remove it, then continue

```
$ ./bin/lldb /tmp/test.o
(lldb) target create "/tmp/test.o"
Current executable set to '/tmp/test.o' (aarch64).
(lldb) b main
Breakpoint 1: where = test.o`main + 12 at test.c:2:3, address = 0x0000000000000720
(lldb) run
Process 116507 launched: '/tmp/test.o' (aarch64)
Process 116507 stopped
* thread #1, name = 'test.o', stop reason = breakpoint 1.1
    frame #0: 0x0000aaaaaaaa0720 test.o`main at test.c:2:3
   1   	int main() {
-> 2   	 __builtin_debugtrap();
   3   	  return 0;
   4   	}
(lldb) breakpoint delete 1
1 breakpoints deleted; 0 breakpoint locations disabled.
(lldb) c
Process 116507 resuming
Process 116507 stopped
* thread #1, name = 'test.o', stop reason = signal SIGTRAP
    frame #0: 0x0000aaaaaaaa0724 test.o`main at test.c:3:3
   1   	int main() {
   2   	 __builtin_debugtrap();
-> 3   	  return 0;
   4   	}
(lldb) c
Process 116507 resuming
Process 116507 exited with status = 0 (0x00000000)
```
This is working as expected.

It's as if continuing after the first LLDB breakpoint hit without removing the LLDB breakpoint puts LLDB into a state where it will never check if we're on an inferior's `brk` instruction.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to