summaryrefslogtreecommitdiffstats
path: root/lldb/source/Plugins/Process/Linux
diff options
context:
space:
mode:
authorChaoren Lin <chaorenl@google.com>2015-02-03 01:51:47 +0000
committerChaoren Lin <chaorenl@google.com>2015-02-03 01:51:47 +0000
commit18fe6404f9bc379045f4bce801f1c7113cddc6a0 (patch)
treeefb591cfed07504ecae67514806e536c98cc8153 /lldb/source/Plugins/Process/Linux
parent2fe1d0abc2434c359c3761910cebceb00a5743bf (diff)
downloadbcm5719-llvm-18fe6404f9bc379045f4bce801f1c7113cddc6a0.tar.gz
bcm5719-llvm-18fe6404f9bc379045f4bce801f1c7113cddc6a0.zip
Implement setting and clearing watchpoints.
llvm-svn: 227930
Diffstat (limited to 'lldb/source/Plugins/Process/Linux')
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp2
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp167
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h26
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp99
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeThreadLinux.h10
5 files changed, 291 insertions, 13 deletions
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index 5ff30bd93a6..01eb37be808 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -2316,7 +2316,7 @@ NativeProcessLinux::MonitorSIGTRAP(const siginfo_t *info, lldb::pid_t pid)
// Mark the thread as stopped at watchpoint.
// The address is at (lldb::addr_t)info->si_addr if we need it.
if (thread_sp)
- reinterpret_cast<NativeThreadLinux*> (thread_sp.get ())->SetStoppedBySignal (SIGTRAP);
+ reinterpret_cast<NativeThreadLinux*> (thread_sp.get ())->SetStoppedByWatchpoint ();
else
{
if (log)
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
index 649dfb238be..80df3265099 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
@@ -1032,3 +1032,170 @@ NativeRegisterContextLinux_x86_64::WriteGPR()
return process_p->WriteGPR (m_thread.GetID (), &m_gpr_x86_64, GetRegisterInfoInterface ().GetGPRSize ()).Success();
}
+Error
+NativeRegisterContextLinux_x86_64::IsWatchpointHit(uint8_t wp_index)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Error ("Watchpoint index out of range");
+
+ RegisterValue reg_value;
+ Error error = ReadRegisterRaw(lldb_dr6_x86_64, reg_value);
+ if (error.Fail()) return error;
+
+ uint64_t status_bits = reg_value.GetAsUInt64();
+
+ bool is_hit = status_bits & (1 << wp_index);
+
+ error.SetError (!is_hit, lldb::eErrorTypeInvalid);
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_x86_64::IsWatchpointVacant(uint32_t wp_index)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Error ("Watchpoint index out of range");
+
+ RegisterValue reg_value;
+ Error error = ReadRegisterRaw(lldb_dr7_x86_64, reg_value);
+ if (error.Fail()) return error;
+
+ uint64_t control_bits = reg_value.GetAsUInt64();
+
+ bool is_vacant = !(control_bits & (1 << (2 * wp_index)));
+
+ error.SetError (!is_vacant, lldb::eErrorTypeInvalid);
+
+ return error;
+}
+
+Error
+NativeRegisterContextLinux_x86_64::SetHardwareWatchpointWithIndex(
+ lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
+
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Error ("Watchpoint index out of range");
+
+ if (watch_flags != 0x1 && watch_flags != 0x3)
+ return Error ("Invalid read/write bits for watchpoint");
+
+ if (size != 1 && size != 2 && size != 4 && size != 8)
+ return Error ("Invalid size for watchpoint");
+
+ Error error = IsWatchpointVacant (wp_index);
+ if (error.Fail()) return error;
+
+ RegisterValue reg_value;
+ error = ReadRegisterRaw(lldb_dr7_x86_64, reg_value);
+ if (error.Fail()) return error;
+
+ // for watchpoints 0, 1, 2, or 3, respectively,
+ // set bits 1, 3, 5, or 7
+ uint64_t enable_bit = 1 << (2 * wp_index);
+
+ // set bits 16-17, 20-21, 24-25, or 28-29
+ // with 0b01 for write, and 0b11 for read/write
+ uint64_t rw_bits = watch_flags << (16 + 4 * wp_index);
+
+ // set bits 18-19, 22-23, 26-27, or 30-31
+ // with 0b00, 0b01, 0b10, or 0b11
+ // for 1, 2, 8 (if supported), or 4 bytes, respectively
+ uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index);
+
+ uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
+
+ uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+
+ control_bits |= enable_bit | rw_bits | size_bits;
+
+ error = WriteRegister(m_reg_info.first_dr + wp_index, RegisterValue(addr));
+ if (error.Fail()) return error;
+
+ error = WriteRegister(lldb_dr7_x86_64, RegisterValue(control_bits));
+ if (error.Fail()) return error;
+
+ error.Clear();
+ return error;
+}
+
+bool
+NativeRegisterContextLinux_x86_64::ClearHardwareWatchpoint(uint32_t wp_index)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return false;
+
+ RegisterValue reg_value;
+
+ // for watchpoints 0, 1, 2, or 3, respectively,
+ // clear bits 0, 1, 2, or 3 of the debug status register (DR6)
+ Error error = ReadRegisterRaw(lldb_dr6_x86_64, reg_value);
+ if (error.Fail()) return false;
+ uint64_t bit_mask = 1 << wp_index;
+ uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ error = WriteRegister(lldb_dr6_x86_64, RegisterValue(status_bits));
+ if (error.Fail()) return false;
+
+ // for watchpoints 0, 1, 2, or 3, respectively,
+ // clear bits {0-1,16-19}, {2-3,20-23}, {4-5,24-27}, or {6-7,28-31}
+ // of the debug control register (DR7)
+ error = ReadRegisterRaw(lldb_dr7_x86_64, reg_value);
+ if (error.Fail()) return false;
+ bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
+ uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ return WriteRegister(lldb_dr7_x86_64, RegisterValue(control_bits)).Success();
+}
+
+Error
+NativeRegisterContextLinux_x86_64::ClearAllHardwareWatchpoints()
+{
+ RegisterValue reg_value;
+
+ // clear bits {0-4} of the debug status register (DR6)
+ Error error = ReadRegisterRaw(lldb_dr6_x86_64, reg_value);
+ if (error.Fail()) return error;
+ uint64_t bit_mask = 0xF;
+ uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ error = WriteRegister(lldb_dr6_x86_64, RegisterValue(status_bits));
+ if (error.Fail()) return error;
+
+ // clear bits {0-7,16-31} of the debug control register (DR7)
+ error = ReadRegisterRaw(lldb_dr7_x86_64, reg_value);
+ if (error.Fail()) return error;
+ bit_mask = 0xFF | (0xFFFF << 16);
+ uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ return WriteRegister(lldb_dr7_x86_64, RegisterValue(control_bits));
+}
+
+uint32_t
+NativeRegisterContextLinux_x86_64::SetHardwareWatchpoint(
+ lldb::addr_t addr, size_t size, uint32_t watch_flags)
+{
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index)
+ if (IsWatchpointVacant(wp_index).Success())
+ {
+ if (SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index).Fail())
+ continue;
+ return wp_index;
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+lldb::addr_t
+NativeRegisterContextLinux_x86_64::GetWatchpointAddress(uint32_t wp_index)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return LLDB_INVALID_ADDRESS;
+ RegisterValue reg_value;
+ if (ReadRegisterRaw(m_reg_info.first_dr + wp_index, reg_value).Fail())
+ return LLDB_INVALID_ADDRESS;
+ return reg_value.GetAsUInt64();
+}
+
+uint32_t
+NativeRegisterContextLinux_x86_64::NumSupportedHardwareWatchpoints ()
+{
+ // Available debug address registers: dr0, dr1, dr2, dr3
+ return 4;
+}
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h
index e624cc1c224..7c9d3528a83 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h
@@ -42,6 +42,32 @@ namespace lldb_private
Error
WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override;
+ Error
+ IsWatchpointHit(uint8_t wp_index);
+
+ Error
+ IsWatchpointVacant(uint32_t wp_index);
+
+ bool
+ ClearHardwareWatchpoint(uint32_t wp_index);
+
+ Error
+ ClearAllHardwareWatchpoints ();
+
+ Error
+ SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags, uint32_t wp_index);
+
+ uint32_t
+ SetHardwareWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags);
+
+ lldb::addr_t
+ GetWatchpointAddress(uint32_t wp_index);
+
+ uint32_t
+ NumSupportedHardwareWatchpoints();
+
private:
// Private member types.
diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
index 66af7deed4a..daa3f3ec18f 100644
--- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
@@ -10,6 +10,7 @@
#include "NativeThreadLinux.h"
#include <signal.h>
+#include <sstream>
#include "NativeProcessLinux.h"
#include "NativeRegisterContextLinux_x86_64.h"
@@ -101,8 +102,15 @@ NativeThreadLinux::GetStopReason (ThreadStopInfo &stop_info, std::string& descri
if (log)
LogThreadStopInfo (*log, m_stop_info, "m_stop_info in thread:");
stop_info = m_stop_info;
- if (m_stop_info.reason == StopReason::eStopReasonException)
- description = m_stop_description;
+ switch (m_stop_info.reason)
+ {
+ case StopReason::eStopReasonException:
+ case StopReason::eStopReasonBreakpoint:
+ case StopReason::eStopReasonWatchpoint:
+ description = m_stop_description;
+ default:
+ break;
+ }
if (log)
LogThreadStopInfo (*log, stop_info, "returned stop_info:");
@@ -209,15 +217,30 @@ NativeThreadLinux::GetRegisterContext ()
Error
NativeThreadLinux::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware)
{
- // TODO implement
- return Error ("not implemented");
+ if (!hardware)
+ return Error ("not implemented");
+ Error error = RemoveWatchpoint(addr);
+ if (error.Fail()) return error;
+ NativeRegisterContextSP reg_ctx = GetRegisterContext ();
+ uint32_t wp_index =
+ reg_ctx->SetHardwareWatchpoint (addr, size, watch_flags);
+ if (wp_index == LLDB_INVALID_INDEX32)
+ return Error ("Setting hardware watchpoint failed.");
+ m_watchpoint_index_map.insert({addr, wp_index});
+ return Error ();
}
Error
NativeThreadLinux::RemoveWatchpoint (lldb::addr_t addr)
{
- // TODO implement
- return Error ("not implemented");
+ auto wp = m_watchpoint_index_map.find(addr);
+ if (wp == m_watchpoint_index_map.end())
+ return Error ();
+ uint32_t wp_index = wp->second;
+ m_watchpoint_index_map.erase(wp);
+ if (GetRegisterContext()->ClearHardwareWatchpoint(wp_index))
+ return Error ();
+ return Error ("Clearing hardware watchpoint failed.");
}
void
@@ -243,6 +266,20 @@ NativeThreadLinux::SetRunning ()
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.
+ if (m_watchpoint_index_map.empty())
+ {
+ const auto &watchpoint_map = GetProcess()->GetWatchpointMap();
+ if (watchpoint_map.empty()) return;
+ GetRegisterContext()->ClearAllHardwareWatchpoints();
+ for (const auto &pair : watchpoint_map)
+ {
+ const auto& wp = pair.second;
+ SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware);
+ }
+ }
}
void
@@ -313,18 +350,56 @@ NativeThreadLinux::SetStoppedByBreakpoint ()
m_stop_info.reason = StopReason::eStopReasonBreakpoint;
m_stop_info.details.signal.signo = SIGTRAP;
+ m_stop_description.clear();
+}
+
+void
+NativeThreadLinux::SetStoppedByWatchpoint ()
+{
+ const StateType new_state = StateType::eStateStopped;
+ MaybeLogStateChange (new_state);
+ m_state = new_state;
+
+ m_stop_info.reason = StopReason::eStopReasonWatchpoint;
+ m_stop_info.details.signal.signo = SIGTRAP;
+
+ NativeRegisterContextLinux_x86_64 *reg_ctx =
+ reinterpret_cast<NativeRegisterContextLinux_x86_64*> (GetRegisterContext().get());
+ const uint32_t num_hw_watchpoints =
+ reg_ctx->NumSupportedHardwareWatchpoints();
+
+ m_stop_description.clear ();
+ for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index)
+ if (reg_ctx->IsWatchpointHit(wp_index).Success())
+ {
+ std::ostringstream ostr;
+ ostr << reg_ctx->GetWatchpointAddress(wp_index) << " " << wp_index;
+ m_stop_description = ostr.str();
+ return;
+ }
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+ if (log)
+ {
+ NativeProcessProtocolSP m_process_sp = m_process_wp.lock ();
+ lldb::pid_t pid = m_process_sp ? m_process_sp->GetID () : LLDB_INVALID_PROCESS_ID;
+ log->Printf ("NativeThreadLinux: thread (pid=%" PRIu64 ", tid=%" PRIu64 ") "
+ "stopped by a watchpoint, but failed to find it",
+ pid, GetID ());
+ }
}
bool
NativeThreadLinux::IsStoppedAtBreakpoint ()
{
- // Are we stopped? If not, this can't be a breakpoint.
- if (GetState () != StateType::eStateStopped)
- return false;
+ return GetState () == StateType::eStateStopped &&
+ m_stop_info.reason == StopReason::eStopReasonBreakpoint;
+}
- // Was the stop reason a signal with signal number SIGTRAP? If not, not a breakpoint.
- return (m_stop_info.reason == StopReason::eStopReasonBreakpoint) &&
- (m_stop_info.details.signal.signo == SIGTRAP);
+bool
+NativeThreadLinux::IsStoppedAtWatchpoint ()
+{
+ return GetState () == StateType::eStateStopped &&
+ m_stop_info.reason == StopReason::eStopReasonWatchpoint;
}
void
diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
index 7233b30237b..2519b5b1da0 100644
--- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
+++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
@@ -13,6 +13,8 @@
#include "lldb/lldb-private-forward.h"
#include "lldb/Host/common/NativeThreadProtocol.h"
+#include <map>
+
namespace lldb_private
{
class NativeProcessLinux;
@@ -73,9 +75,15 @@ namespace lldb_private
void
SetStoppedByBreakpoint ();
+ void
+ SetStoppedByWatchpoint ();
+
bool
IsStoppedAtBreakpoint ();
+ bool
+ IsStoppedAtWatchpoint ();
+
void
SetStoppedByTrace ();
@@ -101,6 +109,8 @@ namespace lldb_private
ThreadStopInfo m_stop_info;
NativeRegisterContextSP m_reg_context_sp;
std::string m_stop_description;
+ using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>;
+ WatchpointIndexMap m_watchpoint_index_map;
};
}
OpenPOWER on IntegriCloud