<rdar://problem/11782789> Changes to the watchpoint implementation on ARM so that we single-step before stopping at the WP. This is necessary because on ARM the WP triggers before the opcode is actually executed, so we would be unable to continue since we would keep hitting the WP. We work around this by disabling the WP, single stepping and then putting the WP back in place.

llvm-svn: 160199
This commit is contained in:
Enrico Granata 2012-07-13 23:18:48 +00:00
parent 7298ea1871
commit f04a21917c
9 changed files with 105 additions and 5 deletions

View File

@ -2786,6 +2786,14 @@ public:
return error; return error;
} }
virtual Error
GetWatchpointSupportInfo (uint32_t &num, bool& after)
{
Error error;
error.SetErrorString ("Process::GetWatchpointSupportInfo() not supported");
return error;
}
lldb::ModuleSP lldb::ModuleSP
ReadModuleFromMemory (const FileSpec& file_spec, ReadModuleFromMemory (const FileSpec& file_spec,
lldb::addr_t header_addr, lldb::addr_t header_addr,

View File

@ -161,14 +161,15 @@ protected:
// N.B. running to evaluate a user expression does not count. // N.B. running to evaluate a user expression does not count.
bool HasTargetRunSinceMe (); bool HasTargetRunSinceMe ();
private: // MakeStopInfoValid is necessary to allow saved stop infos to resurrect themselves as valid.
friend class Thread; // It should only be used by Thread::RestoreThreadStateFromCheckpoint and to make sure the one-step
// needed for before-the-fact watchpoints does not prevent us from stopping
// MakeStopInfoValid is necessary to allow saved stop infos to resurrect themselves as valid. It should
// only need to be called by Thread::RestoreThreadStateFromCheckpoint.
void void
MakeStopInfoValid (); MakeStopInfoValid ();
private:
friend class Thread;
DISALLOW_COPY_AND_ASSIGN (StopInfo); DISALLOW_COPY_AND_ASSIGN (StopInfo);
}; };

View File

