from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
-# Hardware breakpoints are supported only by platforms mentioned in oslist.
-@skipUnlessPlatform(oslist=['linux'])
class HardwareBreakpointMultiThreadTestCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True
mydir = TestBase.compute_mydir(__file__)
- # LLDB supports hardware breakpoints for arm and aarch64 architectures.
+ # LLDB on linux supports hardware breakpoints for arm and aarch64
+ # architectures.
+ @skipUnlessPlatform(oslist=['linux'])
@skipIf(archs=no_match(['arm', 'aarch64']))
- def test_hw_break_set_delete_multi_thread(self):
+ def test_hw_break_set_delete_multi_thread_linux(self):
self.build()
self.setTearDownCleanup()
- self.break_multi_thread('delete')
+ self.break_multi_thread('delete', 'breakpoint')
- # LLDB supports hardware breakpoints for arm and aarch64 architectures.
+ # LLDB on linux supports hardware breakpoints for arm and aarch64
+ # architectures.
+ @skipUnlessPlatform(oslist=['linux'])
@skipIf(archs=no_match(['arm', 'aarch64']))
- def test_hw_break_set_disable_multi_thread(self):
+ def test_hw_break_set_disable_multi_thread_linux(self):
self.build()
self.setTearDownCleanup()
- self.break_multi_thread('disable')
+ self.break_multi_thread('disable', 'breakpoint')
+
+ # LLDB on darwin supports hardware breakpoints for arm, aarch64, x86_64 and
+ # i386 architectures.
+ @skipUnlessDarwin
+ @skipIfOutOfTreeDebugserver
+ def test_hw_break_set_delete_multi_thread_macos(self):
+ self.build()
+ self.setTearDownCleanup()
+ self.break_multi_thread('delete', 'EXC_BREAKPOINT')
+
+ # LLDB on darwin supports hardware breakpoints for arm, aarch64, x86_64 and
+ # i386 architectures.
+ @skipUnlessDarwin
+ @skipIfOutOfTreeDebugserver
+ def test_hw_break_set_disable_multi_thread_macos(self):
+ self.build()
+ self.setTearDownCleanup()
+ self.break_multi_thread('disable', 'EXC_BREAKPOINT')
+
def setUp(self):
# Call super's setUp().
self.first_stop = line_number(
self.source, 'Starting thread creation with hardware breakpoint set')
- def break_multi_thread(self, removal_type):
+ def break_multi_thread(self, removal_type, stop_reason):
"""Test that lldb hardware breakpoints work for multiple threads."""
self.runCmd("file " + self.getBuildArtifact("a.out"),
CURRENT_EXECUTABLE_SET)
# We should be stopped again due to the breakpoint.
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
- substrs=['stopped',
- 'stop reason = breakpoint'])
+ substrs=['stopped', 'stop reason = breakpoint'])
# Now set a hardware breakpoint in thread function.
self.expect("breakpoint set -b hw_break_function --hardware",
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs=[
- 'stop reason = breakpoint',
+ 'stop reason = {}'.format(stop_reason),
'hw_break_function'])
# Continue the loop and test that we are stopped 4 times.
}
uint32_t DNBArchMachARM::EnableHardwareBreakpoint(nub_addr_t addr,
- nub_size_t size) {
+ nub_size_t size,
+ bool also_set_on_task) {
// Make sure our address isn't bogus
if (addr & 1)
return INVALID_NUB_HW_INDEX;
return INVALID_NUB_HW_INDEX;
}
-bool DNBArchMachARM::DisableHardwareBreakpoint(uint32_t hw_index) {
+bool DNBArchMachARM::DisableHardwareBreakpoint(uint32_t hw_index,
+ bool also_set_on_task) {
kern_return_t kret = GetDBGState(false);
const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();
virtual uint32_t NumSupportedHardwareBreakpoints();
virtual uint32_t NumSupportedHardwareWatchpoints();
- virtual uint32_t EnableHardwareBreakpoint(nub_addr_t addr, nub_size_t size);
- virtual bool DisableHardwareBreakpoint(uint32_t hw_break_index);
+ virtual uint32_t EnableHardwareBreakpoint(nub_addr_t addr, nub_size_t size,
+ bool also_set_on_task);
+ virtual bool DisableHardwareBreakpoint(uint32_t hw_break_index,
+ bool also_set_on_task);
virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size,
bool read, bool write,
return false;
}
+uint32_t DNBArchImplI386::NumSupportedHardwareBreakpoints() {
+ // Available debug address registers: dr0, dr1, dr2, dr3.
+ return 4;
+}
+
uint32_t DNBArchImplI386::NumSupportedHardwareWatchpoints() {
// Available debug address registers: dr0, dr1, dr2, dr3.
return 4;
return;
}
+void DNBArchImplI386::SetHardwareBreakpoint(DBG &debug_state, uint32_t hw_index,
+ nub_addr_t addr, nub_size_t size) {
+ // Set both dr7 (debug control register) and dri (debug address register).
+
+ // dr7{7-0} encodes the local/gloabl enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ //
+ // dr7{31-16} encodes the rw/len bits:
+ // b_x+3, b_x+2, b_x+1, b_x
+ // where bits{x+1, x} => rw
+ // 0b00: execute, 0b01: write, 0b11: read-or-write, 0b10: io
+ // read-or-write (unused)
+ // and bits{x+3, x+2} => len
+ // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte
+ //
+ // dr0 -> bits{19-16}
+ // dr1 -> bits{23-20}
+ // dr2 -> bits{27-24}
+ // dr3 -> bits{31-28}
+ debug_state.__dr7 |= (1 << (2 * hw_index) | 0 << (16 + 4 * hw_index));
+ uint32_t addr_32 = addr & 0xffffffff;
+ switch (hw_index) {
+ case 0:
+ debug_state.__dr0 = addr_32;
+ break;
+ case 1:
+ debug_state.__dr1 = addr_32;
+ break;
+ case 2:
+ debug_state.__dr2 = addr_32;
+ break;
+ case 3:
+ debug_state.__dr3 = addr_32;
+ break;
+ default:
+ assert(0 &&
+ "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ }
+ return;
+}
+
+uint32_t DNBArchImplI386::EnableHardwareBreakpoint(nub_addr_t addr,
+ nub_size_t size,
+ bool also_set_on_task) {
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "DNBArchImplI386::EnableHardwareBreakpoint( addr = "
+ "0x%8.8llx, size = %llu )",
+ (uint64_t)addr, (uint64_t)size);
+
+ const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints();
+ // Read the debug state
+ kern_return_t kret = GetDBGState(false);
+
+ if (kret != KERN_SUCCESS) {
+ return INVALID_NUB_HW_INDEX;
+ }
+
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ DBG &debug_state = m_state.context.dbg;
+ for (i = 0; i < num_hw_breakpoints; ++i) {
+ if (IsWatchpointVacant(debug_state, i)) {
+ break;
+ }
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_breakpoints) {
+ DNBLogThreadedIf(
+ LOG_BREAKPOINTS,
+ "DNBArchImplI386::EnableHardwareBreakpoint( free slot = %u )", i);
+
+ StartTransForHWP();
+
+ // Modify our local copy of the debug state, first.
+ SetHardwareBreakpoint(debug_state, i, addr, size);
+ // Now set the watch point in the inferior.
+ kret = SetDBGState(also_set_on_task);
+
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "DNBArchImplI386::"
+ "EnableHardwareBreakpoint() "
+ "SetDBGState() => 0x%8.8x.",
+ kret);
+
+ if (kret == KERN_SUCCESS) {
+ DNBLogThreadedIf(
+ LOG_BREAKPOINTS,
+ "DNBArchImplI386::EnableHardwareBreakpoint( enabled at slot = %u)",
+ i);
+ return i;
+ }
+ // Revert to the previous debug state voluntarily. The transaction
+ // coordinator knows that we have failed.
+ else {
+ m_state.context.dbg = GetDBGCheckpoint();
+ }
+ } else {
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "DNBArchImplI386::EnableHardwareBreakpoint(addr = "
+ "0x%8.8llx, size = %llu) => all hardware breakpoint "
+ "resources are being used.",
+ (uint64_t)addr, (uint64_t)size);
+ }
+
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool DNBArchImplI386::DisableHardwareBreakpoint(uint32_t hw_index,
+ bool also_set_on_task) {
+ kern_return_t kret = GetDBGState(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();
+ if (kret == KERN_SUCCESS) {
+ DBG &debug_state = m_state.context.dbg;
+ if (hw_index < num_hw_points &&
+ !IsWatchpointVacant(debug_state, hw_index)) {
+
+ StartTransForHWP();
+
+ // Modify our local copy of the debug state, first.
+ ClearWatchpoint(debug_state, hw_index);
+ // Now disable the watch point in the inferior.
+ kret = SetDBGState(true);
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "DNBArchImplI386::DisableHardwareBreakpoint( %u )",
+ hw_index);
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ else // Revert to the previous debug state voluntarily. The transaction
+ // coordinator knows that we have failed.
+ m_state.context.dbg = GetDBGCheckpoint();
+ }
+ }
+ return false;
+}
+
void DNBArchImplI386::ClearWatchpoint(DBG &debug_state, uint32_t hw_index) {
debug_state.__dr7 &= ~(3 << (2 * hw_index));
switch (hw_index) {
virtual bool ThreadDidStop();
virtual bool NotifyException(MachException::Data &exc);
+ virtual uint32_t NumSupportedHardwareBreakpoints();
virtual uint32_t NumSupportedHardwareWatchpoints();
+ virtual uint32_t EnableHardwareBreakpoint(nub_addr_t addr, nub_size_t size,
+ bool also_set_on_task);
+ virtual bool DisableHardwareBreakpoint(uint32_t hw_index,
+ bool also_set_on_task);
virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size,
bool read, bool write,
bool also_set_on_task);
static uint32_t GetRegisterContextSize();
+ static void SetHardwareBreakpoint(DBG &debug_state, uint32_t hw_index,
+ nub_addr_t addr, nub_size_t size);
+
// Helper functions for watchpoint manipulations.
static void SetWatchpoint(DBG &debug_state, uint32_t hw_index,
nub_addr_t addr, nub_size_t size, bool read,
return 4;
}
+uint32_t DNBArchImplX86_64::NumSupportedHardwareBreakpoints() {
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "DNBArchImplX86_64::NumSupportedHardwareBreakpoints");
+ return 4;
+}
+
static uint32_t size_and_rw_bits(nub_size_t size, bool read, bool write) {
uint32_t rw;
if (read) {
return m_2pc_dbg_checkpoint;
}
+void DNBArchImplX86_64::SetHardwareBreakpoint(DBG &debug_state,
+ uint32_t hw_index,
+ nub_addr_t addr,
+ nub_size_t size) {
+ // Set both dr7 (debug control register) and dri (debug address register).
+
+ // dr7{7-0} encodes the local/gloabl enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ //
+ // dr7{31-16} encodes the rw/len bits:
+ // b_x+3, b_x+2, b_x+1, b_x
+ // where bits{x+1, x} => rw
+ // 0b00: execute, 0b01: write, 0b11: read-or-write, 0b10: io
+ // read-or-write (unused)
+ // and bits{x+3, x+2} => len
+ // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte
+ //
+ // dr0 -> bits{19-16}
+ // dr1 -> bits{23-20}
+ // dr2 -> bits{27-24}
+ // dr3 -> bits{31-28}
+ debug_state.__dr7 |= (1 << (2 * hw_index) | 0 << (16 + 4 * hw_index));
+
+ switch (hw_index) {
+ case 0:
+ debug_state.__dr0 = addr;
+ break;
+ case 1:
+ debug_state.__dr1 = addr;
+ break;
+ case 2:
+ debug_state.__dr2 = addr;
+ break;
+ case 3:
+ debug_state.__dr3 = addr;
+ break;
+ default:
+ assert(0 &&
+ "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ }
+ return;
+}
+
+uint32_t DNBArchImplX86_64::EnableHardwareBreakpoint(nub_addr_t addr,
+ nub_size_t size,
+ bool also_set_on_task) {
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "DNBArchImplX86_64::EnableHardwareBreakpoint( addr = "
+ "0x%8.8llx, size = %llu )",
+ (uint64_t)addr, (uint64_t)size);
+
+ const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints();
+ // Read the debug state
+ kern_return_t kret = GetDBGState(false);
+
+ if (kret != KERN_SUCCESS) {
+ return INVALID_NUB_HW_INDEX;
+ }
+
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ DBG &debug_state = m_state.context.dbg;
+ for (i = 0; i < num_hw_breakpoints; ++i) {
+ if (IsWatchpointVacant(debug_state, i)) {
+ break;
+ }
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_breakpoints) {
+ DNBLogThreadedIf(
+ LOG_BREAKPOINTS,
+ "DNBArchImplX86_64::EnableHardwareBreakpoint( free slot = %u )", i);
+
+ StartTransForHWP();
+
+ // Modify our local copy of the debug state, first.
+ SetHardwareBreakpoint(debug_state, i, addr, size);
+ // Now set the watch point in the inferior.
+ kret = SetDBGState(also_set_on_task);
+
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "DNBArchImplX86_64::"
+ "EnableHardwareBreakpoint() "
+ "SetDBGState() => 0x%8.8x.",
+ kret);
+
+ if (kret == KERN_SUCCESS) {
+ DNBLogThreadedIf(
+ LOG_BREAKPOINTS,
+ "DNBArchImplX86_64::EnableHardwareBreakpoint( enabled at slot = %u)",
+ i);
+ return i;
+ }
+ // Revert to the previous debug state voluntarily. The transaction
+ // coordinator knows that we have failed.
+ else {
+ m_state.context.dbg = GetDBGCheckpoint();
+ }
+ } else {
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "DNBArchImplX86_64::EnableHardwareBreakpoint(addr = "
+ "0x%8.8llx, size = %llu) => all hardware breakpoint "
+ "resources are being used.",
+ (uint64_t)addr, (uint64_t)size);
+ }
+
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool DNBArchImplX86_64::DisableHardwareBreakpoint(uint32_t hw_index,
+ bool also_set_on_task) {
+ kern_return_t kret = GetDBGState(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();
+ if (kret == KERN_SUCCESS) {
+ DBG &debug_state = m_state.context.dbg;
+ if (hw_index < num_hw_points &&
+ !IsWatchpointVacant(debug_state, hw_index)) {
+
+ StartTransForHWP();
+
+ // Modify our local copy of the debug state, first.
+ ClearWatchpoint(debug_state, hw_index);
+ // Now disable the watch point in the inferior.
+ kret = SetDBGState(true);
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "DNBArchImplX86_64::DisableHardwareBreakpoint( %u )",
+ hw_index);
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ else // Revert to the previous debug state voluntarily. The transaction
+ // coordinator knows that we have failed.
+ m_state.context.dbg = GetDBGCheckpoint();
+ }
+ }
+ return false;
+}
+
uint32_t DNBArchImplX86_64::EnableHardwareWatchpoint(nub_addr_t addr,
nub_size_t size, bool read,
bool write,
virtual bool ThreadDidStop();
virtual bool NotifyException(MachException::Data &exc);
+ virtual uint32_t NumSupportedHardwareBreakpoints();
virtual uint32_t NumSupportedHardwareWatchpoints();
+
+ virtual uint32_t EnableHardwareBreakpoint(nub_addr_t addr, nub_size_t size,
+ bool also_set_on_task);
+ virtual bool DisableHardwareBreakpoint(uint32_t hw_break_index,
+ bool also_set_on_task);
virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size,
bool read, bool write,
bool also_set_on_task);
static uint32_t GetRegisterContextSize();
+ static void SetHardwareBreakpoint(DBG &debug_state, uint32_t hw_index,
+ nub_addr_t addr, nub_size_t size);
+
// Helper functions for watchpoint manipulations.
static void SetWatchpoint(DBG &debug_state, uint32_t hw_index,
nub_addr_t addr, nub_size_t size, bool read,
"x", "Read data from memory"));
t.push_back(Packet(write_data_to_memory, &RNBRemote::HandlePacket_X, NULL,
"X", "Write data to memory"));
- // t.push_back (Packet (insert_hardware_bp,
- // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "Z1", "Insert hardware
- // breakpoint"));
- // t.push_back (Packet (remove_hardware_bp,
- // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "z1", "Remove hardware
- // breakpoint"));
+ t.push_back(Packet(insert_hardware_bp, &RNBRemote::HandlePacket_z, NULL, "Z1",
+ "Insert hardware breakpoint"));
+ t.push_back(Packet(remove_hardware_bp, &RNBRemote::HandlePacket_z, NULL, "z1",
+ "Remove hardware breakpoint"));
t.push_back(Packet(insert_write_watch_bp, &RNBRemote::HandlePacket_z, NULL,
"Z2", "Insert write watchpoint"));
t.push_back(Packet(remove_write_watch_bp, &RNBRemote::HandlePacket_z, NULL,