forked from OSchip/llvm-project
Changed the formerly pure virtual function:
namespace lldb_private { class Thread { virtual lldb::StopInfoSP GetPrivateStopReason() = 0; }; } To not be virtual. The lldb_private::Thread now handles the correct caching and will call a new pure virtual function: namespace lldb_private { class Thread { virtual bool CalculateStopInfo() = 0; } } This function must be overridden by thead lldb_private::Thread subclass and the only thing it needs to do is to set the Thread::StopInfo() with the current stop reason and return true, or return false if there is no stop reason. The lldb_private::Thread class will take care of calling this function only when it is required. This allows lldb_private::Thread subclasses to be a bit simpler and not all need to duplicate the cache and invalidation settings. Also renamed: lldb::StopInfoSP lldb_private::Thread::GetPrivateStopReason(); To: lldb::StopInfoSP lldb_private::Thread::GetPrivateStopInfo(); Also cleaned up a case where the ThreadPlanStepOverBreakpoint might not re-set its breakpoint if the thread disappears (which was happening due to a bug when using the OperatingSystem plug-ins with memory threads and real threads). llvm-svn: 181501
This commit is contained in:
parent
083fcdb414
commit
6e0ff1a3cb
|
@ -848,13 +848,7 @@ public:
|
|||
virtual lldb::user_id_t
|
||||
GetProtocolID () const
|
||||
{
|
||||
return m_protocol_tid.GetID();
|
||||
}
|
||||
|
||||
virtual void
|
||||
SetProtocolID (lldb::user_id_t api_tid)
|
||||
{
|
||||
return m_protocol_tid.SetID(api_tid);
|
||||
return GetID();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
@ -905,7 +899,19 @@ public:
|
|||
// valid. The stop info may be a "checkpointed and restored" stop info, so if it is still around it is right
|
||||
// even if you have not calculated this yourself, or if it disagrees with what you might have calculated.
|
||||
virtual lldb::StopInfoSP
|
||||
GetPrivateStopReason () = 0;
|
||||
GetPrivateStopInfo ();
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Ask the thread subclass to set its stop info.
|
||||
//
|
||||
// Thread subclasses should call Thread::SetStopInfo(...) with the
|
||||
// reason the thread stopped.
|
||||
//
|
||||
// @return
|
||||
// True if Thread::SetStopInfo(...) was called, false otherwise.
|
||||
//----------------------------------------------------------------------
|
||||
virtual bool
|
||||
CalculateStopInfo () = 0;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Gets the temporary resume state for a thread.
|
||||
|
@ -1002,9 +1008,10 @@ protected:
|
|||
// Classes that inherit from Process can see and modify these
|
||||
//------------------------------------------------------------------
|
||||
lldb::ProcessWP m_process_wp; ///< The process that owns this thread.
|
||||
lldb::StopInfoSP m_actual_stop_info_sp; ///< The private stop reason for this thread
|
||||
lldb::StopInfoSP m_stop_info_sp; ///< The private stop reason for this thread
|
||||
uint32_t m_stop_info_stop_id; // This is the stop id for which the StopInfo is valid. Can use this so you know that
|
||||
// the thread's m_stop_info_sp is current and you don't have to fetch it again
|
||||
const uint32_t m_index_id; ///< A unique 1 based index assigned to each thread for easy UI/command line access.
|
||||
UserID m_protocol_tid; ///< The thread ID used in the system level debugging or protocol functions calls. This is usually the same as the Thread::GetID(), but not always.
|
||||
lldb::RegisterContextSP m_reg_context_sp; ///< The register context for this thread's current register state.
|
||||
lldb::StateType m_state; ///< The state of our process.
|
||||
mutable Mutex m_state_mutex; ///< Multithreaded protection for m_state.
|
||||
|
@ -1020,8 +1027,6 @@ protected:
|
|||
/// It gets set in Thread::ShoudResume.
|
||||
std::unique_ptr<lldb_private::Unwind> m_unwinder_ap;
|
||||
bool m_destroy_called; // This is used internally to make sure derived Thread classes call DestroyThread.
|
||||
uint32_t m_thread_stop_reason_stop_id; // This is the stop id for which the StopInfo is valid. Can use this so you know that
|
||||
// the thread's m_actual_stop_info_sp is current and you don't have to fetch it again
|
||||
LazyBool m_override_should_notify;
|
||||
private:
|
||||
//------------------------------------------------------------------
|
||||
|
|
|
@ -410,6 +410,13 @@ public:
|
|||
virtual bool
|
||||
MischiefManaged ();
|
||||
|
||||
virtual void
|
||||
ThreadDestroyed ()
|
||||
{
|
||||
// Any cleanup that a plan might want to do in case the thread goes away
|
||||
// in the middle of the plan being queued on a thread can be done here.
|
||||
}
|
||||
|
||||
bool
|
||||
GetPrivate ()
|
||||
{
|
||||
|
@ -537,7 +544,7 @@ protected:
|
|||
lldb::StopInfoSP
|
||||
GetPrivateStopReason()
|
||||
{
|
||||
return m_thread.GetPrivateStopReason ();
|
||||
return m_thread.GetPrivateStopInfo ();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
virtual lldb::StateType GetPlanRunState ();
|
||||
virtual bool WillStop ();
|
||||
virtual bool MischiefManaged ();
|
||||
virtual void ThreadDestroyed ();
|
||||
void SetAutoContinue (bool do_it);
|
||||
virtual bool ShouldAutoContinue(Event *event_ptr);
|
||||
|
||||
|
@ -39,11 +40,13 @@ protected:
|
|||
virtual bool DoPlanExplainsStop (Event *event_ptr);
|
||||
virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan);
|
||||
|
||||
void ReenableBreakpointSite ();
|
||||
private:
|
||||
|
||||
lldb::addr_t m_breakpoint_addr;
|
||||
lldb::user_id_t m_breakpoint_site_id;
|
||||
bool m_auto_continue;
|
||||
bool m_reenabled_breakpoint_site;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepOverBreakpoint);
|
||||
|
||||
|
|
|
@ -198,36 +198,39 @@ OperatingSystemPython::UpdateThreadList (ThreadList &old_thread_list,
|
|||
{
|
||||
uint32_t i;
|
||||
const uint32_t num_threads = threads_list.GetSize();
|
||||
for (i=0; i<num_threads; ++i)
|
||||
if (num_threads > 0)
|
||||
{
|
||||
PythonDictionary thread_dict(threads_list.GetItemAtIndex(i));
|
||||
if (thread_dict)
|
||||
for (i=0; i<num_threads; ++i)
|
||||
{
|
||||
if (thread_dict.GetItemForKey("core"))
|
||||
PythonDictionary thread_dict(threads_list.GetItemAtIndex(i));
|
||||
if (thread_dict)
|
||||
{
|
||||
// We have some threads that are saying they are on a "core", which means
|
||||
// they map the threads that are gotten from the lldb_private::Process subclass
|
||||
// so clear the new threads list so the core threads don't show up
|
||||
new_thread_list.Clear();
|
||||
break;
|
||||
if (thread_dict.GetItemForKey("core"))
|
||||
{
|
||||
// We have some threads that are saying they are on a "core", which means
|
||||
// they map the threads that are gotten from the lldb_private::Process subclass
|
||||
// so clear the new threads list so the core threads don't show up
|
||||
new_thread_list.Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i=0; i<num_threads; ++i)
|
||||
{
|
||||
PythonDictionary thread_dict(threads_list.GetItemAtIndex(i));
|
||||
if (thread_dict)
|
||||
{
|
||||
ThreadSP thread_sp (CreateThreadFromThreadInfo (thread_dict, core_thread_list, old_thread_list, NULL));
|
||||
if (thread_sp)
|
||||
new_thread_list.AddThread(thread_sp);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i=0; i<num_threads; ++i)
|
||||
{
|
||||
PythonDictionary thread_dict(threads_list.GetItemAtIndex(i));
|
||||
if (thread_dict)
|
||||
{
|
||||
ThreadSP thread_sp (CreateThreadFromThreadInfo (thread_dict, core_thread_list, old_thread_list, NULL));
|
||||
if (thread_sp)
|
||||
new_thread_list.AddThread(thread_sp);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (new_thread_list.GetSize(false) == 0)
|
||||
new_thread_list = old_thread_list;
|
||||
}
|
||||
|
||||
return new_thread_list.GetSize(false) > 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -385,11 +385,16 @@ ProcessKDP::DoResume ()
|
|||
if (kernel_thread_sp)
|
||||
{
|
||||
const StateType thread_resume_state = kernel_thread_sp->GetTemporaryResumeState();
|
||||
|
||||
if (log)
|
||||
log->Printf ("ProcessKDP::DoResume() thread_resume_state = %s", StateAsCString(thread_resume_state));
|
||||
switch (thread_resume_state)
|
||||
{
|
||||
case eStateSuspended:
|
||||
// Nothing to do here when a thread will stay suspended
|
||||
// we just leave the CPU mask bit set to zero for the thread
|
||||
if (log)
|
||||
log->Printf ("ProcessKDP::DoResume() = suspended???");
|
||||
break;
|
||||
|
||||
case eStateStepping:
|
||||
|
@ -398,6 +403,8 @@ ProcessKDP::DoResume ()
|
|||
|
||||
if (reg_ctx_sp)
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep (true);");
|
||||
reg_ctx_sp->HardwareSingleStep (true);
|
||||
resume = true;
|
||||
}
|
||||
|
@ -412,15 +419,17 @@ ProcessKDP::DoResume ()
|
|||
{
|
||||
lldb::RegisterContextSP reg_ctx_sp (kernel_thread_sp->GetRegisterContext());
|
||||
|
||||
if (reg_ctx_sp)
|
||||
{
|
||||
reg_ctx_sp->HardwareSingleStep (false);
|
||||
resume = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
error.SetErrorStringWithFormat("KDP thread 0x%llx has no register context", kernel_thread_sp->GetID());
|
||||
}
|
||||
if (reg_ctx_sp)
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep (false);");
|
||||
reg_ctx_sp->HardwareSingleStep (false);
|
||||
resume = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
error.SetErrorStringWithFormat("KDP thread 0x%llx has no register context", kernel_thread_sp->GetID());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -540,22 +549,15 @@ ProcessKDP::DoDetach(bool keep_stopped)
|
|||
// If we are going to keep the target stopped, then don't send the disconnect message.
|
||||
if (!keep_stopped && m_comm.IsConnected())
|
||||
{
|
||||
|
||||
bool disconnect_success = m_comm.SendRequestDisconnect();
|
||||
if (!disconnect_success)
|
||||
{
|
||||
if (log)
|
||||
log->PutCString ("ProcessKDP::DoDetach(): send disconnect request failed");
|
||||
}
|
||||
|
||||
ConnectionStatus comm_disconnect_result = m_comm.Disconnect ();
|
||||
const bool success = m_comm.SendRequestDisconnect();
|
||||
if (log)
|
||||
{
|
||||
if (comm_disconnect_result == eConnectionStatusSuccess)
|
||||
log->PutCString ("ProcessKDP::DoDetach() conncection channel shutdown successfully");
|
||||
if (success)
|
||||
log->PutCString ("ProcessKDP::DoDetach() detach packet sent successfully");
|
||||
else
|
||||
log->PutCString ("ProcessKDP::DoDetach() connection channel shutdown failed");
|
||||
}
|
||||
m_comm.Disconnect ();
|
||||
}
|
||||
}
|
||||
StopAsyncThread ();
|
||||
|
|
|
@ -148,26 +148,23 @@ ThreadKDP::CreateRegisterContextForFrame (StackFrame *frame)
|
|||
return reg_ctx_sp;
|
||||
}
|
||||
|
||||
lldb::StopInfoSP
|
||||
ThreadKDP::GetPrivateStopReason ()
|
||||
bool
|
||||
ThreadKDP::CalculateStopInfo ()
|
||||
{
|
||||
ProcessSP process_sp (GetProcess());
|
||||
if (process_sp)
|
||||
{
|
||||
const uint32_t process_stop_id = process_sp->GetStopID();
|
||||
if (m_thread_stop_reason_stop_id != process_stop_id ||
|
||||
(m_actual_stop_info_sp && !m_actual_stop_info_sp->IsValid()))
|
||||
if (m_cached_stop_info_sp)
|
||||
{
|
||||
if (IsStillAtLastBreakpointHit())
|
||||
return m_actual_stop_info_sp;
|
||||
|
||||
if (m_cached_stop_info_sp)
|
||||
SetStopInfo (m_cached_stop_info_sp);
|
||||
else
|
||||
SetStopInfo(StopInfo::CreateStopReasonWithSignal (*this, SIGSTOP));
|
||||
SetStopInfo (m_cached_stop_info_sp);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetStopInfo(StopInfo::CreateStopReasonWithSignal (*this, SIGSTOP));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return m_actual_stop_info_sp;
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -89,13 +89,10 @@ protected:
|
|||
lldb::addr_t m_thread_dispatch_qaddr;
|
||||
lldb::StopInfoSP m_cached_stop_info_sp;
|
||||
//------------------------------------------------------------------
|
||||
// Member variables.
|
||||
// Protected member functions.
|
||||
//------------------------------------------------------------------
|
||||
|
||||
virtual lldb::StopInfoSP
|
||||
GetPrivateStopReason ();
|
||||
|
||||
|
||||
virtual bool
|
||||
CalculateStopInfo ();
|
||||
};
|
||||
|
||||
#endif // liblldb_ThreadKDP_h_
|
||||
|
|
|
@ -138,10 +138,11 @@ POSIXThread::CreateRegisterContextForFrame(lldb_private::StackFrame *frame)
|
|||
return reg_ctx_sp;
|
||||
}
|
||||
|
||||
lldb::StopInfoSP
|
||||
POSIXThread::GetPrivateStopReason()
|
||||
bool
|
||||
POSIXThread::CalculateStopInfo()
|
||||
{
|
||||
return m_actual_stop_info_sp;
|
||||
SetStopInfo (m_actual_stop_info_sp);
|
||||
return true;
|
||||
}
|
||||
|
||||
Unwind *
|
||||
|
|
|
@ -97,8 +97,8 @@ private:
|
|||
ProcessMonitor &
|
||||
GetMonitor();
|
||||
|
||||
lldb::StopInfoSP
|
||||
GetPrivateStopReason();
|
||||
virtual bool
|
||||
CalculateStopInfo();
|
||||
|
||||
void BreakNotify(const ProcessMessage &message);
|
||||
void WatchNotify(const ProcessMessage &message);
|
||||
|
|
|
@ -99,209 +99,34 @@ ThreadMemory::CreateRegisterContextForFrame (StackFrame *frame)
|
|||
return reg_ctx_sp;
|
||||
}
|
||||
|
||||
|
||||
//class StopInfoThreadMemory : public StopInfo
|
||||
//{
|
||||
//public:
|
||||
// //------------------------------------------------------------------
|
||||
// // Constructors and Destructors
|
||||
// //------------------------------------------------------------------
|
||||
// StopInfoThreadMemory (Thread &thread,
|
||||
// uint64_t value,
|
||||
// StopInfoSP &backing_stop_info_sp) :
|
||||
// StopInfo (thread, value),
|
||||
// m_backing_stop_info_sp (backing_stop_info_sp)
|
||||
// {
|
||||
// }
|
||||
//
|
||||
// virtual
|
||||
// ~StopInfoThreadMemory()
|
||||
// {
|
||||
// }
|
||||
//
|
||||
// virtual bool
|
||||
// IsValid () const
|
||||
// {
|
||||
// ThreadSP backing_thread_sp (m_thread.GetBackingThread());
|
||||
// if (backing_thread_sp)
|
||||
// return backing_thread_sp->IsValid();
|
||||
// return StopInfo::IsValid();
|
||||
// }
|
||||
//
|
||||
// virtual Thread &
|
||||
// GetThread()
|
||||
// {
|
||||
// return m_thread;
|
||||
// }
|
||||
//
|
||||
// virtual const Thread &
|
||||
// GetThread() const
|
||||
// {
|
||||
// return m_thread;
|
||||
// }
|
||||
//
|
||||
// virtual uint64_t
|
||||
// GetValue() const
|
||||
// {
|
||||
// if (m_backing_stop_info_sp)
|
||||
// return m_backing_stop_info_sp->GetValue();
|
||||
// return StopInfo::GetValue();
|
||||
// }
|
||||
//
|
||||
// virtual lldb::StopReason
|
||||
// GetStopReason () const
|
||||
// {
|
||||
// if (m_backing_stop_info_sp)
|
||||
// return m_backing_stop_info_sp->GetStopReason();
|
||||
// return eStopReasonNone;
|
||||
// }
|
||||
//
|
||||
// // ShouldStopSynchronous will get called before any thread plans are consulted, and if it says we should
|
||||
// // resume the target, then we will just immediately resume. This should not run any code in or resume the
|
||||
// // target.
|
||||
//
|
||||
// virtual bool
|
||||
// ShouldStopSynchronous (Event *event_ptr)
|
||||
// {
|
||||
// if (m_backing_stop_info_sp)
|
||||
// return m_backing_stop_info_sp->ShouldStopSynchronous(event_ptr);
|
||||
// return StopInfo::ShouldStopSynchronous (event_ptr);
|
||||
// }
|
||||
//
|
||||
// // If should stop returns false, check if we should notify of this event
|
||||
// virtual bool
|
||||
// ShouldNotify (Event *event_ptr)
|
||||
// {
|
||||
// if (m_backing_stop_info_sp)
|
||||
// return m_backing_stop_info_sp->ShouldNotify(event_ptr);
|
||||
// return StopInfo::ShouldNotify (event_ptr);
|
||||
// }
|
||||
//
|
||||
// virtual void
|
||||
// WillResume (lldb::StateType resume_state)
|
||||
// {
|
||||
// if (m_backing_stop_info_sp)
|
||||
// return m_backing_stop_info_sp->WillResume(resume_state);
|
||||
// return StopInfo::WillResume (resume_state);
|
||||
// }
|
||||
//
|
||||
// virtual const char *
|
||||
// GetDescription ()
|
||||
// {
|
||||
// if (m_backing_stop_info_sp)
|
||||
// return m_backing_stop_info_sp->GetDescription();
|
||||
// return StopInfo::GetDescription();
|
||||
// }
|
||||
//
|
||||
// virtual void
|
||||
// SetDescription (const char *desc_cstr)
|
||||
// {
|
||||
// if (m_backing_stop_info_sp)
|
||||
// m_backing_stop_info_sp->SetDescription(desc_cstr);
|
||||
// StopInfo::SetDescription(desc_cstr);
|
||||
// }
|
||||
//
|
||||
// // Sometimes the thread plan logic will know that it wants a given stop to stop or not,
|
||||
// // regardless of what the ordinary logic for that StopInfo would dictate. The main example
|
||||
// // of this is the ThreadPlanCallFunction, which for instance knows - based on how that particular
|
||||
// // expression was executed - whether it wants all breakpoints to auto-continue or not.
|
||||
// // Use OverrideShouldStop on the StopInfo to implement this.
|
||||
//
|
||||
// virtual void
|
||||
// OverrideShouldStop (bool override_value)
|
||||
// {
|
||||
// if (m_backing_stop_info_sp)
|
||||
// m_backing_stop_info_sp->OverrideShouldStop(override_value);
|
||||
// StopInfo::OverrideShouldStop (override_value);
|
||||
// }
|
||||
//
|
||||
// virtual bool
|
||||
// GetOverrideShouldStop()
|
||||
// {
|
||||
// if (m_backing_stop_info_sp)
|
||||
// return m_backing_stop_info_sp->GetOverrideShouldStop();
|
||||
// return StopInfo::GetOverrideShouldStop();
|
||||
// }
|
||||
//
|
||||
// virtual bool
|
||||
// GetOverriddenShouldStopValue ()
|
||||
// {
|
||||
// if (m_backing_stop_info_sp)
|
||||
// return m_backing_stop_info_sp->GetOverriddenShouldStopValue();
|
||||
// return StopInfo::GetOverriddenShouldStopValue();
|
||||
// }
|
||||
//
|
||||
// virtual void
|
||||
// PerformAction (Event *event_ptr)
|
||||
// {
|
||||
// if (m_backing_stop_info_sp)
|
||||
// return m_backing_stop_info_sp->PerformAction(event_ptr);
|
||||
// return StopInfo::PerformAction(event_ptr);
|
||||
// }
|
||||
//
|
||||
// virtual bool
|
||||
// ShouldStop (Event *event_ptr)
|
||||
// {
|
||||
// if (m_backing_stop_info_sp)
|
||||
// return m_backing_stop_info_sp->ShouldStop(event_ptr);
|
||||
// return StopInfo::ShouldStop(event_ptr);
|
||||
// }
|
||||
//
|
||||
//
|
||||
//protected:
|
||||
// StopInfoSP m_backing_stop_info_sp;
|
||||
//
|
||||
//private:
|
||||
// DISALLOW_COPY_AND_ASSIGN (StopInfoThreadMemory);
|
||||
//};
|
||||
|
||||
|
||||
lldb::StopInfoSP
|
||||
ThreadMemory::GetPrivateStopReason ()
|
||||
bool
|
||||
ThreadMemory::CalculateStopInfo ()
|
||||
{
|
||||
if (m_actual_stop_info_sp)
|
||||
return m_actual_stop_info_sp;
|
||||
|
||||
if (m_backing_thread_sp)
|
||||
{
|
||||
lldb::StopInfoSP backing_stop_info_sp (m_backing_thread_sp->GetPrivateStopReason());
|
||||
lldb::StopInfoSP backing_stop_info_sp (m_backing_thread_sp->GetPrivateStopInfo());
|
||||
if (backing_stop_info_sp)
|
||||
{
|
||||
m_actual_stop_info_sp = backing_stop_info_sp;
|
||||
m_actual_stop_info_sp->SetThread (shared_from_this());
|
||||
return m_actual_stop_info_sp;
|
||||
backing_stop_info_sp->SetThread (shared_from_this());
|
||||
SetStopInfo (backing_stop_info_sp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessSP process_sp (GetProcess());
|
||||
|
||||
if (process_sp)
|
||||
else
|
||||
{
|
||||
const uint32_t process_stop_id = process_sp->GetStopID();
|
||||
if (m_thread_stop_reason_stop_id != process_stop_id ||
|
||||
(m_actual_stop_info_sp && !m_actual_stop_info_sp->IsValid()))
|
||||
{
|
||||
if (IsStillAtLastBreakpointHit())
|
||||
return m_actual_stop_info_sp;
|
||||
ProcessSP process_sp (GetProcess());
|
||||
|
||||
// If GetGDBProcess().SetThreadStopInfo() doesn't find a stop reason
|
||||
// for this thread, then m_actual_stop_info_sp will not ever contain
|
||||
// a valid stop reason and the "m_actual_stop_info_sp->IsValid() == false"
|
||||
// check will never be able to tell us if we have the correct stop info
|
||||
// for this thread and we will continually send qThreadStopInfo packets
|
||||
// down to the remote GDB server, so we need to keep our own notion
|
||||
// of the stop ID that m_actual_stop_info_sp is valid for (even if it
|
||||
// contains nothing). We use m_thread_stop_reason_stop_id for this below.
|
||||
m_thread_stop_reason_stop_id = process_stop_id;
|
||||
m_actual_stop_info_sp.reset();
|
||||
|
||||
if (process_sp)
|
||||
{
|
||||
OperatingSystem *os = process_sp->GetOperatingSystem ();
|
||||
if (os)
|
||||
m_actual_stop_info_sp = os->CreateThreadStopReason (this);
|
||||
{
|
||||
SetStopInfo (os->CreateThreadStopReason (this));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return m_actual_stop_info_sp;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -309,4 +134,7 @@ ThreadMemory::RefreshStateAfterStop()
|
|||
{
|
||||
if (m_backing_thread_sp)
|
||||
return m_backing_thread_sp->RefreshStateAfterStop();
|
||||
|
||||
if (m_reg_context_sp)
|
||||
m_reg_context_sp->InvalidateAllRegisters();
|
||||
}
|
||||
|
|
|
@ -39,8 +39,8 @@ public:
|
|||
virtual lldb::RegisterContextSP
|
||||
CreateRegisterContextForFrame (lldb_private::StackFrame *frame);
|
||||
|
||||
virtual lldb::StopInfoSP
|
||||
GetPrivateStopReason ();
|
||||
virtual bool
|
||||
CalculateStopInfo ();
|
||||
|
||||
virtual const char *
|
||||
GetInfo ()
|
||||
|
|
|
@ -1770,7 +1770,7 @@ ProcessGDBRemote::DoDestroy ()
|
|||
for (size_t i = 0; i < num_threads; i++)
|
||||
{
|
||||
ThreadSP thread_sp = threads.GetThreadAtIndex(i);
|
||||
StopInfoSP stop_info_sp = thread_sp->GetPrivateStopReason();
|
||||
StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo();
|
||||
StopReason reason = eStopReasonInvalid;
|
||||
if (stop_info_sp)
|
||||
reason = stop_info_sp->GetStopReason();
|
||||
|
@ -1805,7 +1805,7 @@ ProcessGDBRemote::DoDestroy ()
|
|||
for (size_t i = 0; i < num_threads; i++)
|
||||
{
|
||||
ThreadSP thread_sp = threads.GetThreadAtIndex(i);
|
||||
StopInfoSP stop_info_sp = thread_sp->GetPrivateStopReason();
|
||||
StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo();
|
||||
StopReason reason = eStopReasonInvalid;
|
||||
if (stop_info_sp)
|
||||
reason = stop_info_sp->GetStopReason();
|
||||
|
|
|
@ -197,56 +197,18 @@ ThreadGDBRemote::PrivateSetRegisterValue (uint32_t reg, StringExtractor &respons
|
|||
return gdb_reg_ctx->PrivateSetRegisterValue (reg, response);
|
||||
}
|
||||
|
||||
lldb::StopInfoSP
|
||||
ThreadGDBRemote::GetPrivateStopReason ()
|
||||
bool
|
||||
ThreadGDBRemote::CalculateStopInfo ()
|
||||
{
|
||||
ProcessSP process_sp (GetProcess());
|
||||
if (process_sp)
|
||||
{
|
||||
const uint32_t process_stop_id = process_sp->GetStopID();
|
||||
if (m_thread_stop_reason_stop_id == process_stop_id)
|
||||
{
|
||||
// Our stop info is up to date even if it is empty...
|
||||
return m_actual_stop_info_sp;
|
||||
}
|
||||
|
||||
if (m_actual_stop_info_sp && m_actual_stop_info_sp->IsValid())
|
||||
{
|
||||
// The stop info is up to date, reset it so everything updates
|
||||
SetStopInfo (m_actual_stop_info_sp);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsStillAtLastBreakpointHit())
|
||||
{
|
||||
SetStopInfo(m_actual_stop_info_sp);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If GetGDBProcess().SetThreadStopInfo() doesn't find a stop reason
|
||||
// for this thread, then m_actual_stop_info_sp will not ever contain
|
||||
// a valid stop reason and the "m_actual_stop_info_sp->IsValid() == false"
|
||||
// check will never be able to tell us if we have the correct stop info
|
||||
// for this thread and we will continually send qThreadStopInfo packets
|
||||
// down to the remote GDB server, so we need to keep our own notion
|
||||
// of the stop ID that m_actual_stop_info_sp is valid for (even if it
|
||||
// contains nothing). We use m_thread_stop_reason_stop_id for this below.
|
||||
m_actual_stop_info_sp.reset();
|
||||
|
||||
StringExtractorGDBRemote stop_packet;
|
||||
ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get());
|
||||
if (gdb_process->GetGDBRemote().GetThreadStopInfo(GetProtocolID(), stop_packet))
|
||||
{
|
||||
gdb_process->SetThreadStopInfo (stop_packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetStopInfo (StopInfoSP());
|
||||
}
|
||||
}
|
||||
}
|
||||
StringExtractorGDBRemote stop_packet;
|
||||
ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get());
|
||||
if (gdb_process->GetGDBRemote().GetThreadStopInfo(GetProtocolID(), stop_packet))
|
||||
return gdb_process->SetThreadStopInfo (stop_packet) == eStateStopped;
|
||||
}
|
||||
return m_actual_stop_info_sp;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -98,8 +98,8 @@ protected:
|
|||
void
|
||||
SetStopInfoFromPacket (StringExtractor &stop_packet, uint32_t stop_id);
|
||||
|
||||
virtual lldb::StopInfoSP
|
||||
GetPrivateStopReason ();
|
||||
virtual bool
|
||||
CalculateStopInfo ();
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -117,39 +117,16 @@ ThreadMachCore::CreateRegisterContextForFrame (StackFrame *frame)
|
|||
return reg_ctx_sp;
|
||||
}
|
||||
|
||||
lldb::StopInfoSP
|
||||
ThreadMachCore::GetPrivateStopReason ()
|
||||
bool
|
||||
ThreadMachCore::CalculateStopInfo ()
|
||||
{
|
||||
ProcessSP process_sp (GetProcess());
|
||||
|
||||
if (process_sp)
|
||||
{
|
||||
const uint32_t process_stop_id = process_sp->GetStopID();
|
||||
if (m_thread_stop_reason_stop_id != process_stop_id ||
|
||||
(m_actual_stop_info_sp && !m_actual_stop_info_sp->IsValid()))
|
||||
{
|
||||
if (IsStillAtLastBreakpointHit())
|
||||
return m_actual_stop_info_sp;
|
||||
|
||||
// TODO: can we query the initial state of the thread here?
|
||||
// For now I am just going to pretend that a SIGSTOP happened.
|
||||
|
||||
SetStopInfo(StopInfo::CreateStopReasonWithSignal (*this, SIGSTOP));
|
||||
|
||||
// If GetKDPProcess().SetThreadStopInfo() doesn't find a stop reason
|
||||
// for this thread, then m_actual_stop_info_sp will not ever contain
|
||||
// a valid stop reason and the "m_actual_stop_info_sp->IsValid() == false"
|
||||
// check will never be able to tell us if we have the correct stop info
|
||||
// for this thread and we will continually send qThreadStopInfo packets
|
||||
// down to the remote KDP server, so we need to keep our own notion
|
||||
// of the stop ID that m_actual_stop_info_sp is valid for (even if it
|
||||
// contains nothing). We use m_thread_stop_reason_stop_id for this below.
|
||||
// m_thread_stop_reason_stop_id = process_stop_id;
|
||||
// m_actual_stop_info_sp.reset();
|
||||
|
||||
}
|
||||
SetStopInfo(StopInfo::CreateStopReasonWithSignal (*this, SIGSTOP));
|
||||
return true;
|
||||
}
|
||||
return m_actual_stop_info_sp;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -79,11 +79,10 @@ protected:
|
|||
lldb::addr_t m_thread_dispatch_qaddr;
|
||||
lldb::RegisterContextSP m_thread_reg_ctx_sp;
|
||||
//------------------------------------------------------------------
|
||||
// Member variables.
|
||||
// Protected member functions.
|
||||
//------------------------------------------------------------------
|
||||
|
||||
virtual lldb::StopInfoSP
|
||||
GetPrivateStopReason ();
|
||||
virtual bool
|
||||
CalculateStopInfo ();
|
||||
};
|
||||
|
||||
#endif // liblldb_ThreadMachCore_h_
|
||||
|
|
|
@ -243,9 +243,9 @@ Thread::Thread (Process &process, lldb::tid_t tid) :
|
|||
UserID (tid),
|
||||
Broadcaster(&process.GetTarget().GetDebugger(), Thread::GetStaticBroadcasterClass().AsCString()),
|
||||
m_process_wp (process.shared_from_this()),
|
||||
m_actual_stop_info_sp (),
|
||||
m_stop_info_sp (),
|
||||
m_stop_info_stop_id (0),
|
||||
m_index_id (process.GetNextThreadIndexID(tid)),
|
||||
m_protocol_tid (tid),
|
||||
m_reg_context_sp (),
|
||||
m_state (eStateUnloaded),
|
||||
m_state_mutex (Mutex::eMutexTypeRecursive),
|
||||
|
@ -259,7 +259,6 @@ Thread::Thread (Process &process, lldb::tid_t tid) :
|
|||
m_temporary_resume_state (eStateRunning),
|
||||
m_unwinder_ap (),
|
||||
m_destroy_called (false),
|
||||
m_thread_stop_reason_stop_id (0),
|
||||
m_override_should_notify (eLazyBoolCalculate)
|
||||
{
|
||||
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT));
|
||||
|
@ -283,11 +282,17 @@ Thread::~Thread()
|
|||
void
|
||||
Thread::DestroyThread ()
|
||||
{
|
||||
// Tell any plans on the plan stack that the thread is being destroyed since
|
||||
// any active plans that have a thread go away in the middle of might need
|
||||
// to do cleanup.
|
||||
for (auto plan : m_plan_stack)
|
||||
plan->ThreadDestroyed();
|
||||
|
||||
m_destroy_called = true;
|
||||
m_plan_stack.clear();
|
||||
m_discarded_plan_stack.clear();
|
||||
m_completed_plan_stack.clear();
|
||||
m_actual_stop_info_sp.reset();
|
||||
m_stop_info_sp.reset();
|
||||
m_reg_context_sp.reset();
|
||||
m_unwinder_ap.reset();
|
||||
Mutex::Locker locker(m_frame_mutex);
|
||||
|
@ -366,19 +371,58 @@ Thread::GetStopInfo ()
|
|||
}
|
||||
else
|
||||
{
|
||||
if ((m_thread_stop_reason_stop_id == stop_id) || // Stop info is valid, just return what we have (even if empty)
|
||||
(m_actual_stop_info_sp && m_actual_stop_info_sp->IsValid())) // Stop info is valid, just return what we have
|
||||
if ((m_stop_info_stop_id == stop_id) || // Stop info is valid, just return what we have (even if empty)
|
||||
(m_stop_info_sp && m_stop_info_sp->IsValid())) // Stop info is valid, just return what we have
|
||||
{
|
||||
return m_actual_stop_info_sp;
|
||||
return m_stop_info_sp;
|
||||
}
|
||||
else
|
||||
{
|
||||
GetPrivateStopReason ();
|
||||
return m_actual_stop_info_sp;
|
||||
GetPrivateStopInfo ();
|
||||
return m_stop_info_sp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lldb::StopInfoSP
|
||||
Thread::GetPrivateStopInfo ()
|
||||
{
|
||||
ProcessSP process_sp (GetProcess());
|
||||
if (process_sp)
|
||||
{
|
||||
ProcessSP process_sp (GetProcess());
|
||||
if (process_sp)
|
||||
{
|
||||
const uint32_t process_stop_id = process_sp->GetStopID();
|
||||
if (m_stop_info_stop_id != process_stop_id)
|
||||
{
|
||||
if (m_stop_info_sp)
|
||||
{
|
||||
if (m_stop_info_sp->IsValid())
|
||||
{
|
||||
SetStopInfo (m_stop_info_sp);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsStillAtLastBreakpointHit())
|
||||
SetStopInfo(m_stop_info_sp);
|
||||
else
|
||||
m_stop_info_sp.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_stop_info_sp)
|
||||
{
|
||||
if (CalculateStopInfo() == false)
|
||||
SetStopInfo (StopInfoSP());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return m_stop_info_sp;
|
||||
}
|
||||
|
||||
|
||||
lldb::StopReason
|
||||
Thread::GetStopReason()
|
||||
{
|
||||
|
@ -393,20 +437,20 @@ Thread::GetStopReason()
|
|||
void
|
||||
Thread::SetStopInfo (const lldb::StopInfoSP &stop_info_sp)
|
||||
{
|
||||
m_actual_stop_info_sp = stop_info_sp;
|
||||
if (m_actual_stop_info_sp)
|
||||
m_stop_info_sp = stop_info_sp;
|
||||
if (m_stop_info_sp)
|
||||
{
|
||||
m_actual_stop_info_sp->MakeStopInfoValid();
|
||||
m_stop_info_sp->MakeStopInfoValid();
|
||||
// If we are overriding the ShouldReportStop, do that here:
|
||||
if (m_override_should_notify != eLazyBoolCalculate)
|
||||
m_actual_stop_info_sp->OverrideShouldNotify (m_override_should_notify == eLazyBoolYes);
|
||||
m_stop_info_sp->OverrideShouldNotify (m_override_should_notify == eLazyBoolYes);
|
||||
}
|
||||
|
||||
ProcessSP process_sp (GetProcess());
|
||||
if (process_sp)
|
||||
m_thread_stop_reason_stop_id = process_sp->GetStopID();
|
||||
m_stop_info_stop_id = process_sp->GetStopID();
|
||||
else
|
||||
m_thread_stop_reason_stop_id = UINT32_MAX;
|
||||
m_stop_info_stop_id = UINT32_MAX;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -417,8 +461,8 @@ Thread::SetShouldReportStop (Vote vote)
|
|||
else
|
||||
{
|
||||
m_override_should_notify = (vote == eVoteYes ? eLazyBoolYes : eLazyBoolNo);
|
||||
if (m_actual_stop_info_sp)
|
||||
m_actual_stop_info_sp->OverrideShouldNotify (m_override_should_notify == eLazyBoolYes);
|
||||
if (m_stop_info_sp)
|
||||
m_stop_info_sp->OverrideShouldNotify (m_override_should_notify == eLazyBoolYes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,7 +477,7 @@ Thread::SetStopInfoToNothing()
|
|||
bool
|
||||
Thread::ThreadStoppedForAReason (void)
|
||||
{
|
||||
return (bool) GetPrivateStopReason ();
|
||||
return (bool) GetPrivateStopInfo ();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -554,18 +598,22 @@ Thread::ShouldResume (StateType resume_state)
|
|||
|
||||
m_temporary_resume_state = resume_state;
|
||||
|
||||
// Make sure m_actual_stop_info_sp is valid
|
||||
GetPrivateStopReason();
|
||||
lldb::ThreadSP backing_thread_sp (GetBackingThread ());
|
||||
if (backing_thread_sp)
|
||||
backing_thread_sp->m_temporary_resume_state = resume_state;
|
||||
|
||||
// Make sure m_stop_info_sp is valid
|
||||
GetPrivateStopInfo();
|
||||
|
||||
// This is a little dubious, but we are trying to limit how often we actually fetch stop info from
|
||||
// the target, 'cause that slows down single stepping. So assume that if we got to the point where
|
||||
// we're about to resume, and we haven't yet had to fetch the stop reason, then it doesn't need to know
|
||||
// about the fact that we are resuming...
|
||||
const uint32_t process_stop_id = GetProcess()->GetStopID();
|
||||
if (m_thread_stop_reason_stop_id == process_stop_id &&
|
||||
(m_actual_stop_info_sp && m_actual_stop_info_sp->IsValid()))
|
||||
if (m_stop_info_stop_id == process_stop_id &&
|
||||
(m_stop_info_sp && m_stop_info_sp->IsValid()))
|
||||
{
|
||||
StopInfo *stop_info = GetPrivateStopReason().get();
|
||||
StopInfo *stop_info = GetPrivateStopInfo().get();
|
||||
if (stop_info)
|
||||
stop_info->WillResume (resume_state);
|
||||
}
|
||||
|
@ -590,7 +638,7 @@ Thread::ShouldResume (StateType resume_state)
|
|||
|
||||
if (need_to_resume && resume_state != eStateSuspended)
|
||||
{
|
||||
m_actual_stop_info_sp.reset();
|
||||
m_stop_info_sp.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -676,7 +724,7 @@ Thread::ShouldStop (Event* event_ptr)
|
|||
// First query the stop info's ShouldStopSynchronous. This handles "synchronous" stop reasons, for example the breakpoint
|
||||
// command on internal breakpoints. If a synchronous stop reason says we should not stop, then we don't have to
|
||||
// do any more work on this stop.
|
||||
StopInfoSP private_stop_info (GetPrivateStopReason());
|
||||
StopInfoSP private_stop_info (GetPrivateStopInfo());
|
||||
if (private_stop_info && private_stop_info->ShouldStopSynchronous(event_ptr) == false)
|
||||
{
|
||||
if (log)
|
||||
|
@ -1906,10 +1954,10 @@ Thread::IsStillAtLastBreakpointHit ()
|
|||
// If we are currently stopped at a breakpoint, always return that stopinfo and don't reset it.
|
||||
// This allows threads to maintain their breakpoint stopinfo, such as when thread-stepping in
|
||||
// multithreaded programs.
|
||||
if (m_actual_stop_info_sp) {
|
||||
StopReason stop_reason = m_actual_stop_info_sp->GetStopReason();
|
||||
if (m_stop_info_sp) {
|
||||
StopReason stop_reason = m_stop_info_sp->GetStopReason();
|
||||
if (stop_reason == lldb::eStopReasonBreakpoint) {
|
||||
uint64_t value = m_actual_stop_info_sp->GetValue();
|
||||
uint64_t value = m_stop_info_sp->GetValue();
|
||||
lldb::RegisterContextSP reg_ctx_sp (GetRegisterContext());
|
||||
if (reg_ctx_sp)
|
||||
{
|
||||
|
|
|
@ -505,6 +505,8 @@ ThreadList::WillResume ()
|
|||
if ((*pos)->GetResumeState() != eStateSuspended &&
|
||||
(*pos)->GetCurrentPlan()->StopOthers())
|
||||
{
|
||||
if ((*pos)->IsOperatingSystemPluginThread() && !(*pos)->GetBackingThread())
|
||||
continue;
|
||||
wants_solo_run = true;
|
||||
break;
|
||||
}
|
||||
|
@ -535,6 +537,8 @@ ThreadList::WillResume ()
|
|||
if ((*pos)->GetResumeState() != eStateSuspended
|
||||
&& (!wants_solo_run || (*pos)->GetCurrentPlan()->StopOthers()))
|
||||
{
|
||||
if ((*pos)->IsOperatingSystemPluginThread() && !(*pos)->GetBackingThread())
|
||||
continue;
|
||||
(*pos)->SetupForResume ();
|
||||
}
|
||||
}
|
||||
|
@ -546,7 +550,6 @@ ThreadList::WillResume ()
|
|||
|
||||
run_me_only_list.SetStopID(m_process->GetStopID());
|
||||
|
||||
ThreadSP immediate_thread_sp;
|
||||
bool run_only_current_thread = false;
|
||||
|
||||
for (pos = m_threads.begin(); pos != end; ++pos)
|
||||
|
@ -555,6 +558,9 @@ ThreadList::WillResume ()
|
|||
if (thread_sp->GetResumeState() != eStateSuspended &&
|
||||
thread_sp->GetCurrentPlan()->StopOthers())
|
||||
{
|
||||
if ((*pos)->IsOperatingSystemPluginThread() && !(*pos)->GetBackingThread())
|
||||
continue;
|
||||
|
||||
// You can't say "stop others" and also want yourself to be suspended.
|
||||
assert (thread_sp->GetCurrentPlan()->RunState() != eStateSuspended);
|
||||
|
||||
|
@ -573,18 +579,7 @@ ThreadList::WillResume ()
|
|||
|
||||
bool need_to_resume = true;
|
||||
|
||||
if (immediate_thread_sp)
|
||||
{
|
||||
for (pos = m_threads.begin(); pos != end; ++pos)
|
||||
{
|
||||
ThreadSP thread_sp(*pos);
|
||||
if (thread_sp.get() == immediate_thread_sp.get())
|
||||
thread_sp->ShouldResume(thread_sp->GetCurrentPlan()->RunState());
|
||||
else
|
||||
thread_sp->ShouldResume (eStateSuspended);
|
||||
}
|
||||
}
|
||||
else if (run_me_only_list.GetSize (false) == 0)
|
||||
if (run_me_only_list.GetSize (false) == 0)
|
||||
{
|
||||
// Everybody runs as they wish:
|
||||
for (pos = m_threads.begin(); pos != end; ++pos)
|
||||
|
|
|
@ -34,7 +34,8 @@ ThreadPlanStepOverBreakpoint::ThreadPlanStepOverBreakpoint (Thread &thread) :
|
|||
// first in the thread plan stack when stepping
|
||||
// over a breakpoint
|
||||
m_breakpoint_addr (LLDB_INVALID_ADDRESS),
|
||||
m_auto_continue(false)
|
||||
m_auto_continue(false),
|
||||
m_reenabled_breakpoint_site (false)
|
||||
|
||||
{
|
||||
m_breakpoint_addr = m_thread.GetRegisterContext()->GetPC();
|
||||
|
@ -105,9 +106,7 @@ ThreadPlanStepOverBreakpoint::DoWillResume (StateType resume_state, bool current
|
|||
bool
|
||||
ThreadPlanStepOverBreakpoint::WillStop ()
|
||||
{
|
||||
BreakpointSiteSP bp_site_sp (m_thread.GetProcess()->GetBreakpointSiteList().FindByAddress (m_breakpoint_addr));
|
||||
if (bp_site_sp)
|
||||
m_thread.GetProcess()->EnableBreakpointSite (bp_site_sp.get());
|
||||
ReenableBreakpointSite ();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -128,14 +127,31 @@ ThreadPlanStepOverBreakpoint::MischiefManaged ()
|
|||
if (log)
|
||||
log->Printf("Completed step over breakpoint plan.");
|
||||
// Otherwise, re-enable the breakpoint we were stepping over, and we're done.
|
||||
BreakpointSiteSP bp_site_sp (m_thread.GetProcess()->GetBreakpointSiteList().FindByAddress (m_breakpoint_addr));
|
||||
if (bp_site_sp)
|
||||
m_thread.GetProcess()->EnableBreakpointSite (bp_site_sp.get());
|
||||
ReenableBreakpointSite ();
|
||||
ThreadPlan::MischiefManaged ();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ThreadPlanStepOverBreakpoint::ReenableBreakpointSite ()
|
||||
{
|
||||
if (!m_reenabled_breakpoint_site)
|
||||
{
|
||||
m_reenabled_breakpoint_site = true;
|
||||
BreakpointSiteSP bp_site_sp (m_thread.GetProcess()->GetBreakpointSiteList().FindByAddress (m_breakpoint_addr));
|
||||
if (bp_site_sp)
|
||||
{
|
||||
m_thread.GetProcess()->EnableBreakpointSite (bp_site_sp.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
void
|
||||
ThreadPlanStepOverBreakpoint::ThreadDestroyed ()
|
||||
{
|
||||
ReenableBreakpointSite ();
|
||||
}
|
||||
|
||||
void
|
||||
ThreadPlanStepOverBreakpoint::SetAutoContinue (bool do_it)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue