Index: include/lldb/lldb-enumerations.h =================================================================== --- include/lldb/lldb-enumerations.h +++ include/lldb/lldb-enumerations.h @@ -934,8 +934,8 @@ // Indicates what types of events cause the watchpoint to fire. // Used by Native*Protocol-related classes. //---------------------------------------------------------------------- -FLAGS_ENUM(WatchpointKind){eWatchpointKindRead = (1u << 0), - eWatchpointKindWrite = (1u << 1)}; + FLAGS_ENUM(WatchpointKind){eWatchpointKindWrite = (1u << 0), + eWatchpointKindRead = (1u << 1)}; enum GdbSignal { eGdbSignalBadAccess = 0x91, Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_ppc64le.h =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_ppc64le.h +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_ppc64le.h @@ -1,4 +1,4 @@ -//===-- NativeRegisterContextLinux_ppc64le.h ---------------------*- C++ -*-===// +//===-- NativeRegisterContextLinux_ppc64le.h --------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -49,6 +49,28 @@ Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + //------------------------------------------------------------------ + // Hardware watchpoint mangement functions + //------------------------------------------------------------------ + + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t GetWatchpointSize(uint32_t wp_index); + + bool WatchpointIsEnabled(uint32_t wp_index); + protected: Status DoReadGPR(void *buf, size_t buf_size) override; @@ -60,6 +82,29 @@ GPR m_gpr_ppc64le; // 64-bit general purpose registers. bool IsGPR(unsigned reg) const; + + Status ReadHardwareDebugInfo(); + + Status WriteHardwareDebugRegs(); + + // Debug register info for hardware watchpoints management. + struct DREG { + lldb::addr_t address; // Breakpoint/watchpoint address value. + lldb::addr_t hit_addr; // Address at which last watchpoint trigger + // exception occurred. + lldb::addr_t real_addr; // Address value that should cause target to stop. + uint32_t control; // Breakpoint/watchpoint control value. + uint32_t refcount; // Serves as enable/disable and reference counter. + long slot; // Saves the value returned from PTRACE_SETHWDEBUG. + int mode; // Defines if watchpoint is read/write/access. + }; + + std::array m_hwp_regs; + + // 16 is just a maximum value, query hardware for actual watchpoint count + uint32_t m_max_hwp_supported = 16; + uint32_t m_max_hbp_supported = 16; + bool m_refresh_hwdebug_info = true; }; } // namespace process_linux Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_ppc64le.cpp =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_ppc64le.cpp +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_ppc64le.cpp @@ -54,7 +54,13 @@ namespace { // Number of register sets provided by this context. -enum { k_num_register_sets = 1 }; + enum { k_num_register_sets = 1 }; + + enum watch_mode { + write_mode = 1, + read_mode = 2, + access_mode = 3 + }; } static const RegisterSet g_reg_sets_ppc64le[k_num_register_sets] = { @@ -85,6 +91,7 @@ } ::memset(&m_gpr_ppc64le, 0, sizeof(m_gpr_ppc64le)); + ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); } uint32_t NativeRegisterContextLinux_ppc64le::GetRegisterSetCount() const { @@ -242,4 +249,262 @@ ®set, buf, buf_size); } +uint32_t NativeRegisterContextLinux_ppc64le::NumSupportedHardwareWatchpoints() { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return 0; + + LLDB_LOG(log, "{0}", m_max_hwp_supported); + return m_max_hwp_supported; +} + +uint32_t NativeRegisterContextLinux_ppc64le::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); + LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size, + watch_flags); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + uint32_t control_value = 0, wp_index = 0; + lldb::addr_t real_addr = addr; + uint32_t rw_mode = 0; + + // Check if we are setting watchpoint other than read/write/access + // Update watchpoint flag to match ppc64le write-read bit configuration. + switch (watch_flags) { + case eWatchpointKindWrite: + rw_mode = PPC_BREAKPOINT_TRIGGER_WRITE; + watch_flags = 2; + break; + case eWatchpointKindRead: + rw_mode = PPC_BREAKPOINT_TRIGGER_READ; + watch_flags = 1; + break; + case (eWatchpointKindRead | eWatchpointKindWrite): + rw_mode = PPC_BREAKPOINT_TRIGGER_RW; + break; + default: + return LLDB_INVALID_INDEX32; + } + + // Check if size has a valid hardware watchpoint length. + if (size != 1 && size != 2 && size != 4 && size != 8) + return LLDB_INVALID_INDEX32; + + // Check 8-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 8-byte alligned addresses as well. + if (addr & 0x07) { + + addr_t begin = llvm::alignDown(addr, 8); + addr_t end = llvm::alignTo(addr+size, 8); + size = llvm::PowerOf2Ceil(end-begin); + + addr = addr & (~0x07); + } + + // Setup control value + control_value = watch_flags << 3; + control_value |= ((1 << size) - 1) << 5; + control_value |= (2 << 1) | 1; + + // Iterate over stored watchpoints and find a free wp_index + 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) { + return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints. + } + } + + if (wp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // 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].mode = rw_mode; + + // PTRACE call to set corresponding watchpoint register. + error = WriteHardwareDebugRegs(); + + if (error.Fail()) { + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].control &= llvm::maskTrailingZeros(1); + + return LLDB_INVALID_INDEX32; + } + + return wp_index; +} + +bool NativeRegisterContextLinux_ppc64le::ClearHardwareWatchpoint( + uint32_t wp_index) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return false; + + if (wp_index >= m_max_hwp_supported) + return false; + + // 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; + long * tempSlot = reinterpret_cast(m_hwp_regs[wp_index].slot); + + // Update watchpoint in local cache + m_hwp_regs[wp_index].control &= llvm::maskTrailingZeros(1); + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].slot = 0; + m_hwp_regs[wp_index].mode = 0; + + // Ptrace call to update hardware debug registers + error = NativeProcessLinux::PtraceWrapper(PPC_PTRACE_DELHWDEBUG, + m_thread.GetID(), 0, tempSlot); + + if (error.Fail()) { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].address = tempAddr; + m_hwp_regs[wp_index].slot = reinterpret_cast(tempSlot); + + return false; + } + + return true; +} + +uint32_t +NativeRegisterContextLinux_ppc64le::GetWatchpointSize(uint32_t wp_index) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + unsigned control = (m_hwp_regs[wp_index].control >> 5) & 0xff; + assert(llvm::isPowerOf2_32(control+1)); + return llvm::countPopulation(control); +} + +bool NativeRegisterContextLinux_ppc64le::WatchpointIsEnabled( + uint32_t wp_index) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + return !!((m_hwp_regs[wp_index].control & 0x1) == 0x1); +} + +Status NativeRegisterContextLinux_ppc64le::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); + LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr); + + uint32_t watch_size; + lldb::addr_t watch_addr; + + for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { + watch_size = GetWatchpointSize(wp_index); + watch_addr = m_hwp_regs[wp_index].address; + + if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && + trap_addr <= watch_addr + watch_size) { + m_hwp_regs[wp_index].hit_addr = trap_addr; + return Status(); + } + } + + wp_index = LLDB_INVALID_INDEX32; + return Status(); +} + +lldb::addr_t +NativeRegisterContextLinux_ppc64le::GetWatchpointAddress(uint32_t wp_index) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].real_addr; + else + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t +NativeRegisterContextLinux_ppc64le::GetWatchpointHitAddress(uint32_t wp_index) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].hit_addr; + + return LLDB_INVALID_ADDRESS; +} + +Status NativeRegisterContextLinux_ppc64le::ReadHardwareDebugInfo() { + if (!m_refresh_hwdebug_info) { + return Status(); + } + + ::pid_t tid = m_thread.GetID(); + + struct ppc_debug_info hwdebug_info; + Status error; + + error = NativeProcessLinux::PtraceWrapper(PPC_PTRACE_GETHWDBGINFO, + tid, 0, &hwdebug_info, sizeof(hwdebug_info)); + + if (error.Fail()) + return error; + + m_max_hwp_supported = hwdebug_info.num_data_bps; + m_max_hbp_supported = hwdebug_info.num_instruction_bps; + m_refresh_hwdebug_info = false; + + return error; +} + +Status NativeRegisterContextLinux_ppc64le::WriteHardwareDebugRegs() { + struct ppc_hw_breakpoint reg_state; + Status error; + long ret; + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + reg_state.addr = m_hwp_regs[i].address; + reg_state.trigger_type = m_hwp_regs[i].mode; + reg_state.version = 1; + reg_state.addr_mode = PPC_BREAKPOINT_MODE_EXACT; + reg_state.condition_mode = PPC_BREAKPOINT_CONDITION_NONE; + reg_state.addr2 = 0; + reg_state.condition_value = 0; + + error = NativeProcessLinux::PtraceWrapper(PPC_PTRACE_SETHWDEBUG, + m_thread.GetID(), 0, ®_state, sizeof(reg_state), &ret); + + if (error.Fail()) + return error; + + m_hwp_regs[i].slot = ret; + } + + return error; +} + #endif // defined(__powerpc64__) Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -1601,21 +1601,24 @@ // and we only want to override this behavior if we have explicitly // received a qHostInfo telling us otherwise if (m_qHostInfo_is_valid != eLazyBoolYes) { - // On targets like MIPS, watchpoint exceptions are always generated - // before the instruction is executed. The connected target may not - // support qHostInfo or qWatchpointSupportInfo packets. + // On targets like MIPS and ppc64le, watchpoint exceptions are always + // generated before the instruction is executed. The connected target + // may not support qHostInfo or qWatchpointSupportInfo packets. if (atype == llvm::Triple::mips || atype == llvm::Triple::mipsel || - atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el) + atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el || + atype == llvm::Triple::ppc64le) after = false; else after = true; } else { - // For MIPS, set m_watchpoints_trigger_after_instruction to eLazyBoolNo - // if it is not calculated before. - if (m_watchpoints_trigger_after_instruction == eLazyBoolCalculate && + // For MIPS and ppc64le, set m_watchpoints_trigger_after_instruction to + // eLazyBoolNo if it is not calculated before. + if ((m_watchpoints_trigger_after_instruction == eLazyBoolCalculate && (atype == llvm::Triple::mips || atype == llvm::Triple::mipsel || - atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el)) + atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el)) || + atype == llvm::Triple::ppc64le) { m_watchpoints_trigger_after_instruction = eLazyBoolNo; + } after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo); }