@ -49,6 +49,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) :
m_supports_alloc_dealloc_memory (eLazyBoolCalculate), m_supports_alloc_dealloc_memory (eLazyBoolCalculate),
m_supports_memory_region_info (eLazyBoolCalculate), m_supports_memory_region_info (eLazyBoolCalculate),
m_supports_watchpoint_support_info (eLazyBoolCalculate), m_supports_watchpoint_support_info (eLazyBoolCalculate),
m_watchpoints_trigger_after_instruction(eLazyBoolCalculate),
m_supports_qProcessInfoPID (true), m_supports_qProcessInfoPID (true),
m_supports_qfProcessInfo (true), m_supports_qfProcessInfo (true),
m_supports_qUserName (true), m_supports_qUserName (true),
@ -1019,6 +1020,17 @@ GDBRemoteCommunicationClient::GetHostInfo (bool force)
if (m_os_version_major != UINT32_MAX) if (m_os_version_major != UINT32_MAX)
++num_keys_decoded; ++num_keys_decoded;
} }
else if (name.compare("watchpoint_exceptions_received") == 0)
{
++num_keys_decoded;
if (strcmp(value.c_str(),"before") == 0)
m_watchpoints_trigger_after_instruction = eLazyBoolNo;
else if (strcmp(value.c_str(),"after") == 0)
m_watchpoints_trigger_after_instruction = eLazyBoolYes;
else
--num_keys_decoded;
}
} }
if (num_keys_decoded > 0) if (num_keys_decoded > 0)
@ -1352,6 +1364,30 @@ GDBRemoteCommunicationClient::GetWatchpointSupportInfo (uint32_t &num)
} }
lldb_private::Error
GDBRemoteCommunicationClient::GetWatchpointSupportInfo (uint32_t &num, bool& after)
{
Error error(GetWatchpointSupportInfo(num));
if (error.Success())
error = GetWatchpointsTriggerAfterInstruction(after);
return error;
}
lldb_private::Error
GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction (bool &after)
{
Error error;
// we assume watchpoints will happen after running the relevant opcode
// and we only want to override this behavior if we have explicitly
// received a qHostInfo telling us otherwise
if (m_qHostInfo_is_valid != eLazyBoolYes)
after = true;
else
after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo);
return error;
}
int int
GDBRemoteCommunicationClient::SetSTDIN (char const *path) GDBRemoteCommunicationClient::SetSTDIN (char const *path)
{ {

View File

@ -209,6 +209,12 @@ public:
lldb_private::Error lldb_private::Error
GetWatchpointSupportInfo (uint32_t &num); GetWatchpointSupportInfo (uint32_t &num);
lldb_private::Error
GetWatchpointSupportInfo (uint32_t &num, bool& after);
lldb_private::Error
GetWatchpointsTriggerAfterInstruction (bool &after);
const lldb_private::ArchSpec & const lldb_private::ArchSpec &
GetHostArchitecture (); GetHostArchitecture ();
@ -358,6 +364,7 @@ protected:
lldb_private::LazyBool m_supports_alloc_dealloc_memory; lldb_private::LazyBool m_supports_alloc_dealloc_memory;
lldb_private::LazyBool m_supports_memory_region_info; lldb_private::LazyBool m_supports_memory_region_info;
lldb_private::LazyBool m_supports_watchpoint_support_info; lldb_private::LazyBool m_supports_watchpoint_support_info;
lldb_private::LazyBool m_watchpoints_trigger_after_instruction;
bool bool
m_supports_qProcessInfoPID:1, m_supports_qProcessInfoPID:1,

View File

@ -223,6 +223,11 @@ GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet
if (sub != LLDB_INVALID_CPUTYPE) if (sub != LLDB_INVALID_CPUTYPE)
response.Printf ("cpusubtype:%u;", sub); response.Printf ("cpusubtype:%u;", sub);
if (cpu == CPU_TYPE_ARM)
response.Printf("watchpoint_exceptions_received:before;"); // On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes.
else
response.Printf("watchpoint_exceptions_received:after;");
switch (lldb::endian::InlHostByteOrder()) switch (lldb::endian::InlHostByteOrder())
{ {
case eByteOrderBig: response.PutCString ("endian:big;"); break; case eByteOrderBig: response.PutCString ("endian:big;"); break;

View File

@ -2014,6 +2014,13 @@ ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num)
return error; return error;
} }
Error
ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num, bool& after)
{
Error error (m_gdb_comm.GetWatchpointSupportInfo (num, after));
return error;
}
Error Error
ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr) ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr)
{ {

View File

@ -207,6 +207,9 @@ public:
virtual lldb_private::Error virtual lldb_private::Error
GetWatchpointSupportInfo (uint32_t &num); GetWatchpointSupportInfo (uint32_t &num);
virtual lldb_private::Error
GetWatchpointSupportInfo (uint32_t &num, bool& after);
virtual bool virtual bool
StartNoticingNewThreads(); StartNoticingNewThreads();

View File

@ -461,6 +461,36 @@ public:
if (wp_sp) if (wp_sp)
{ {
ExecutionContext exe_ctx (m_thread.GetStackFrameAtIndex(0)); ExecutionContext exe_ctx (m_thread.GetStackFrameAtIndex(0));
{
// check if this process is running on an architecture where watchpoints trigger
// before the associated instruction runs. if so, disable the WP, single-step and then
// re-enable the watchpoint
Process* process = exe_ctx.GetProcessPtr();
if (process)
{
uint32_t num; bool wp_triggers_after;
if (process->GetWatchpointSupportInfo(num, wp_triggers_after).Success())
{
if (!wp_triggers_after)
{
process->DisableWatchpoint(wp_sp.get());
ThreadPlan *new_plan = m_thread.QueueThreadPlanForStepSingleInstruction(false, // step-over
false, // abort_other_plans
true); // stop_other_threads
new_plan->SetIsMasterPlan (true);
new_plan->SetOkayToDiscard (false);
process->GetThreadList().SetSelectedThreadByID (m_thread.GetID());
process->Resume ();
process->WaitForProcessToStop (NULL);
process->GetThreadList().SetSelectedThreadByID (m_thread.GetID());
MakeStopInfoValid(); // make sure we do not fail to stop because of the single-step taken above
process->EnableWatchpoint(wp_sp.get());
}
}
}
}
StoppointCallbackContext context (event_ptr, exe_ctx, false); StoppointCallbackContext context (event_ptr, exe_ctx, false);
bool stop_requested = wp_sp->InvokeCallback (&context); bool stop_requested = wp_sp->InvokeCallback (&context);
// Also make sure that the callback hasn't continued the target. // Also make sure that the callback hasn't continued the target.

View File

@ -3587,10 +3587,13 @@ RNBRemote::HandlePacket_qHostInfo (const char *p)
if (cputype == CPU_TYPE_ARM) if (cputype == CPU_TYPE_ARM)
{ {
strm << "ostype:ios;"; strm << "ostype:ios;";
// On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes.
strm << "watchpoint_exceptions_received:before;";
} }
else else
{ {
strm << "ostype:macosx;"; strm << "ostype:macosx;";
strm << "watchpoint_exceptions_received:after;";
} }
// char ostype[64]; // char ostype[64];
// len = sizeof(ostype); // len = sizeof(ostype);