Skip to content

Commit 96f3ea0

Browse files
committed
[lldb/debugserver] Implement hardware breakpoints for x86_64 and i386
This implements hardware breakpoints for x86_64 and i386 in debugserver. It's based on Pedro's patch sent to lldb-commits [1] although most of it is the same as the existing hardware watchpoint implementation. [1] http://lists.llvm.org/pipermail/lldb-commits/Week-of-Mon-20200113/060327.html Differential revision: https://reviews.llvm.org/D72985
1 parent 9902c8e commit 96f3ea0

File tree

8 files changed

+364
-22
lines changed

8 files changed

+364
-22
lines changed

lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/hardware_breakpoints/hardware_breakpoint_on_multiple_threads/TestHWBreakMultiThread.py

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,47 @@
99
from lldbsuite.test.lldbtest import *
1010
from lldbsuite.test import lldbutil
1111

12-
# Hardware breakpoints are supported only by platforms mentioned in oslist.
13-
@skipUnlessPlatform(oslist=['linux'])
1412
class HardwareBreakpointMultiThreadTestCase(TestBase):
1513
NO_DEBUG_INFO_TESTCASE = True
1614

1715
mydir = TestBase.compute_mydir(__file__)
1816

19-
# LLDB supports hardware breakpoints for arm and aarch64 architectures.
17+
# LLDB on linux supports hardware breakpoints for arm and aarch64
18+
# architectures.
19+
@skipUnlessPlatform(oslist=['linux'])
2020
@skipIf(archs=no_match(['arm', 'aarch64']))
21-
def test_hw_break_set_delete_multi_thread(self):
21+
def test_hw_break_set_delete_multi_thread_linux(self):
2222
self.build()
2323
self.setTearDownCleanup()
24-
self.break_multi_thread('delete')
24+
self.break_multi_thread('delete', 'breakpoint')
2525

26-
# LLDB supports hardware breakpoints for arm and aarch64 architectures.
26+
# LLDB on linux supports hardware breakpoints for arm and aarch64
27+
# architectures.
28+
@skipUnlessPlatform(oslist=['linux'])
2729
@skipIf(archs=no_match(['arm', 'aarch64']))
28-
def test_hw_break_set_disable_multi_thread(self):
30+
def test_hw_break_set_disable_multi_thread_linux(self):
2931
self.build()
3032
self.setTearDownCleanup()
31-
self.break_multi_thread('disable')
33+
self.break_multi_thread('disable', 'breakpoint')
34+
35+
# LLDB on darwin supports hardware breakpoints for arm, aarch64, x86_64 and
36+
# i386 architectures.
37+
@skipUnlessDarwin
38+
@skipIfOutOfTreeDebugserver
39+
def test_hw_break_set_delete_multi_thread_macos(self):
40+
self.build()
41+
self.setTearDownCleanup()
42+
self.break_multi_thread('delete', 'EXC_BREAKPOINT')
43+
44+
# LLDB on darwin supports hardware breakpoints for arm, aarch64, x86_64 and
45+
# i386 architectures.
46+
@skipUnlessDarwin
47+
@skipIfOutOfTreeDebugserver
48+
def test_hw_break_set_disable_multi_thread_macos(self):
49+
self.build()
50+
self.setTearDownCleanup()
51+
self.break_multi_thread('disable', 'EXC_BREAKPOINT')
52+
3253

3354
def setUp(self):
3455
# Call super's setUp().
@@ -39,7 +60,7 @@ def setUp(self):
3960
self.first_stop = line_number(
4061
self.source, 'Starting thread creation with hardware breakpoint set')
4162

42-
def break_multi_thread(self, removal_type):
63+
def break_multi_thread(self, removal_type, stop_reason):
4364
"""Test that lldb hardware breakpoints work for multiple threads."""
4465
self.runCmd("file " + self.getBuildArtifact("a.out"),
4566
CURRENT_EXECUTABLE_SET)
@@ -54,8 +75,7 @@ def break_multi_thread(self, removal_type):
5475
# We should be stopped again due to the breakpoint.
5576
# The stop reason of the thread should be breakpoint.
5677
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
57-
substrs=['stopped',
58-
'stop reason = breakpoint'])
78+
substrs=['stopped', 'stop reason = breakpoint'])
5979

6080
# Now set a hardware breakpoint in thread function.
6181
self.expect("breakpoint set -b hw_break_function --hardware",
@@ -75,7 +95,7 @@ def break_multi_thread(self, removal_type):
7595
# The stop reason of the thread should be breakpoint.
7696
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
7797
substrs=[
78-
'stop reason = breakpoint',
98+
'stop reason = {}'.format(stop_reason),
7999
'hw_break_function'])
80100

81101
# Continue the loop and test that we are stopped 4 times.

lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,8 @@ uint32_t DNBArchMachARM::NumSupportedHardwareWatchpoints() {
980980
}
981981

