[Lldb-commits] [lldb] [lldb][NFC] Refactor Watchpoint class (PR #163695)

via lldb-commits lldb-commits at lists.llvm.org
Wed Oct 15 23:38:07 PDT 2025


dlav-sc wrote:

I'd like to share some concerns regarding `StopInfo{Watchpoint, Breakpoint}`. You mentioned that you want to unify the logic of these classes, so this information might be relevant. Or perhaps you are already aware of the issue I want to demonstrate.

>From my point of view, performing any checks regarding whether there was a watchpoint hit or not in `StopInfo` is quite questionable. I'll try to illustrate my concerns with an example that you can reproduce by yourself using `ToT lldb`. Let's consider a program:
```
int main() {
  int watch_value = 10;
  int a = 5;
  watch_value += a;
  int b = 5;
  watch_value = 42;
}
```
Consider this scenario:

1. I set a watchpoint on `watch_value`
2. I configure a large ignore count for this watchpoint
3. I attempt to step to the next instruction using `next`

```
(lldb) b main
(lldb) r
-> int watch_value = 10;
(lldb) wa s v watch_value // set hw wp on watch_value
(lldb) watchpoint ignore -i 100 1 // set ignore_count=100
(lldb) next
Process 3706163 exited with status = 0 (0x00000000)
```
I expected to obtain a control at the next line, however as we can see, lldb doesn't return control to the user.
\
\
\
To understand the source of the issue, I decided to examine the `lldb step` logs. Here's what I saw after the `next` command:
```
intern-state     ThreadList::ShouldStop: 1 threads, 1 unsuspended threads                                                                                                               
intern-state     Thread::ShouldStop(0x5652ead54180) for tid = 0x3a7483 0x3a7483, pc = 0x000055555555513b                                                                                
intern-state     ^^^^^^^^ Thread::ShouldStop Begin ^^^^^^^^                                                                                                                             
intern-state     Plan stack initial state:                                                                                                                                              
  thread #1: tid = 0x3a7483:                                                                                                                                                            
    Active plan stack:                                                                                                                                                                  
      Element 0: Base thread plan.                                                                                                                                                      
      Element 1: Stepping over line ig_c.c:2:7 using ranges: [0x0000555555555134-0x000055555555513b).                                                                                   
      Element 2: Single stepping past breakpoint site 2 at 0x555555555134

intern-state     Step over breakpoint stopped for reason: watchpoint.
intern-state     ThreadPlanStepOverRange got asked if it explains the stop for some reason other than step.
intern-state     Plan base plan explains stop. 
intern-state     Base plan discarding thread plans for thread tid = 0x3a7483 (breakpoint hit.)
intern-state     Discarding thread plans for thread (tid = 0x3a7483, force 0)
intern-state     Plan Step over breakpoint trap being discarded in cleanup, it says it is already done.
intern-state     Discarding plan: "Step over breakpoint trap", tid = 0x3a7483.
intern-state     Step range plan out of range to 0x55555555513b
intern-state     Plan Step range stepping over being discarded in cleanup, it says it is already done.
intern-state     Popping plan: "Step range stepping over", tid = 0x3a7483.
intern-state     Plan stack final state:
  thread #1: tid = 0x3a7483:
    Active plan stack:
      Element 0: Base thread plan.
    Completed plan stack:
      Element 0: Stepping over line ig_c.c:2:7 using ranges: [0x0000555555555134-0x000055555555513b).
    Discarded plan stack:
      Element 0: Single stepping past breakpoint site 2 at 0x555555555134
```
As we can see, `lldb-server` sends a `watchpoint stop reason` to us. This is expected because before the first instruction of the `main()`, `watch_value` was uninitialized and contained garbage, and after the instruction it was initialized with 10. `lldb-server` detected the memory access and stopped execution with a `watchpoint stop reason`.

`lldb-client` starts its usual `ShouldStop` procedure, which involves walking through the thread plan stack. `StepOverThreadPlan` doesn't handle `watchpoint stop reason` (it expects `trace stop reason`), therefore we falls through to `ThreadPlanBase`.

After that, we initialize `StopInfoWatchpoint` and only in `PerformAction` do we discover that the ignore count condition is not satisfied. In that case, we don't report the watchpoint hit to the user and continue execution instead. Meanwhile `StepOverThreadPlan` has already been marked complete and removed from the stack. Therefore, before resuming, we have only the `ThreadPlanBase` plan on the stack:

```
intern-state     Plan stack initial state:
  thread #1: tid = 0x3a7483:
    Active plan stack:
      Element 0: Base thread plan.
```
As a result, with only `ThreadPlanBase` remaining, execution resumes without stopping.
\
\
\
The fundamental problem for me is that watchpoint checks are performed too late.

This behavior led me to believe that we should perform **all** watchpoint/breakpoint checks elsewhere, **before** creating `StopInfo{Watchpoint, Breakpoint}`.

I'm curious to hear your thoughts about the issue. Do you see this as a valid concern?

https://github.com/llvm/llvm-project/pull/163695


More information about the lldb-commits mailing list