diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/hardware_breakpoints/hardware_breakpoint_on_multiple_threads/TestHWBreakMultiThread.py b/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/hardware_breakpoints/hardware_breakpoint_on_multiple_threads/TestHWBreakMultiThread.py --- a/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/hardware_breakpoints/hardware_breakpoint_on_multiple_threads/TestHWBreakMultiThread.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/hardware_breakpoints/hardware_breakpoint_on_multiple_threads/TestHWBreakMultiThread.py @@ -9,26 +9,47 @@ 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(). @@ -39,7 +60,7 @@ 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) @@ -54,8 +75,7 @@ # 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", @@ -75,7 +95,7 @@ # 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. diff --git a/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h b/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h --- a/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h +++ b/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h @@ -70,8 +70,10 @@ 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, diff --git a/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp b/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp --- a/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp +++ b/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp @@ -980,7 +980,8 @@ } 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; @@ -1052,7 +1053,8 @@ 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(); diff --git a/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h b/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h --- a/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h +++ b/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h @@ -51,7 +51,12 @@ 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); @@ -210,6 +215,9 @@ 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, diff --git a/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp b/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp --- a/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp +++ b/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp @@ -718,6 +718,11 @@ 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; @@ -797,6 +802,151 @@ 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) { diff --git a/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h --- a/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h +++ b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h @@ -50,7 +50,13 @@ 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); @@ -213,6 +219,9 @@ 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, diff --git a/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp --- a/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp +++ b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp @@ -679,6 +679,12 @@ 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) { @@ -853,6 +859,153 @@ 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, diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp --- a/lldb/tools/debugserver/source/RNBRemote.cpp +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -279,12 +279,10 @@ "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,