982982
uint32_t DNBArchMachARM::EnableHardwareBreakpoint(nub_addr_t addr,
983-
nub_size_t size) {
983+
nub_size_t size,
984+
bool also_set_on_task) {
984985
// Make sure our address isn't bogus
985986
if (addr & 1)
986987
return INVALID_NUB_HW_INDEX;
@@ -1052,7 +1053,8 @@ uint32_t DNBArchMachARM::EnableHardwareBreakpoint(nub_addr_t addr,
10521053
return INVALID_NUB_HW_INDEX;
10531054
}
10541055

1055-
bool DNBArchMachARM::DisableHardwareBreakpoint(uint32_t hw_index) {
1056+
bool DNBArchMachARM::DisableHardwareBreakpoint(uint32_t hw_index,
1057+
bool also_set_on_task) {
10561058
kern_return_t kret = GetDBGState(false);
10571059

10581060
const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();

lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,10 @@ class DNBArchMachARM : public DNBArchProtocol {
7070

7171
virtual uint32_t NumSupportedHardwareBreakpoints();
7272
virtual uint32_t NumSupportedHardwareWatchpoints();
73-
virtual uint32_t EnableHardwareBreakpoint(nub_addr_t addr, nub_size_t size);
74-
virtual bool DisableHardwareBreakpoint(uint32_t hw_break_index);
73+
virtual uint32_t EnableHardwareBreakpoint(nub_addr_t addr, nub_size_t size,
74+
bool also_set_on_task);
75+
virtual bool DisableHardwareBreakpoint(uint32_t hw_break_index,
76+
bool also_set_on_task);
7577

7678
virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size,
7779
bool read, bool write,

lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,11 @@ bool DNBArchImplI386::NotifyException(MachException::Data &exc) {
718718
return false;
719719
}
720720

721+
uint32_t DNBArchImplI386::NumSupportedHardwareBreakpoints() {
722+
// Available debug address registers: dr0, dr1, dr2, dr3.
723+
return 4;
724+
}
725+
721726
uint32_t DNBArchImplI386::NumSupportedHardwareWatchpoints() {
722727
// Available debug address registers: dr0, dr1, dr2, dr3.
723728
return 4;
@@ -797,6 +802,151 @@ void DNBArchImplI386::SetWatchpoint(DBG &debug_state, uint32_t hw_index,
797802
return;
798803
}
799804

805+
void DNBArchImplI386::SetHardwareBreakpoint(DBG &debug_state, uint32_t hw_index,
806+
nub_addr_t addr, nub_size_t size) {
807+
// Set both dr7 (debug control register) and dri (debug address register).
808+
809+
// dr7{7-0} encodes the local/gloabl enable bits:
810+
// global enable --. .-- local enable
811+
// | |
812+
// v v
813+
// dr0 -> bits{1-0}
814+
// dr1 -> bits{3-2}
815+
// dr2 -> bits{5-4}
816+
// dr3 -> bits{7-6}
817+
//
818+
// dr7{31-16} encodes the rw/len bits:
819+
// b_x+3, b_x+2, b_x+1, b_x
820+
// where bits{x+1, x} => rw
821+
// 0b00: execute, 0b01: write, 0b11: read-or-write, 0b10: io
822+
// read-or-write (unused)
823+
// and bits{x+3, x+2} => len
824+
// 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte
825+
//
826+
// dr0 -> bits{19-16}
827+
// dr1 -> bits{23-20}
828+
// dr2 -> bits{27-24}
829+
// dr3 -> bits{31-28}
830+
debug_state.__dr7 |= (1 << (2 * hw_index) | 0 << (16 + 4 * hw_index));
831+
uint32_t addr_32 = addr & 0xffffffff;
832+
switch (hw_index) {
833+
case 0:
834+
debug_state.__dr0 = addr_32;
835+
break;
836+
case 1:
837+
debug_state.__dr1 = addr_32;
838+
break;
839+
case 2:
840+
debug_state.__dr2 = addr_32;
841+
break;
842+
case 3:
843+
debug_state.__dr3 = addr_32;
844+
break;
845+
default:
846+
assert(0 &&
847+
"invalid hardware register index, must be one of 0, 1, 2, or 3");
848+
}
849+
return;
850+
}
851+
852+
uint32_t DNBArchImplI386::EnableHardwareBreakpoint(nub_addr_t addr,
853+
nub_size_t size,
854+
bool also_set_on_task) {
855+
DNBLogThreadedIf(LOG_BREAKPOINTS,
856+
"DNBArchImplI386::EnableHardwareBreakpoint( addr = "
857+
"0x%8.8llx, size = %llu )",
858+
(uint64_t)addr, (uint64_t)size);
859+
860+
const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints();
861+
// Read the debug state
862+
kern_return_t kret = GetDBGState(false);
863+
864+
if (kret != KERN_SUCCESS) {
865+
return INVALID_NUB_HW_INDEX;
866+
}
867+
868+
// Check to make sure we have the needed hardware support
869+
uint32_t i = 0;
870+
871+
DBG &debug_state = m_state.context.dbg;
872+
for (i = 0; i < num_hw_breakpoints; ++i) {
873+
if (IsWatchpointVacant(debug_state, i)) {
874+
break;
875+
}
876+
}
877+
878+
// See if we found an available hw breakpoint slot above
879+
if (i < num_hw_breakpoints) {
880+
DNBLogThreadedIf(
881+
LOG_BREAKPOINTS,
882+
"DNBArchImplI386::EnableHardwareBreakpoint( free slot = %u )", i);
883+
884+
StartTransForHWP();
885+
886+
// Modify our local copy of the debug state, first.
887+
SetHardwareBreakpoint(debug_state, i, addr, size);
888+
// Now set the watch point in the inferior.
889+
kret = SetDBGState(also_set_on_task);
890+
891+
DNBLogThreadedIf(LOG_BREAKPOINTS,
892+
"DNBArchImplI386::"
893+
"EnableHardwareBreakpoint() "
894+
"SetDBGState() => 0x%8.8x.",
895+
kret);
896+
897+
if (kret == KERN_SUCCESS) {
898+
DNBLogThreadedIf(
899+
LOG_BREAKPOINTS,
900+
"DNBArchImplI386::EnableHardwareBreakpoint( enabled at slot = %u)",
901+
i);
902+
return i;
903+
}
904+
// Revert to the previous debug state voluntarily. The transaction
905+
// coordinator knows that we have failed.
906+
else {
907+
m_state.context.dbg = GetDBGCheckpoint();
908+
}
909+
} else {
910+
DNBLogThreadedIf(LOG_BREAKPOINTS,
911+
"DNBArchImplI386::EnableHardwareBreakpoint(addr = "
912+
"0x%8.8llx, size = %llu) => all hardware breakpoint "
913+
"resources are being used.",
914+
(uint64_t)addr, (uint64_t)size);
915+
}
916+
917+
return INVALID_NUB_HW_INDEX;
918+
}
919+
920+
bool DNBArchImplI386::DisableHardwareBreakpoint(uint32_t hw_index,
921+
bool also_set_on_task) {
922+
kern_return_t kret = GetDBGState(false);
923+
924+
const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();
925+
if (kret == KERN_SUCCESS) {
926+
DBG &debug_state = m_state.context.dbg;
927+
if (hw_index < num_hw_points &&
928+
!IsWatchpointVacant(debug_state, hw_index)) {
929+
930+
StartTransForHWP();
931+
932+
// Modify our local copy of the debug state, first.
933+
ClearWatchpoint(debug_state, hw_index);
934+
// Now disable the watch point in the inferior.
935+
kret = SetDBGState(true);
936+
DNBLogThreadedIf(LOG_WATCHPOINTS,
937+
"DNBArchImplI386::DisableHardwareBreakpoint( %u )",
938+
hw_index);
939+
940+
if (kret == KERN_SUCCESS)
941+
return true;
942+
else // Revert to the previous debug state voluntarily. The transaction
943+
// coordinator knows that we have failed.
944+
m_state.context.dbg = GetDBGCheckpoint();
945+
}
946+
}
947+
return false;
948+
}
949+
800950
void DNBArchImplI386::ClearWatchpoint(DBG &debug_state, uint32_t hw_index) {
801951
debug_state.__dr7 &= ~(3 << (2 * hw_index));
802952
switch (hw_index) {

lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,12 @@ class DNBArchImplI386 : public DNBArchProtocol {
5151
virtual bool ThreadDidStop();
5252
virtual bool NotifyException(MachException::Data &exc);
5353

54+
virtual uint32_t NumSupportedHardwareBreakpoints();
5455
virtual uint32_t NumSupportedHardwareWatchpoints();
56+
virtual uint32_t EnableHardwareBreakpoint(nub_addr_t addr, nub_size_t size,
57+
bool also_set_on_task);
58+
virtual bool DisableHardwareBreakpoint(uint32_t hw_index,
59+
bool also_set_on_task);
5560
virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size,
5661
bool read, bool write,
5762
bool also_set_on_task);
@@ -210,6 +215,9 @@ class DNBArchImplI386 : public DNBArchProtocol {
210215

211216
static uint32_t GetRegisterContextSize();
212217

218+
static void SetHardwareBreakpoint(DBG &debug_state, uint32_t hw_index,
219+
nub_addr_t addr, nub_size_t size);
220+
213221
// Helper functions for watchpoint manipulations.
214222
static void SetWatchpoint(DBG &debug_state, uint32_t hw_index,
215223
nub_addr_t addr, nub_size_t size, bool read,

0 commit comments

Comments
 (0)