This is an archive of the discontinued LLVM Phabricator instance.

debugserver: move AArch64 watchpoint traps within a watchpointed region, parse ESR flags and send them to lldb
ClosedPublic

Authored by jasonmolenda on Apr 7 2023, 3:39 PM.

Details

Summary

Currently on Darwin arm64 systems using debugserver, if you watch a memory region and a large write starts before the watched region, and extends in to the watched region, we will have a watchpoint exception but the address reported may be the start of the access -- before the watched memory range. In this case, lldb will not recognize which watchpoint to disable to step past the watchpoint and execution will stop, leaving the user to disable/stepi/re-enable manually.

This patch takes the trap address (FAR register) and finds the nearest watchpoint if it is not contained in any watched region. It also parses the ESR register flags and if the processor reported the watchpoint index number instead of an address in the FAR register, handle that. Send (1) an address within the watched mem range, (2) the watchpoint hardware index, and (3) the actual trap address which may exist outside a watched mem range to lldb in a description string in the stop packet.

Add a test case that has a uint8_t[8] array, watches a one-byte element in that array, and then does a 64-bit write to the entire array - so our FAR address may be the start of the uint8_t[8] array, and confirm that lldb correctly associates this with the watchpoint we set.

This patch depends on https://reviews.llvm.org/D147816 ("Clarify how watchpoint description in stop packets work, fix AArch64 unintended behavior") being present. Without that patch, when we have a trap address outside the range of all watched memory ranges, lldb will silently continue past this watchpoint. Correct behavior on MIPS targets, but not on AArch64.

Diff Detail

Event Timeline

jasonmolenda created this revision.Apr 7 2023, 3:39 PM
jasonmolenda requested review of this revision.Apr 7 2023, 3:39 PM
JDevlieghere accepted this revision.Apr 10 2023, 2:38 PM

I didn't double check the spec so I'm assuming those bitfield indexes are correct. Everything else makes sense to me. LGTM.

lldb/tools/debugserver/source/DNBBreakpoint.cpp
114–125

You could simplify this with the ternary operator:

uint32_t delta = addr < start_addr ? start_addr - addr : addr - end_addr;
if (delta < best_match) {
  closest = &pos.second;
  best_match = delta;
}
lldb/tools/debugserver/source/DNBDefs.h
284–297

Instead of a boolean and a field, you could make it a std::optional<esr_field>.

lldb/tools/debugserver/source/MacOSX/MachException.cpp
162–169

Interesting that you picked > 1 on line 163 and >= 3 on line 168.

This revision is now accepted and ready to land.Apr 10 2023, 2:38 PM

Thanks for the review. Will update the patch.

lldb/tools/debugserver/source/MacOSX/MachException.cpp
162–169

Yeah I should check for > 2 in the first conditional expression, because I"m going to access exc_data[1]. The third element (exc_data[2]) with the watchpoint number is optional and may not be provided in the mach exception.

jasonmolenda added inline comments.Apr 10 2023, 2:45 PM
lldb/tools/debugserver/source/MacOSX/MachException.cpp
162–169

(urk I meant to write >= 2)

This patch takes the trap address (FAR register) and finds the nearest watchpoint if it is not contained in any watched region.

From what I remember, the Linux kernel also does this.

lldb/test/API/commands/watchpoints/unaligned-watchpoint/TestUnalignedWatchpoint.py
6

Can you add here why "correctly associating" is not as easy as it sounds.

"This requires lldb to handle...".

lldb/test/API/commands/watchpoints/unaligned-watchpoint/main.c
9–12

These do what exactly?

lldb/tools/debugserver/source/MacOSX/MachProcess.mm
1425

"bit"

lldb/tools/debugserver/source/RNBRemote.cpp
2888

"by humans"?

jasonmolenda added inline comments.Apr 11 2023, 1:45 PM
lldb/test/API/commands/watchpoints/unaligned-watchpoint/main.c
9–12

These are doing 64-bit writes to u64_p which is pointing to the start of the uint8_t buf[8], while I'm watching 1 byte in the middle, so the FAR address I get is the start of the buffer (and outside of the address range I'm watching). This is the one that lldb currently skips over silently on AArch64 Linux/FreeBSD. Doing two of these writes on each source line was something I did while testing something else, I didn't really need to write it this way for this test case.

lldb/tools/debugserver/source/RNBRemote.cpp
2888

"by jason", really. ;) I was mostly trying to aid Future Jason who looks at this five years from now and wonders why the heck debugserver is sending all of these fields to lldb where none of them are used.

Update patch to incorporate Jonas' & David's feedback. I'm holding this patch until I can get the lldb changes to fix the reason:watchpoint description handling corrected, or the test case in this patch will fail. This patch changes debugserver's watchpoint notification to be the same as AArch64 lldb-server, but that currently skips over watchpoint traps that originate outside the watched region and the test case will fail with that behavior.

DavidSpickett added inline comments.Apr 12 2023, 12:46 AM
lldb/test/API/commands/watchpoints/unaligned-watchpoint/main.c
9–12

So can we have the minimum writes here then?

I guess that another *u64_p = 5; would be enough.

lldb/tools/debugserver/source/DNBDefs.h
284–297

+1