Index: packages/Python/lldbsuite/test/functionalities/watchpoint/multi_watchpoint_slots/Makefile =================================================================== --- packages/Python/lldbsuite/test/functionalities/watchpoint/multi_watchpoint_slots/Makefile +++ packages/Python/lldbsuite/test/functionalities/watchpoint/multi_watchpoint_slots/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules Index: packages/Python/lldbsuite/test/functionalities/watchpoint/multi_watchpoint_slots/TestWatchpointMultipleSlots.py =================================================================== --- packages/Python/lldbsuite/test/functionalities/watchpoint/multi_watchpoint_slots/TestWatchpointMultipleSlots.py +++ packages/Python/lldbsuite/test/functionalities/watchpoint/multi_watchpoint_slots/TestWatchpointMultipleSlots.py @@ -0,0 +1,135 @@ +""" +Test watchpoint slots (1-byte/2-byte watchpoints with selective deletion) +Make sure we can watch all installed byte/word size watchpoints, +when we have installed multiple byte/word size watchpoints within same dword. +Also make sure we hit only the ones which are left after we selective deletion. + +""" + +from __future__ import print_function + +import os +import time +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class WatchpointSlotsTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + # Source filename. + self.source = 'main.c' + + # Output filename. + self.exe_name = 'a.out' + self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name} + + # Watchpoints not supported + @expectedFailureAndroid(archs=['arm', 'aarch64']) + @expectedFailureAll( + oslist=["windows"], + bugnumber="llvm.org/pr24446: WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows") + # Read-write watchpoints not supported on SystemZ + @expectedFailureAll(archs=['s390x']) + # This is a arm specific test case. No other architectures tested. + @skipIf(archs=no_match(['arm'])) + def test_byte_size_watchpoints_multi_slots(self): + """Test to selectively watch different bytes in a 8-byte array.""" + self.run_watchpoint_slot_test('byteArray', 16, '1', + ['2', '4', '6', '8'], ['1', '3', '5', '7']) + + # Watchpoints not supported + @expectedFailureAndroid(archs=['arm', 'aarch64']) + @expectedFailureAll( + oslist=["windows"], + bugnumber="llvm.org/pr24446: WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows") + # Read-write watchpoints not supported on SystemZ + @expectedFailureAll(archs=['s390x']) + # This is a arm specific test case. No other architectures tested. + @skipIf(archs=no_match(['arm'])) + def test_two_byte_watchpoints_multi_slots(self): + """Test to randomly watch different words in an 8-byte word array.""" + self.run_watchpoint_slot_test('wordArray', 8, '2', + ['2', '4', '6', '8'], ['1', '3', '5', '7']) + + def run_watchpoint_slot_test(self, arrayName, array_size, watchsize, + watch_hit_list, watch_del_list): + self.build(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Detect line number after which we are going to increment arrayName. + loc_line = line_number('main.c', '// About to write ' + arrayName) + + # Set a breakpoint on the line detected above. + lldbutil.run_break_set_by_file_and_line( + self, "main.c", loc_line, num_expected_locations=1, loc_exact=True) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', 'stop reason = breakpoint']) + + # Delete breakpoint we just hit. + self.expect("breakpoint delete 1", substrs=['1 breakpoints deleted']) + + for i in range(array_size): + # Set a read_write type watchpoint arrayName + watch_loc = arrayName + "[" + str(i) + "]" + self.expect( + "watchpoint set variable -w read_write " + + watch_loc, + WATCHPOINT_CREATED, + substrs=[ + 'Watchpoint created', + 'size = ' + + watchsize, + 'type = rw']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v 1", substrs=['hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped due to the watchpoint. + # The stop reason of the thread should be watchpoint. + self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, + substrs=['stopped', 'stop reason = watchpoint 1']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should now be 1. + self.expect("watchpoint list -v 1", substrs=['hit_count = 1']) + + # Selectively delete watchpoints. + for idel in watch_del_list: + self.expect("watchpoint delete " + idel, + substrs=['1 watchpoints deleted']) + + # Try setting watchpoint here and it should fail. + self.expect("watchpoint set variable -w read_write dWordVar", + substrs=['Watchpoint creation failed'], error=True) + + for ihit in watch_hit_list: + # Resume inferior. + self.runCmd("process continue") + + # We should be stopped due to the watchpoint. + # The stop reason of the thread should be watchpoint. + self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, + substrs=['stopped', 'stop reason = watchpoint ' + ihit]) + + # Resume inferior. + self.runCmd("process continue") Index: packages/Python/lldbsuite/test/functionalities/watchpoint/multi_watchpoint_slots/main.c =================================================================== --- packages/Python/lldbsuite/test/functionalities/watchpoint/multi_watchpoint_slots/main.c +++ packages/Python/lldbsuite/test/functionalities/watchpoint/multi_watchpoint_slots/main.c @@ -0,0 +1,40 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include +#include + +uint64_t pad0 = 0; +uint8_t byteArray[16] = {0}; +uint64_t pad1 = 0; +uint16_t wordArray[8] = {0}; +uint32_t dWordVar = 0; + +int main(int argc, char** argv) { + + int i; + + for (i = 0; i < 16; i++) + { + printf("About to write byteArray[%d] ...\n", i); // About to write byteArray + pad0++; + byteArray[i] = 7; + pad1++; + } + + for (i = 0; i < 8; i++) + { + printf("About to write wordArray[%d] ...\n", i); // About to write wordArray + pad0++; + wordArray[i] = 7; + pad1++; + } + + dWordVar = 5; // Write dWordVar to check if a stale watchpoint is active + return 0; +} Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp @@ -508,8 +508,19 @@ if (error.Fail()) return LLDB_INVALID_INDEX32; - uint32_t control_value = 0, wp_index = 0, addr_word_offset = 0, byte_mask = 0; + uint32_t control_value = 0; lldb::addr_t real_addr = addr; + uint32_t wp_index = LLDB_INVALID_INDEX32; + + // Find out how many bytes we need to watch after 4-byte alignment boundary. + uint8_t watch_size = (addr & 0x03) + size; + + // We cannot watch zero or more than 4 bytes after 4-byte alignment boundary. + if (size == 0 || watch_size > 4) + return LLDB_INVALID_INDEX32; + + // Strip away last two bits of address for byte/half-word/word selection. + addr &= ~((lldb::addr_t)3); // Check if we are setting watchpoint other than read/write/access // Also update watchpoint flag to match Arm write-read bit configuration. @@ -526,40 +537,41 @@ return LLDB_INVALID_INDEX32; } - // Can't watch zero bytes - // Can't watch more than 4 bytes per WVR/WCR pair + // Iterate over stored watchpoints and find a free or duplicate wp_index + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + if ((m_hwp_regs[i].control & 1) == 0 && (m_hwp_regs[i].refcount <= 0)) { + wp_index = i; // Mark last free slot + } else if (m_hwp_regs[i].address == addr) { + wp_index = i; // Mark duplicate index + break; // Stop searching here + } + } - if (size == 0 || size > 4) + // No vaccant slot available and no duplicate slot found. + if (wp_index == LLDB_INVALID_INDEX32) return LLDB_INVALID_INDEX32; - // Check 4-byte alignment for hardware watchpoint target address. - // Below is a hack to recalculate address and size in order to - // make sure we can watch non 4-byte alligned addresses as well. - if (addr & 0x03) { - uint8_t watch_mask = (addr & 0x03) + size; - - if (watch_mask > 0x04) - return LLDB_INVALID_INDEX32; - else if (watch_mask <= 0x02) - size = 2; - else if (watch_mask <= 0x04) - size = 4; - - addr = addr & (~0x03); + uint8_t current_watch_size, new_watch_size; + // Calculate overall size width to be watched by current hardware watchpoint slot. + current_watch_size = GetWatchpointSize(wp_index); + new_watch_size = std::max(current_watch_size, watch_size); + + // Make new_watch_size a power of 2. + if (new_watch_size == 3) + new_watch_size++; + + // There is no need to update watchpoint registers + // if requested byte range is already covered by exiting watchpoint. + if (current_watch_size == new_watch_size && + m_hwp_regs[wp_index].control & 1) { + m_hwp_regs[wp_index].refcount++; + return wp_index; } - // We can only watch up to four bytes that follow a 4 byte aligned address - // per watchpoint register pair, so make sure we can properly encode this. - addr_word_offset = addr % 4; - byte_mask = ((1u << size) - 1u) << addr_word_offset; - - // Check if we need multiple watchpoint register - if (byte_mask > 0xfu) - return LLDB_INVALID_INDEX32; - // Setup control value + // Create byte mask for control register // Make the byte_mask into a valid Byte Address Select mask - control_value = byte_mask << 5; + control_value = ((1u << new_watch_size) - 1u) << 5; // Turn on appropriate watchpoint flags read or write control_value |= (watch_flags << 3); @@ -567,45 +579,29 @@ // Enable this watchpoint and make it stop in privileged or user mode; control_value |= 7; - // Make sure bits 1:0 are clear in our address - addr &= ~((lldb::addr_t)3); + lldb::addr_t tempAddr = 0; + uint32_t tempControl = 0; - // Iterate over stored watchpoints - // Find a free wp_index or update reference count if duplicate. - wp_index = LLDB_INVALID_INDEX32; - for (uint32_t i = 0; i < m_max_hwp_supported; i++) { - if ((m_hwp_regs[i].control & 1) == 0) { - wp_index = i; // Mark last free slot - } else if (m_hwp_regs[i].address == addr && - m_hwp_regs[i].control == control_value) { - wp_index = i; // Mark duplicate index - break; // Stop searching here - } - } + // Create a backup we can revert to in case of failure. + tempAddr = m_hwp_regs[wp_index].address; + tempControl = m_hwp_regs[wp_index].control; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].address = addr; + m_hwp_regs[wp_index].control = control_value; + + // PTRACE call to set corresponding watchpoint register. + error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index); + + if (error.Fail()) { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].address = tempAddr; - if (wp_index == LLDB_INVALID_INDEX32) return LLDB_INVALID_INDEX32; + } - // Add new or update existing watchpoint - if ((m_hwp_regs[wp_index].control & 1) == 0) { - // Update watchpoint in local cache - m_hwp_regs[wp_index].real_addr = real_addr; - m_hwp_regs[wp_index].address = addr; - m_hwp_regs[wp_index].control = control_value; - m_hwp_regs[wp_index].refcount = 1; - - // PTRACE call to set corresponding watchpoint register. - error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index); - - if (error.Fail()) { - m_hwp_regs[wp_index].address = 0; - m_hwp_regs[wp_index].control &= ~1; - m_hwp_regs[wp_index].refcount = 0; - - return LLDB_INVALID_INDEX32; - } - } else - m_hwp_regs[wp_index].refcount++; + m_hwp_regs[wp_index].real_addr = real_addr; + m_hwp_regs[wp_index].refcount++; return wp_index; } @@ -628,36 +624,24 @@ if (wp_index >= m_max_hwp_supported) return false; - // Update reference count if multiple references. - if (m_hwp_regs[wp_index].refcount > 1) { - m_hwp_regs[wp_index].refcount--; - return true; - } else if (m_hwp_regs[wp_index].refcount == 1) { - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; - uint32_t tempControl = m_hwp_regs[wp_index].control; - uint32_t tempRefCount = m_hwp_regs[wp_index].refcount; - - // Update watchpoint in local cache - m_hwp_regs[wp_index].control &= ~1; - m_hwp_regs[wp_index].address = 0; - m_hwp_regs[wp_index].refcount = 0; + // Create a backup we can revert to in case of failure. + uint32_t tempControl = m_hwp_regs[wp_index].control; - // Ptrace call to update hardware debug registers - error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index); + // Update watchpoint in local cache + m_hwp_regs[wp_index].control &= ~1; + m_hwp_regs[wp_index].refcount--; - if (error.Fail()) { - m_hwp_regs[wp_index].control = tempControl; - m_hwp_regs[wp_index].address = tempAddr; - m_hwp_regs[wp_index].refcount = tempRefCount; + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index); - return false; - } + if (error.Fail()) { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].refcount++; - return true; + return false; } - return false; + return true; } Error NativeRegisterContextLinux_arm::ClearAllHardwareWatchpoints() { @@ -685,7 +669,7 @@ tempRefCount = m_hwp_regs[i].refcount; // Clear watchpoints in local cache - m_hwp_regs[i].control &= ~1; + m_hwp_regs[i].control = 0; m_hwp_regs[i].address = 0; m_hwp_regs[i].refcount = 0; @@ -700,6 +684,11 @@ return error; } } + else { + m_hwp_regs[i].control = 0; + m_hwp_regs[i].address = 0; + m_hwp_regs[i].refcount = 0; + } } return Error(); @@ -750,8 +739,8 @@ watch_size = GetWatchpointSize(wp_index); watch_addr = m_hwp_regs[wp_index].address; - if (m_hwp_regs[wp_index].refcount >= 1 && WatchpointIsEnabled(wp_index) && - trap_addr >= watch_addr && trap_addr < watch_addr + watch_size) { + if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && + trap_addr < watch_addr + watch_size) { m_hwp_regs[wp_index].hit_addr = trap_addr; return Error(); } Index: source/Plugins/Process/Linux/NativeThreadLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeThreadLinux.h +++ source/Plugins/Process/Linux/NativeThreadLinux.h @@ -108,6 +108,7 @@ using WatchpointIndexMap = std::map; WatchpointIndexMap m_watchpoint_index_map; cpu_set_t m_original_cpu_set; // For single-step workaround. + bool m_invalidate_watchpoints; }; typedef std::shared_ptr NativeThreadLinuxSP; Index: source/Plugins/Process/Linux/NativeThreadLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeThreadLinux.cpp +++ source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -87,7 +87,8 @@ NativeThreadLinux::NativeThreadLinux(NativeProcessLinux *process, lldb::tid_t tid) : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), - m_stop_info(), m_reg_context_sp(), m_stop_description() {} + m_stop_info(), m_reg_context_sp(), m_stop_description(), + m_invalidate_watchpoints(false) {} std::string NativeThreadLinux::GetName() { NativeProcessProtocolSP process_sp = m_process_wp.lock(); @@ -185,8 +186,10 @@ return Error(); uint32_t wp_index = wp->second; m_watchpoint_index_map.erase(wp); - if (GetRegisterContext()->ClearHardwareWatchpoint(wp_index)) + if (GetRegisterContext()->ClearHardwareWatchpoint(wp_index)) { + m_invalidate_watchpoints = true; return Error(); + } return Error("Clearing hardware watchpoint failed."); } @@ -198,8 +201,13 @@ m_stop_info.reason = StopReason::eStopReasonNone; m_stop_description.clear(); - // If watchpoints have been set, but none on this thread, - // then this is a new thread. So set all existing watchpoints. + // Invalidate watchpoint index map for a re-sync + if (m_invalidate_watchpoints) { + m_invalidate_watchpoints = false; + m_watchpoint_index_map.clear(); + } + + // Re-sync all available watchpoints. if (m_watchpoint_index_map.empty()) { NativeProcessLinux &process = GetProcess();