Bug 31486 - [gdb/tdep, aarch64] Hang when hitting access watchpoint on M1
Summary: [gdb/tdep, aarch64] Hang when hitting access watchpoint on M1
Status: NEW
Alias: None
Product: gdb
Classification: Unclassified
Component: tdep (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-03-14 12:39 UTC by Tom de Vries
Modified: 2024-03-22 16:26 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
Tentative linux kernel PR text (2.51 KB, text/plain)
2024-03-19 12:42 UTC, Tom de Vries
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Tom de Vries 2024-03-14 12:39:31 UTC
With commit 9a03f218534 ("[gdb/tdep] Fix gdb.base/watchpoint-unaligned.exp on aarch64"), things have improved for regular watchpoints on the M1 aarch64 SOC, but the problem reported in PR29423 still exists for other watchpoints.

For instance, this works ok:
...
$ gdb -q -batch -iex "set trace-commands on" \
    outputs/gdb.base/watchpoint-unaligned/watchpoint-unaligned \
    -ex "awatch data.u.size8twice[0]" \
    -ex run
+awatch data.u.size8twice[0]
Hardware access (read/write) watchpoint 1: data.u.size8twice[0]
+run
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Hardware access (read/write) watchpoint 1: data.u.size8twice[0]

Old value = 0
New value = 1
write_size8twice () at /home/vries/gdb/src/gdb/testsuite/gdb.base/watchpoint-unaligned.c:56
56      }
...
but this doesn't:
...
$ gdb -q -batch -iex "set trace-commands on" \
    outputs/gdb.base/watchpoint-unaligned/watchpoint-unaligned \
    -ex "awatch data.u.size8twice[1]" \
    -ex run
+awatch data.u.size8twice[1]
Hardware access (read/write) watchpoint 1: data.u.size8twice[1]
+run
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
<hangs>
...

Luis mentioned an idea for trying to handle this here ( https://sourceware.org/pipermail/gdb-patches/2024-March/207259.html ).

Another idea for a fix explores the fact that on the one hand it's trivial to fix this by doing:
...
index 802bab6d682..f22840dd914 100644
--- a/gdb/aarch64-nat.c
+++ b/gdb/aarch64-nat.c
@@ -292,7 +292,7 @@ aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state,
           RK3399 SOC). But it also can be p (observed on M1 SOC).  Checking
           for this situation introduces the possibility of false positives,
           so we only do this for hw_write watchpoints.  */
-       const CORE_ADDR max_access_size = type == hw_write ? 16 : 8;
+       const CORE_ADDR max_access_size = type == hw_write ? 16 : 16;
        const CORE_ADDR addr_watch_base = addr_watch_aligned -
          (max_access_size - AARCH64_HWP_MAX_LEN_PER_REG);
        if (!(addr_trap >= addr_watch_base
...
but on the other hand that runs the risk of false positives.  It may not be acceptable to expose the user to this unprompted, but it may be acceptable if the user does so explicitly, by say "maint set imprecise-watchpoint-window 16".

Also, it seems to me that if the kernel would communicate the watchpoint register values (DBGBCRn_EL1 and DBGBVRn_EL1) for which it reports a trigger to user space, it would make it trivial to determine whether and which watchpoint triggered (well, according to the kernel, which may still be wrong).  We should probably file a linux kernel PR for this.
Comment 1 Tom de Vries 2024-03-19 09:04:52 UTC
There's a similar issue with lldb (version 17.0.6), with less serious failure mode though.

When setting a write watchpoint on data.u.size8twice[1], execution stops for reason "trace":
...
$ lldb outputs/gdb.base/watchpoint-unaligned/watchpoint-unaligned
(lldb) target create "outputs/gdb.base/watchpoint-unaligned/watchpoint-unaligned"
Current executable set to '/home/vries/gdb/outputs/gdb.base/watchpoint-unaligned/watchpoint-unaligned' (aarch64).
(lldb) b main
Breakpoint 1: where = watchpoint-unaligned`main + 8 at watchpoint-unaligned.c:65:3, address = 0x0000000000410208
(lldb) r
Process 1075081 launched: '/home/vries/gdb/outputs/gdb.base/watchpoint-unaligned/watchpoint-unaligned' (aarch64)
Process 1075081 stopped
* thread #1, name = 'watchpoint-unal', stop reason = breakpoint 1.1
    frame #0: 0x0000000000410208 watchpoint-unaligned`main at watchpoint-unaligned.c:65:3
   62  
   63     assert (sizeof (data) == 8 + 3 * 8);
   64  
-> 65     write_size8twice ();
   66  
   67     while (size)
   68       {
(lldb) watch set var -w write data.u.size8twice[1]
Watchpoint created: Watchpoint 1: addr = 0x00440048 size = 8 state = enabled type = w
    declare @ '/home/vries/gdb/src/gdb/testsuite/gdb.base/watchpoint-unaligned.c:35:3'
    watchpoint spec = 'data.u.size8twice[1]'
    new value: 0
(lldb) c
Process 1075081 resuming
Process 1075081 stopped
* thread #1, name = 'watchpoint-unal', stop reason = trace
    frame #0: 0x00000000004101f0 watchpoint-unaligned`write_size8twice at watchpoint-unaligned.c:48:3
   45  
   46   #ifdef __aarch64__
   47     volatile void *p = &data.u.size8twice[offset];
-> 48     asm volatile ("stp %1, %2, [%0]"
   49                   : /* output */
   50                   : "r" (p), "r" (first), "r" (second) /* input */
   51                   : "memory" /* clobber */);
...

In contrast, when setting a write watchpoint on data.u.size8twice[0], execution stops for reason "watchpoint":
...
(lldb) c
Process 1075169 resuming

Watchpoint 1 hit:
old value: 0
new value: 1
Process 1075169 stopped
* thread #1, name = 'watchpoint-unal', stop reason = watchpoint 1
    frame #0: 0x00000000004101f4 watchpoint-unaligned`write_size8twice at watchpoint-unaligned.c:56:1
   53     data.u.size8twice[offset] = first;
   54     data.u.size8twice[offset + 1] = second;
   55   #endif
-> 56   }
   57  
   58   int
   59   main (void)
(lldb) 
...

This is using a write watchpoint, but it's the same for a regular watchpoint.  Note that gdb handles a regualar watchpoint by reporting a watchpoint stop in both (data.u.size8twice[0] and data.u.size8twice[1]) cases.
Comment 2 Tom de Vries 2024-03-19 12:42:54 UTC
Created attachment 15416 [details]
Tentative linux kernel PR text

(In reply to Tom de Vries from comment #0)
> Also, it seems to me that if the kernel would communicate the watchpoint
> register values (DBGBCRn_EL1 and DBGBVRn_EL1) for which it reports a trigger
> to user space, it would make it trivial to determine whether and which
> watchpoint triggered (well, according to the kernel, which may still be
> wrong).  We should probably file a linux kernel PR for this.

I wrote a draft.

Any comments?
Comment 3 Luis Machado 2024-03-20 09:50:37 UTC
Looks OK to me. I sent a message off-list.

Thanks for putting this together.
Comment 4 Tom de Vries 2024-03-22 16:26:21 UTC
(In reply to Luis Machado from comment #3)
> Looks OK to me. I sent a message off-list.
> 
> Thanks for putting this together.

Filed at https://bugzilla.kernel.org/show_bug.cgi?id=218628